diff --git a/.gitignore b/.gitignore index 39e43efbe..9b84c9dfd 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ coverage.txt *.swp *.swo testworld/hostconfigs/* -.DS_Store \ No newline at end of file +.DS_Store +localAddresses \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..92602b8d7 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "build/privacy-enabled-erc721"] + path = build/privacy-enabled-erc721 + url = https://github.com/centrifuge/privacy-enabled-erc721.git + branch = develop diff --git a/.travis.yml b/.travis.yml index 5a4512a16..b52cc496b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ services: env: matrix: - - DEPCACHEDIR=/tmp/depcache IMAGE_NAME=centrifugeio/go-centrifuge PROTOTOOL_VERSION=1.4.0 PROTOTOOL_BIN=~/bin/1.4.0/prototool + - DEPCACHEDIR=/tmp/depcache IMAGE_NAME=centrifugeio/go-centrifuge checkout: post: @@ -19,17 +19,20 @@ checkout: cache: directories: - ~/bin/ - - ~/.cache/prototool/Linux/x86_64/protobuf/ - /tmp/depcache before_install: - nvm install 10.15.1 - npm install -g truffle@4.1.13 + - travis_retry bash <(curl https://nixos.org/nix/install) + - . $HOME/.nix-profile/etc/profile.d/nix.sh + - nix-env -if https://github.com/cachix/cachix/tarball/master --substituters https://cachix.cachix.org --trusted-public-keys cachix.cachix.org-1:eWNHQldwUO7G2VkjpnjDbWwy4KQ/HNxht7H4SSoMckM= + - cachix use dapp + - git clone --recursive https://github.com/dapphub/dapptools $HOME/.dapp/dapptools + - nix-env -f $HOME/.dapp/dapptools -iA dapp solc install: - mkdir -p ~/bin - - PROTOTOOL_VERSION=$PROTOTOOL_VERSION ./build/scripts/install_prototool.sh - - go get -u github.com/kyoh86/richgo - make install jobs: @@ -37,8 +40,8 @@ jobs: - stage: "Tests" name: "Linting" script: - - make lint-check proto-lint proto-all gen-swagger generate format-go - - echo "Checking that prototool and format-go don't result in a modified git tree" && git diff --exit-code protobufs/gen + - make lint-check gen-swagger generate format-go + - echo "Checking that swagger gen didn't result in a modified git tree" && git diff --exit-code ./httpapi - stage: "Tests" name: "Unit and CMD tests" script: diff --git a/Gopkg.lock b/Gopkg.lock index 34ee5d000..bb269dce7 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -68,7 +68,7 @@ revision = "863c5efc0a49aff1e9c1cf2ef5f72f62c65425ed" [[projects]] - digest = "1:9a7966c6c967e522d698557690ea1d0a62b75e78d0bf738c137410df9468dcd6" + digest = "1:ba35b9731e67dd7e2702a8559468f10c64f6d872fbe5884c32439fa6d42ffff7" name = "github.com/centrifuge/centrifuge-protobufs" packages = [ "documenttypes", @@ -78,12 +78,11 @@ "gen/go/errors", "gen/go/generic", "gen/go/invoice", - "gen/go/notification", "gen/go/p2p", - "gen/go/purchaseorder", + "gen/go/protocol", ] pruneopts = "T" - revision = "7d846bc3247e93446882acde5edc23767cb12824" + revision = "a4dc99bb9e43371800469049e6536cc8052ce5ef" [[projects]] digest = "1:bdd797d675043d8547be2de04b25bcf7319ca834b72c88dd13f25972ac84e09a" @@ -93,14 +92,14 @@ revision = "98e4381dac54051f9a6cd6d4e0c2815d87f625d8" [[projects]] - digest = "1:37231b09a3facf49d61e6c56d88383cf09899d606e7e56a7015963bcd868382a" + digest = "1:f46a61d4cdbfd59f161576753d03df52844379a7c65dc8bd9d264da2dee134b4" name = "github.com/centrifuge/precise-proofs" packages = [ "proofs", "proofs/proto", ] pruneopts = "UT" - revision = "f443fbe849de3fae72475c0838ec8eabf6930edd" + revision = "2ed0cc3986aa6f8c5e9efc0f661607bf2857713f" [[projects]] branch = "master" @@ -212,6 +211,7 @@ packages = ["."] pruneopts = "UT" revision = "0ca9ea5df5451ffdf184b4428c902747c2c11cd7" + version = "v1.0.0" [[projects]] digest = "1:58c033da727ebd5a6769209e479cac9a6e4dab53c879631af3d8161423099d57" @@ -292,31 +292,22 @@ version = "v1.2.1" [[projects]] - digest = "1:1ba1d79f2810270045c328ae5d674321db34e3aae468eb4233883b473c5c0467" - name = "github.com/golang/glog" - packages = ["."] - pruneopts = "UT" - revision = "23def4e6c14b4da8ac2ed8007337bc5eb5007998" - -[[projects]] - digest = "1:b2d7e82c930fd78145f944d3c5e8e900c770b9147bddf917c6fbdfe2426b7210" + digest = "1:45e19a9f4fe8b3dc41d050dc07118748fd94350a4a908431764ebd66a1526697" name = "github.com/golang/protobuf" packages = [ "descriptor", "jsonpb", "proto", - "protoc-gen-go", "protoc-gen-go/descriptor", "protoc-gen-go/generator", "protoc-gen-go/generator/internal/remap", - "protoc-gen-go/grpc", "protoc-gen-go/plugin", "ptypes", "ptypes/any", "ptypes/duration", - "ptypes/empty", "ptypes/struct", "ptypes/timestamp", + "ptypes/wrappers", ] pruneopts = "UT" revision = "b5d812f8a3706043e23a9cd5babf2e5423744d30" @@ -366,22 +357,16 @@ version = "v1.4.0" [[projects]] - digest = "1:fbce563e8f11fde364d451c7df043b88eb7776444cd7875a99e837e4aedd65cb" + digest = "1:07ac748d2913b3c6d963aceec7fc4348c34a4ea58c0063779d08c2717380f7e9" name = "github.com/grpc-ecosystem/grpc-gateway" packages = [ - "codegenerator", - "protoc-gen-grpc-gateway", - "protoc-gen-grpc-gateway/descriptor", - "protoc-gen-grpc-gateway/generator", - "protoc-gen-grpc-gateway/gengateway", - "protoc-gen-grpc-gateway/httprule", - "protoc-gen-swagger/options", + "internal", "runtime", - "runtime/internal", "utilities", ] - pruneopts = "T" - revision = "8558711daa6c2853489043207b563dceacbc19cf" + pruneopts = "UT" + revision = "bebc7374a79e1105d786ef3468b474e47d652511" + version = "v1.9.4" [[projects]] digest = "1:43c609cba7aa615eaae5aa8f2ddba65dc1b2d3af7bc434f738238df8d3797ddb" @@ -558,6 +543,13 @@ pruneopts = "UT" revision = "b497e2f366b8624394fb2e89c10ab607bebdde0b" +[[projects]] + digest = "1:ae940e9156d0d778ce1991bcc99e58fa5ca2790ddf7c5f0bc05385854416f7a6" + name = "github.com/jinzhu/copier" + packages = ["."] + pruneopts = "UT" + revision = "976e0346caa839d22a17f8031a96bcd0870c0128" + [[projects]] digest = "1:aaa8e0e7e35d92e21daed3f241832cee73d15ca1cd3302ba3843159a959a7eac" name = "github.com/klauspost/compress" @@ -1388,12 +1380,12 @@ version = "v1.1.0" [[projects]] - digest = "1:65f2553aabd1d6879691058ce20eac0f798a82d92c71f13cd56de3350fa06183" + digest = "1:2833602d8454c0f7dac4f73c1a1c0fc1a52dcc738e3ad87d4a64f6446966510d" name = "github.com/xsleonard/go-merkle" packages = ["."] pruneopts = "UT" - revision = "a73b8999d89d80bdcd89d199f41ab7fcdfd12dde" - source = "github.com/mikiquantum/go-merkle" + revision = "f8894f6f7351abae156c2365b31354d4cd78a1af" + source = "github.com/centrifuge/go-merkle" [[projects]] branch = "master" @@ -1533,17 +1525,18 @@ revision = "d61658bd2e18010be0e21349cc92b1b706e35146" [[projects]] - digest = "1:f969b1b8bcb2a5162a5dc797b261eff0c2f099d7fc63c143ada31b002709f6a5" + digest = "1:dad602a4b79ce01f4ea9b3e7a6d3074d7de6bddca4ce798e0705bfda7089d8a8" name = "google.golang.org/genproto" packages = [ - "googleapis/api/annotations", + "googleapis/api/httpbody", "googleapis/rpc/status", + "protobuf/field_mask", ] pruneopts = "UT" revision = "5a2fd4cab2d6d4a18e70c34937662526cd0c4bd1" [[projects]] - digest = "1:c00eb80d7b152379c3e94c38d82b29deca98b1d0f53e4e20362589b7fcbffa07" + digest = "1:cf01ae0753310464677058b125fa31e74fd943781782ada503180ad784fc83d3" name = "google.golang.org/grpc" packages = [ ".", @@ -1560,6 +1553,7 @@ "grpclog", "internal", "internal/backoff", + "internal/balancerload", "internal/binarylog", "internal/channelz", "internal/envconfig", @@ -1574,13 +1568,14 @@ "resolver", "resolver/dns", "resolver/passthrough", + "serviceconfig", "stats", "status", "tap", ] pruneopts = "UT" - revision = "3507fb8e1a5ad030303c106fef3a47c9fdad16ad" - version = "v1.19.1" + revision = "1d89a3c832915b2314551c1d2a506874d62e53f7" + version = "v1.22.0" [[projects]] branch = "v2" @@ -1619,9 +1614,8 @@ "github.com/centrifuge/centrifuge-protobufs/gen/go/errors", "github.com/centrifuge/centrifuge-protobufs/gen/go/generic", "github.com/centrifuge/centrifuge-protobufs/gen/go/invoice", - "github.com/centrifuge/centrifuge-protobufs/gen/go/notification", "github.com/centrifuge/centrifuge-protobufs/gen/go/p2p", - "github.com/centrifuge/centrifuge-protobufs/gen/go/purchaseorder", + "github.com/centrifuge/centrifuge-protobufs/gen/go/protocol", "github.com/centrifuge/gocelery", "github.com/centrifuge/precise-proofs/proofs", "github.com/centrifuge/precise-proofs/proofs/proto", @@ -1640,26 +1634,23 @@ "github.com/ethereum/go-ethereum/log", "github.com/ethereum/go-ethereum/rpc", "github.com/gavv/httpexpect", + "github.com/ghodss/yaml", "github.com/go-chi/chi", "github.com/go-chi/chi/middleware", "github.com/go-chi/render", "github.com/go-errors/errors", "github.com/gogo/protobuf/io", "github.com/golang/protobuf/proto", - "github.com/golang/protobuf/protoc-gen-go", "github.com/golang/protobuf/ptypes", "github.com/golang/protobuf/ptypes/any", - "github.com/golang/protobuf/ptypes/empty", "github.com/golang/protobuf/ptypes/timestamp", - "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway", - "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/options", "github.com/grpc-ecosystem/grpc-gateway/runtime", - "github.com/grpc-ecosystem/grpc-gateway/utilities", "github.com/ipfs/go-cid", "github.com/ipfs/go-datastore", "github.com/ipfs/go-ipfs-addr", "github.com/ipfs/go-log", "github.com/jbenet/go-context/io", + "github.com/jinzhu/copier", "github.com/libp2p/go-libp2p", "github.com/libp2p/go-libp2p-crypto", "github.com/libp2p/go-libp2p-host", @@ -1691,11 +1682,8 @@ "golang.org/x/crypto/ssh/terminal", "golang.org/x/net/context", "golang.org/x/tools/cmd/goimports", - "google.golang.org/genproto/googleapis/api/annotations", "google.golang.org/grpc", "google.golang.org/grpc/codes", - "google.golang.org/grpc/grpclog", - "google.golang.org/grpc/metadata", "google.golang.org/grpc/status", "gopkg.in/resty.v1", ] diff --git a/Gopkg.toml b/Gopkg.toml index c9baa3d93..a9f4a8b9a 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -24,11 +24,11 @@ # go-tests = true # unused-packages = true -required = ["github.com/centrifuge/centrifuge-ethereum-contracts", "github.com/roboll/go-vendorinstall", "github.com/golang/protobuf/protoc-gen-go", "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway", "golang.org/x/tools/cmd/goimports", "github.com/swaggo/swag", "github.com/urfave/cli"] +required = ["github.com/centrifuge/centrifuge-ethereum-contracts", "github.com/roboll/go-vendorinstall", "github.com/ghodss/yaml", "golang.org/x/tools/cmd/goimports", "github.com/swaggo/swag", "github.com/urfave/cli"] [[constraint]] name = "github.com/centrifuge/centrifuge-protobufs" - revision = "7d846bc3247e93446882acde5edc23767cb12824" + revision = "a4dc99bb9e43371800469049e6536cc8052ce5ef" [[override]] name = "github.com/centrifuge/centrifuge-ethereum-contracts" @@ -40,7 +40,7 @@ required = ["github.com/centrifuge/centrifuge-ethereum-contracts", "github.com/r [[override]] name = "github.com/centrifuge/precise-proofs" - revision = "f443fbe849de3fae72475c0838ec8eabf6930edd" + revision = "2ed0cc3986aa6f8c5e9efc0f661607bf2857713f" [[constraint]] name = "github.com/centrifuge/gocelery" @@ -66,10 +66,6 @@ required = ["github.com/centrifuge/centrifuge-ethereum-contracts", "github.com/r name = "github.com/golang/protobuf" version = "1.3.0" -[[override]] - name = "github.com/grpc-ecosystem/grpc-gateway" - revision= "8558711daa6c2853489043207b563dceacbc19cf" - [[constraint]] name = "golang.org/x/tools" revision = "677d2ff680c188ddb7dcd2bfa6bc7d3f2f2f75b2" @@ -254,18 +250,10 @@ required = ["github.com/centrifuge/centrifuge-ethereum-contracts", "github.com/r name = "golang.org/x/net" revision = "26e67e76b6c3f6ce91f7c52def5af501b4e0f3a2" -[[constraint]] - name = "google.golang.org/grpc" - version = "1.15.0" - [[constraint]] name = "gopkg.in/resty.v1" version = "1.9.1" -[[override]] - name = "google.golang.org/genproto" - revision = "5a2fd4cab2d6d4a18e70c34937662526cd0c4bd1" - [[prune.project]] name = "github.com/libp2p/go-libp2p" unused-packages = false @@ -282,17 +270,14 @@ required = ["github.com/centrifuge/centrifuge-ethereum-contracts", "github.com/r name = "github.com/centrifuge/centrifuge-protobufs" unused-packages = false -[[prune.project]] - name = "github.com/grpc-ecosystem/grpc-gateway" - unused-packages = false - [[prune.project]] name = "github.com/hashicorp/go-multierror" unused-packages = false [prune] - go-tests = true - unused-packages = true + go-tests = true + unused-packages = true + [[constraint]] branch = "master" name = "github.com/savaki/jq" @@ -324,3 +309,7 @@ required = ["github.com/centrifuge/centrifuge-ethereum-contracts", "github.com/r [[constraint]] name = "github.com/urfave/cli" version = "1.20.0" + +[[constraint]] + name = "github.com/jinzhu/copier" + revision = "976e0346caa839d22a17f8031a96bcd0870c0128" diff --git a/Makefile b/Makefile index 01557b29a..0455ab643 100644 --- a/Makefile +++ b/Makefile @@ -13,9 +13,6 @@ TAGINSTANCE="${TAG}" # GOBIN needs to be set to ensure govendor can actually be found and executed PATH=$(shell printenv PATH):$(GOBIN) -# If you need to overwrite PROTOTOOL_BIN, you can set this environment variable. -PROTOTOOL_BIN ?=$(shell which prototool) - # Lock metalinter version GOMETALINTER_VERSION="v2.0.12" @@ -35,41 +32,26 @@ clean: ##clean vendor's folder. Should be run before a make install install-deps: ## Install Dependencies @command -v dep >/dev/null 2>&1 || go get -u github.com/golang/dep/... @dep ensure - @npm --prefix ./build install @curl -L https://git.io/vp6lP | sh -s ${GOMETALINTER_VERSION} + @git submodule update --init --recursive @mv ./bin/* $(GOPATH)/bin/; rm -rf ./bin lint-check: ## runs linters on go code @gometalinter --exclude=anchors/service.go --disable-all --enable=golint --enable=goimports --enable=vet --enable=nakedret \ - --enable=staticcheck --vendor --skip=resources --skip=testingutils --skip=protobufs --deadline=1m ./...; + --enable=staticcheck --vendor --skip=resources --skip=testingutils --deadline=1m ./...; format-go: ## formats go code @goimports -w . -proto-lint: ## runs prototool lint - $(PROTOTOOL_BIN) lint protobufs - -proto-gen-go: ## generates the go bindings - $(PROTOTOOL_BIN) generate protobufs - -proto-all: ## runs prototool all - $(PROTOTOOL_BIN) all protobufs - @goimports -w ./protobufs/gen/ - gen-swagger: ## generates the swagger documentation - swag init -g ./httpapi/router.go -o ./protobufs/gen/swagger/api - rm -rf ./protobufs/gen/swagger/api/docs.go ./protobufs/gen/swagger/api/swagger.yaml - mv ./protobufs/gen/swagger/api/swagger.json ./protobufs/gen/swagger/api/api.swagger.json - npm --prefix ./build run build_swagger + swag init -g ./httpapi/router.go -o ./httpapi + rm -rf ./httpapi/docs.go ./httpapi/swagger.yaml generate: ## autogenerate go files for config go generate ./config/configuration.go vendorinstall: ## Installs all protobuf dependencies with go-vendorinstall go install github.com/centrifuge/go-centrifuge/vendor/github.com/roboll/go-vendorinstall - go-vendorinstall github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway - go-vendorinstall github.com/golang/protobuf/protoc-gen-go - go-vendorinstall github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger go-vendorinstall golang.org/x/tools/cmd/goimports go-vendorinstall github.com/swaggo/swag/cmd/swag go get -u github.com/jteeuwen/go-bindata/... diff --git a/anchors/anchor_repository.go b/anchors/anchor_repository.go index 91b244ccf..e7f8288d9 100644 --- a/anchors/anchor_repository.go +++ b/anchors/anchor_repository.go @@ -14,10 +14,10 @@ var log = logging.Logger("anchorRepository") type AnchorRepository interface { // PreCommitAnchor will call the transaction PreCommit on the smart contract, to pre commit a document update - PreCommitAnchor(ctx context.Context, anchorID AnchorID, signingRoot DocumentRoot) (confirmations chan bool, err error) + PreCommitAnchor(ctx context.Context, anchorID AnchorID, signingRoot DocumentRoot) (confirmations chan error, err error) // CommitAnchor will send a commit transaction to Ethereum. - CommitAnchor(ctx context.Context, anchorID AnchorID, documentRoot DocumentRoot, proof [32]byte) (chan bool, error) + CommitAnchor(ctx context.Context, anchorID AnchorID, documentRoot DocumentRoot, proof [32]byte) (chan error, error) // GetAnchorData takes an anchorID and returns the corresponding documentRoot from the chain. GetAnchorData(anchorID AnchorID) (docRoot DocumentRoot, anchoredTime time.Time, err error) diff --git a/anchors/anchor_repository_integration_test.go b/anchors/anchor_repository_integration_test.go index f5d250aa2..859b8aef5 100644 --- a/anchors/anchor_repository_integration_test.go +++ b/anchors/anchor_repository_integration_test.go @@ -109,9 +109,9 @@ func commitAnchor(t *testing.T, anchorID, documentRoot []byte, documentProof [32 ctx := testingconfig.CreateAccountContext(t, cfg) done, err := anchorRepo.CommitAnchor(ctx, anchorIDTyped, docRootTyped, documentProof) - isDone := <-done - assert.True(t, isDone, "isDone should be true") assert.Nil(t, err) + doneErr := <-done + assert.NoError(t, doneErr, "no error") } func preCommitAnchor(t *testing.T, anchorID, documentRoot []byte) { @@ -121,15 +121,15 @@ func preCommitAnchor(t *testing.T, anchorID, documentRoot []byte) { ctx := testingconfig.CreateAccountContext(t, cfg) done, err := anchorRepo.PreCommitAnchor(ctx, anchorIDTyped, docRootTyped) - isDone := <-done - assert.True(t, isDone, "isDone should be true") assert.Nil(t, err) + doneErr := <-done + assert.NoError(t, doneErr, "no error") } func TestCommitAnchor_Integration_Concurrent(t *testing.T) { t.Parallel() var commitDataList [5]*anchors.CommitData - var doneList [5]chan bool + var doneList [5]chan error for ix := 0; ix < 5; ix++ { anchorIDPreImage := utils.RandomSlice(32) @@ -156,8 +156,8 @@ func TestCommitAnchor_Integration_Concurrent(t *testing.T) { } for ix := 0; ix < 5; ix++ { - isDone := <-doneList[ix] - assert.True(t, isDone) + doneErr := <-doneList[ix] + assert.NoError(t, doneErr) anchorID := commitDataList[ix].AnchorID docRoot := commitDataList[ix].DocumentRoot gotDocRoot, _, err := anchorRepo.GetAnchorData(anchorID) diff --git a/anchors/bootstrapper.go b/anchors/bootstrapper.go index 472b70756..14c53389f 100644 --- a/anchors/bootstrapper.go +++ b/anchors/bootstrapper.go @@ -14,7 +14,7 @@ const ( // BootstrappedAnchorRepo is used as a key to map the configured anchor repository through context. BootstrappedAnchorRepo string = "BootstrappedAnchorRepo" - // ErrAnchorRepoNotInitialised is a sentinal error when repository is not initialised + // ErrAnchorRepoNotInitialised is a sentinel error when repository is not initialised ErrAnchorRepoNotInitialised = errors.Error("anchor repository not initialised") ) diff --git a/anchors/service.go b/anchors/service.go index 4eb48eec9..9ee99e98c 100644 --- a/anchors/service.go +++ b/anchors/service.go @@ -75,7 +75,7 @@ func (s *service) GetAnchorData(anchorID AnchorID) (docRoot DocumentRoot, anchor } // PreCommitAnchor will call the transaction PreCommit on the smart contract -func (s *service) PreCommitAnchor(ctx context.Context, anchorID AnchorID, signingRoot DocumentRoot) (confirmations chan bool, err error) { +func (s *service) PreCommitAnchor(ctx context.Context, anchorID AnchorID, signingRoot DocumentRoot) (confirmations chan error, err error) { did, err := getDID(ctx) if err != nil { return nil, err @@ -97,7 +97,7 @@ func (s *service) PreCommitAnchor(ctx context.Context, anchorID AnchorID, signin opts.GasLimit = s.config.GetEthereumGasLimit(config.AnchorPreCommit) pc := newPreCommitData(anchorID, signingRoot) log.Infof("Add Anchor to Pre-commit %s from did:%s", anchorID.String(), did.ToAddress().String()) - _, done, err := s.jobsMan.ExecuteWithinJob(ctx, did, jobID, "Check Job for anchor commit", + _, done, err := s.jobsMan.ExecuteWithinJob(contextutil.Copy(ctx), did, jobID, "Check Job for anchor commit", s.ethereumTX(opts, s.anchorRepositoryContract.PreCommit, pc.AnchorID.BigInt(), pc.SigningRoot)) if err != nil { return nil, err @@ -138,15 +138,12 @@ func getDID(ctx context.Context) (identity.DID, error) { return identity.DID{}, err } - addressByte, err := tc.GetIdentityID() - if err != nil { - return identity.DID{}, err - } + addressByte := tc.GetIdentityID() return identity.NewDID(common.BytesToAddress(addressByte)), nil } // CommitAnchor will send a commit transaction to Ethereum. -func (s *service) CommitAnchor(ctx context.Context, anchorID AnchorID, documentRoot DocumentRoot, proof [32]byte) (chan bool, error) { +func (s *service) CommitAnchor(ctx context.Context, anchorID AnchorID, documentRoot DocumentRoot, proof [32]byte) (chan error, error) { did, err := getDID(ctx) if err != nil { return nil, err @@ -174,7 +171,7 @@ func (s *service) CommitAnchor(ctx context.Context, anchorID AnchorID, documentR cd := NewCommitData(h.Number.Uint64(), anchorID, documentRoot, proof) log.Infof("Add Anchor to Commit %s from did:%s", anchorID.String(), did.ToAddress().String()) - _, done, err := s.jobsMan.ExecuteWithinJob(ctx, did, jobID, "Check Job for anchor commit", + _, done, err := s.jobsMan.ExecuteWithinJob(contextutil.Copy(ctx), did, jobID, "Check Job for anchor commit", s.ethereumTX(opts, s.anchorRepositoryContract.Commit, cd.AnchorID.BigInt(), cd.DocumentRoot, cd.DocumentProof)) if err != nil { return nil, err diff --git a/api/server.go b/api/server.go index cfab07ad4..6728a36e9 100644 --- a/api/server.go +++ b/api/server.go @@ -1,34 +1,19 @@ package api import ( - "io" - "net" "net/http" _ "net/http/pprof" // we need this side effect that loads the pprof endpoints to defaultServerMux "sync" "time" - "github.com/centrifuge/go-centrifuge/config" - "github.com/centrifuge/go-centrifuge/errors" - "github.com/centrifuge/go-centrifuge/utils" - "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/centrifuge/go-centrifuge/httpapi" + "github.com/centrifuge/go-centrifuge/utils/httputils" + "github.com/go-chi/render" logging "github.com/ipfs/go-log" "golang.org/x/net/context" - "google.golang.org/grpc" - "google.golang.org/grpc/metadata" ) -const ( - // ErrNoAuthHeader used for requests when header is not passed. - ErrNoAuthHeader = errors.Error("'authorization' header missing") -) - -var ( - log = logging.Logger("api-server") - - // noAuthPaths holds the paths that doesn't require header to be passed. - noAuthPaths = [...]string{"/health.HealthCheckService/Ping"} -) +var log = logging.Logger("api-server") // Config defines methods required for the package api type Config interface { @@ -52,19 +37,7 @@ func (c apiServer) Start(ctx context.Context, wg *sync.WaitGroup, startupErr cha defer wg.Done() apiAddr := c.config.GetServerAddress() - grpcAddr, _, err := utils.GetFreeAddrPort() - if err != nil { - startupErr <- errors.New("failed to get random port for grpc: %v", err) - return - } - - // set http error interceptor - runtime.HTTPError = httpResponseInterceptor - opts := []grpc.ServerOption{grpcInterceptor()} - - grpcServer := grpc.NewServer(opts...) - gwmux := runtime.NewServeMux() - mux, err := registerServices(ctx, grpcServer, gwmux, grpcAddr, []grpc.DialOption{grpc.WithInsecure()}) + mux, err := httpapi.Router(ctx) if err != nil { startupErr <- err return @@ -75,9 +48,11 @@ func (c apiServer) Start(ctx context.Context, wg *sync.WaitGroup, startupErr cha mux.Handle("/debug/", http.DefaultServeMux) } - // if we dont find the route in the mux, we will redirect it to gmux - mux.NotFound(func(writer http.ResponseWriter, request *http.Request) { - gwmux.ServeHTTP(writer, request) + mux.NotFound(func(w http.ResponseWriter, r *http.Request) { + render.Status(r, http.StatusNotFound) + render.JSON(w, r, httputils.HTTPError{ + Message: http.StatusText(http.StatusNotFound), + }) }) srv := &http.Server{ Addr: apiAddr, @@ -85,20 +60,6 @@ func (c apiServer) Start(ctx context.Context, wg *sync.WaitGroup, startupErr cha } startUpErrOut := make(chan error) - go func(startUpErrInner chan<- error) { - conn, err := net.Listen("tcp", grpcAddr) - if err != nil { - startUpErrInner <- err - return - } - - log.Infof("GRPC Server running at: %s\n", grpcAddr) - err = grpcServer.Serve(conn) - if err != nil { - startUpErrInner <- err - } - }(startUpErrOut) - go func(startUpErrInner chan<- error) { log.Infof("HTTP API running at: %s\n", c.config.GetServerAddress()) log.Infof("Connecting to Network: %s\n", c.config.GetNetworkString()) @@ -129,71 +90,7 @@ func (c apiServer) Start(ctx context.Context, wg *sync.WaitGroup, startupErr cha if err != nil { panic(err) } - grpcServer.GracefulStop() log.Info("API server stopped") return } } - -// grpcInterceptor returns a GRPC UnaryInterceptor for all grpc/http requests. -func grpcInterceptor() grpc.ServerOption { - return grpc.UnaryInterceptor(auth) -} - -// auth is the grpc unary interceptor to to check if the account ID is passed in the header. -// interceptor will check "authorisation" header. If not set, we return an error. -// -// at this point we are going with one interceptor. Once we have more than one interceptor, -// we can write a wrapper interceptor that will call the chain of interceptor -// -// Note: each handler can access accountID from the context: ctx.Value(api.AccountHeaderKey) -func auth(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { - // if this request is for ping - if utils.ContainsString(noAuthPaths[:], info.FullMethod) { - return handler(ctx, req) - } - - err = errors.NewHTTPError(http.StatusBadRequest, ErrNoAuthHeader) - md, ok := metadata.FromIncomingContext(ctx) - if !ok { - return nil, err - } - - auth := md.Get("authorization") - if len(auth) < 1 { - return nil, err - } - - ctx = context.WithValue(ctx, config.AccountHeaderKey, auth[0]) - return handler(ctx, req) -} - -// httpResponseInterceptor will intercept if the we return an error from the grpc handler. -// we fetch the http code from the error using errors.GetHTTPDetails. -// -// copied some stuff from the DefaultHTTPError interceptor. -// Note: this is where we marshal the error. -func httpResponseInterceptor(_ context.Context, _ *runtime.ServeMux, marshaler runtime.Marshaler, w http.ResponseWriter, _ *http.Request, err error) { - const fallback = `{"error": "failed to marshal error message"}` - - w.Header().Set("Content-Type", marshaler.ContentType()) - var errBody struct { - Error string `protobuf:"bytes,1,name=error" json:"error"` - } - - code, msg := errors.GetHTTPDetails(err) - errBody.Error = msg - buf, merr := marshaler.Marshal(errBody) - if merr != nil { - w.WriteHeader(http.StatusInternalServerError) - if _, err := io.WriteString(w, fallback); err != nil { - log.Infof("Failed to write response: %v", err) - } - return - } - - w.WriteHeader(code) - if _, err := w.Write(buf); err != nil { - log.Infof("Failed to write response: %v", err) - } -} diff --git a/api/server_test.go b/api/server_test.go index 5df783362..61d0e9962 100644 --- a/api/server_test.go +++ b/api/server_test.go @@ -19,20 +19,20 @@ import ( "github.com/centrifuge/go-centrifuge/documents/entityrelationship" "github.com/centrifuge/go-centrifuge/documents/generic" "github.com/centrifuge/go-centrifuge/documents/invoice" - "github.com/centrifuge/go-centrifuge/documents/purchaseorder" - "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/go-centrifuge/ethereum" "github.com/centrifuge/go-centrifuge/extensions/funding" "github.com/centrifuge/go-centrifuge/extensions/transferdetails" + "github.com/centrifuge/go-centrifuge/httpapi/coreapi" + "github.com/centrifuge/go-centrifuge/httpapi/userapi" + "github.com/centrifuge/go-centrifuge/httpapi/v2" "github.com/centrifuge/go-centrifuge/identity/ideth" "github.com/centrifuge/go-centrifuge/jobs/jobsv1" "github.com/centrifuge/go-centrifuge/nft" "github.com/centrifuge/go-centrifuge/p2p" + "github.com/centrifuge/go-centrifuge/pending" "github.com/centrifuge/go-centrifuge/queue" "github.com/centrifuge/go-centrifuge/storage/leveldb" "github.com/stretchr/testify/assert" - "google.golang.org/grpc" - "google.golang.org/grpc/metadata" ) var ctx = map[string]interface{}{} @@ -53,18 +53,21 @@ func TestMain(m *testing.M) { &configstore.Bootstrapper{}, anchors.Bootstrapper{}, documents.Bootstrapper{}, + pending.Bootstrapper{}, &invoice.Bootstrapper{}, &entityrelationship.Bootstrapper{}, - &purchaseorder.Bootstrapper{}, generic.Bootstrapper{}, ðereum.Bootstrapper{}, &nft.Bootstrapper{}, &queue.Starter{}, p2p.Bootstrapper{}, documents.PostBootstrapper{}, + coreapi.Bootstrapper{}, &entity.Bootstrapper{}, funding.Bootstrapper{}, transferdetails.Bootstrapper{}, + userapi.Bootstrapper{}, + v2.Bootstrapper{}, } bootstrap.RunTestBootstrappers(ibootstappers, ctx) @@ -125,45 +128,3 @@ func TestCentAPIServer_FailedToGetRegistry(t *testing.T) { assert.NotNil(t, err, "Error should be not nil") assert.Equal(t, "failed to get NodeObjRegistry", err.Error()) } - -func Test_auth(t *testing.T) { - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return ctx.Value(config.AccountHeaderKey), nil - } - - // send ping path - resp, err := auth( - context.Background(), - nil, - &grpc.UnaryServerInfo{FullMethod: noAuthPaths[0]}, - handler, - ) - assert.Nil(t, resp) - assert.Nil(t, err) - - // send no auth - resp, err = auth( - context.Background(), - nil, - &grpc.UnaryServerInfo{FullMethod: "some method"}, - handler, - ) - - assert.Nil(t, resp) - assert.True(t, errors.IsOfType(ErrNoAuthHeader, err)) - - // send Auth - ctx := metadata.NewIncomingContext( - context.Background(), - map[string][]string{"authorization": {"1234567890"}}) - - resp, err = auth( - ctx, - nil, - &grpc.UnaryServerInfo{FullMethod: "some method"}, - handler, - ) - - assert.Nil(t, err) - assert.Equal(t, "1234567890", resp) -} diff --git a/api/service.go b/api/service.go deleted file mode 100644 index 8ebffe3ca..000000000 --- a/api/service.go +++ /dev/null @@ -1,132 +0,0 @@ -package api - -import ( - "github.com/centrifuge/go-centrifuge/bootstrap" - "github.com/centrifuge/go-centrifuge/config" - "github.com/centrifuge/go-centrifuge/config/configstore" - "github.com/centrifuge/go-centrifuge/documents" - "github.com/centrifuge/go-centrifuge/documents/entity" - "github.com/centrifuge/go-centrifuge/documents/invoice" - "github.com/centrifuge/go-centrifuge/documents/purchaseorder" - "github.com/centrifuge/go-centrifuge/errors" - "github.com/centrifuge/go-centrifuge/extensions/funding" - "github.com/centrifuge/go-centrifuge/extensions/transferdetails" - "github.com/centrifuge/go-centrifuge/httpapi" - "github.com/centrifuge/go-centrifuge/jobs" - "github.com/centrifuge/go-centrifuge/nft" - "github.com/centrifuge/go-centrifuge/protobufs/gen/go/account" - "github.com/centrifuge/go-centrifuge/protobufs/gen/go/entity" - funpb "github.com/centrifuge/go-centrifuge/protobufs/gen/go/funding" - invoicepb "github.com/centrifuge/go-centrifuge/protobufs/gen/go/invoice" - "github.com/centrifuge/go-centrifuge/protobufs/gen/go/nft" - purchaseorderpb "github.com/centrifuge/go-centrifuge/protobufs/gen/go/purchaseorder" - "github.com/go-chi/chi" - "github.com/grpc-ecosystem/grpc-gateway/runtime" - "golang.org/x/net/context" - "google.golang.org/grpc" -) - -// registerServices registers all endpoints to the grpc server -func registerServices(ctx context.Context, grpcServer *grpc.Server, gwmux *runtime.ServeMux, addr string, dopts []grpc.DialOption) (mux *chi.Mux, err error) { - // node object registry - nodeObjReg, ok := ctx.Value(bootstrap.NodeObjRegistry).(map[string]interface{}) - if !ok { - return nil, errors.New("failed to get %s", bootstrap.NodeObjRegistry) - } - - configService, ok := nodeObjReg[config.BootstrappedConfigStorage].(config.Service) - if !ok { - return nil, errors.New("failed to get %s", config.BootstrappedConfigStorage) - } - - invoiceUnpaidService, ok := nodeObjReg[bootstrap.BootstrappedInvoiceUnpaid].(nft.InvoiceUnpaid) - if !ok { - return nil, errors.New("failed to get %s", bootstrap.BootstrappedInvoiceUnpaid) - } - - docService, ok := nodeObjReg[documents.BootstrappedDocumentService].(documents.Service) - if !ok { - return nil, errors.New("failed to get %s", documents.BootstrappedDocumentService) - } - - transferService, ok := nodeObjReg[transferdetails.BootstrappedTransferDetailService].(transferdetails.Service) - if !ok { - return nil, errors.New("failed to get %s", transferdetails.BootstrappedTransferDetailService) - } - - // register document types - err = registerDocumentTypes(ctx, nodeObjReg, grpcServer, gwmux, addr, dopts) - if err != nil { - return nil, err - } - - // register other api endpoints - err = registerAPIs(ctx, invoiceUnpaidService, configService, grpcServer, gwmux, addr, dopts) - if err != nil { - return nil, err - } - - cfg := nodeObjReg[bootstrap.BootstrappedConfig].(config.Configuration) - jobsMan := nodeObjReg[jobs.BootstrappedService].(jobs.Manager) - return httpapi.Router(cfg, configService, invoiceUnpaidService, docService, transferService, jobsMan), nil -} - -func registerAPIs(ctx context.Context, InvoiceUnpaidService nft.InvoiceUnpaid, configService config.Service, grpcServer *grpc.Server, gwmux *runtime.ServeMux, addr string, dopts []grpc.DialOption) error { - - // nft api - nftpb.RegisterNFTServiceServer(grpcServer, nft.GRPCHandler(configService, InvoiceUnpaidService)) - err := nftpb.RegisterNFTServiceHandlerFromEndpoint(ctx, gwmux, addr, dopts) - if err != nil { - return err - } - - // account api - accountpb.RegisterAccountServiceServer(grpcServer, configstore.GRPCAccountHandler(configService)) - return accountpb.RegisterAccountServiceHandlerFromEndpoint(ctx, gwmux, addr, dopts) -} - -func registerDocumentTypes(ctx context.Context, nodeObjReg map[string]interface{}, grpcServer *grpc.Server, gwmux *runtime.ServeMux, addr string, dopts []grpc.DialOption) error { - // register invoice - invHandler, ok := nodeObjReg[invoice.BootstrappedInvoiceHandler].(invoicepb.InvoiceServiceServer) - if !ok { - return errors.New("invoice grpc handler not registered") - } - - invoicepb.RegisterInvoiceServiceServer(grpcServer, invHandler) - err := invoicepb.RegisterInvoiceServiceHandlerFromEndpoint(ctx, gwmux, addr, dopts) - if err != nil { - return err - } - - // register purchase order - poHandler, ok := nodeObjReg[purchaseorder.BootstrappedPOHandler].(purchaseorderpb.PurchaseOrderServiceServer) - if !ok { - return errors.New("purchase order grpc handler not registered") - } - - purchaseorderpb.RegisterPurchaseOrderServiceServer(grpcServer, poHandler) - err = purchaseorderpb.RegisterPurchaseOrderServiceHandlerFromEndpoint(ctx, gwmux, addr, dopts) - if err != nil { - return err - } - - // register entity - entityHandler, ok := nodeObjReg[entity.BootstrappedEntityHandler].(entitypb.EntityServiceServer) - if !ok { - return errors.New("entity grpc handler not registered") - } - - entitypb.RegisterEntityServiceServer(grpcServer, entityHandler) - err = entitypb.RegisterEntityServiceHandlerFromEndpoint(ctx, gwmux, addr, dopts) - if err != nil { - return err - } - - fundingHandler, ok := nodeObjReg[funding.BootstrappedFundingAPIHandler].(funpb.FundingServiceServer) - if !ok { - return errors.New("funding API handler not registered") - } - - funpb.RegisterFundingServiceServer(grpcServer, fundingHandler) - return funpb.RegisterFundingServiceHandlerFromEndpoint(ctx, gwmux, addr, dopts) -} diff --git a/bootstrap/bootstrappers/bootstrapper.go b/bootstrap/bootstrappers/bootstrapper.go index 794c7c298..5c609c17c 100644 --- a/bootstrap/bootstrappers/bootstrapper.go +++ b/bootstrap/bootstrappers/bootstrapper.go @@ -11,15 +11,18 @@ import ( "github.com/centrifuge/go-centrifuge/documents/entityrelationship" "github.com/centrifuge/go-centrifuge/documents/generic" "github.com/centrifuge/go-centrifuge/documents/invoice" - "github.com/centrifuge/go-centrifuge/documents/purchaseorder" "github.com/centrifuge/go-centrifuge/ethereum" "github.com/centrifuge/go-centrifuge/extensions/funding" "github.com/centrifuge/go-centrifuge/extensions/transferdetails" + "github.com/centrifuge/go-centrifuge/httpapi/coreapi" + "github.com/centrifuge/go-centrifuge/httpapi/userapi" + v2 "github.com/centrifuge/go-centrifuge/httpapi/v2" "github.com/centrifuge/go-centrifuge/identity/ideth" "github.com/centrifuge/go-centrifuge/jobs/jobsv1" "github.com/centrifuge/go-centrifuge/nft" "github.com/centrifuge/go-centrifuge/node" "github.com/centrifuge/go-centrifuge/p2p" + "github.com/centrifuge/go-centrifuge/pending" "github.com/centrifuge/go-centrifuge/queue" "github.com/centrifuge/go-centrifuge/storage/leveldb" "github.com/centrifuge/go-centrifuge/version" @@ -49,14 +52,17 @@ func (m *MainBootstrapper) PopulateBaseBootstrappers() { api.Bootstrapper{}, &invoice.Bootstrapper{}, &entityrelationship.Bootstrapper{}, - &purchaseorder.Bootstrapper{}, generic.Bootstrapper{}, &nft.Bootstrapper{}, p2p.Bootstrapper{}, documents.PostBootstrapper{}, + pending.Bootstrapper{}, + coreapi.Bootstrapper{}, &entity.Bootstrapper{}, funding.Bootstrapper{}, transferdetails.Bootstrapper{}, + userapi.Bootstrapper{}, + v2.Bootstrapper{}, } } diff --git a/bootstrap/bootstrappers/testingbootstrap/testing_bootstrap.go b/bootstrap/bootstrappers/testingbootstrap/testing_bootstrap.go index 1731ac6a2..596c6c551 100644 --- a/bootstrap/bootstrappers/testingbootstrap/testing_bootstrap.go +++ b/bootstrap/bootstrappers/testingbootstrap/testing_bootstrap.go @@ -13,14 +13,17 @@ import ( "github.com/centrifuge/go-centrifuge/documents/entityrelationship" "github.com/centrifuge/go-centrifuge/documents/generic" "github.com/centrifuge/go-centrifuge/documents/invoice" - "github.com/centrifuge/go-centrifuge/documents/purchaseorder" "github.com/centrifuge/go-centrifuge/ethereum" "github.com/centrifuge/go-centrifuge/extensions/funding" "github.com/centrifuge/go-centrifuge/extensions/transferdetails" + "github.com/centrifuge/go-centrifuge/httpapi/coreapi" + "github.com/centrifuge/go-centrifuge/httpapi/userapi" + v2 "github.com/centrifuge/go-centrifuge/httpapi/v2" "github.com/centrifuge/go-centrifuge/identity/ideth" "github.com/centrifuge/go-centrifuge/jobs/jobsv1" "github.com/centrifuge/go-centrifuge/nft" "github.com/centrifuge/go-centrifuge/p2p" + "github.com/centrifuge/go-centrifuge/pending" "github.com/centrifuge/go-centrifuge/queue" "github.com/centrifuge/go-centrifuge/storage/leveldb" "github.com/centrifuge/go-centrifuge/testingutils" @@ -42,14 +45,17 @@ var bootstrappers = []bootstrap.TestBootstrapper{ documents.Bootstrapper{}, &invoice.Bootstrapper{}, &entityrelationship.Bootstrapper{}, - &purchaseorder.Bootstrapper{}, generic.Bootstrapper{}, &nft.Bootstrapper{}, p2p.Bootstrapper{}, documents.PostBootstrapper{}, + pending.Bootstrapper{}, + coreapi.Bootstrapper{}, &entity.Bootstrapper{}, funding.Bootstrapper{}, transferdetails.Bootstrapper{}, + userapi.Bootstrapper{}, + v2.Bootstrapper{}, &queue.Starter{}, } diff --git a/build/configs/default_config.yaml b/build/configs/default_config.yaml index 0b6d1d198..908fdb5f1 100644 --- a/build/configs/default_config.yaml +++ b/build/configs/default_config.yaml @@ -20,8 +20,8 @@ networks: id: 51 # Bootstrap list of nodes that Centrifuge provides to the russianhill testnet bootstrapPeers: - - "/ip4/35.225.200.42/tcp/38202/ipfs/12D3KooWNsZsW7TbcZ58N71UQSK5DjZqmFkHPHwxFKTAyoUUD8df" - - "/ip4/35.225.86.210/tcp/38202/ipfs/12D3KooWDe2swWE3f2iJbUeQ9GUUQBHCrWLbBca9MGMqfmbCgrub" + - "/ip4/35.246.134.95/tcp/38202/ipfs/12D3KooWNsZsW7TbcZ58N71UQSK5DjZqmFkHPHwxFKTAyoUUD8df" + - "/ip4/35.198.161.180/tcp/38202/ipfs/12D3KooWDe2swWE3f2iJbUeQ9GUUQBHCrWLbBca9MGMqfmbCgrub" # Ethereum network ID - Rinkeby ethereumNetworkId: 4 # Latest deployed Smart Contracts for the given testnet @@ -36,8 +36,8 @@ networks: id: 52 # Bootstrap list of nodes that Centrifuge provides to the bernalheights testnet bootstrapPeers: - - "/ip4/104.154.18.51/tcp/38202/ipfs/12D3KooWSbxbKCbZh9JVtsQyVGdTPra4RpSA4tbvs6an11jwGA2z" - - "/ip4/104.155.185.237/tcp/38202/ipfs/12D3KooWQm2cSmrEiaSMV4gUv7WGhpgRwo8woFSsHhZGbGi3aA8x" + - "/ip4/35.242.230.116/tcp/38202/ipfs/12D3KooWSbxbKCbZh9JVtsQyVGdTPra4RpSA4tbvs6an11jwGA2z" + - "/ip4/35.234.72.127/tcp/38202/ipfs/12D3KooWQm2cSmrEiaSMV4gUv7WGhpgRwo8woFSsHhZGbGi3aA8x" # Ethereum network ID - Kovan ethereumNetworkId: 42 # Latest deployed Smart Contracts for the given testnet @@ -52,8 +52,8 @@ networks: id: 53 # Bootstrap list of nodes that Centrifuge provides to the dogpatch (TODO create ropsten bootnodes) bootstrapPeers: - - "/ip4/35.225.200.42/tcp/38202/ipfs/12D3KooWNsZsW7TbcZ58N71UQSK5DjZqmFkHPHwxFKTAyoUUD8df" - - "/ip4/35.225.86.210/tcp/38202/ipfs/12D3KooWDe2swWE3f2iJbUeQ9GUUQBHCrWLbBca9MGMqfmbCgrub" + - "/ip4/35.246.134.95/tcp/38202/ipfs/12D3KooWNsZsW7TbcZ58N71UQSK5DjZqmFkHPHwxFKTAyoUUD8df" + - "/ip4/35.198.161.180/tcp/38202/ipfs/12D3KooWDe2swWE3f2iJbUeQ9GUUQBHCrWLbBca9MGMqfmbCgrub" # Ethereum network ID - Ropsten ethereumNetworkId: 3 # Latest deployed Smart Contracts for the given testnet diff --git a/build/package-lock.json b/build/package-lock.json deleted file mode 100644 index b5d5185f8..000000000 --- a/build/package-lock.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "go-centrifuge", - "version": "0.0.5", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "swagger-merge": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/swagger-merge/-/swagger-merge-0.3.3.tgz", - "integrity": "sha1-az7ogziut14pU+uECGKG3ISvsWs=", - "dev": true - } - } -} diff --git a/build/package.json b/build/package.json deleted file mode 100644 index 06468f3d0..000000000 --- a/build/package.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "go-centrifuge", - "version": "0.0.5", - "description": "Protobuf files & go bindings for go-centrifuge", - "main": "index.js", - "scripts": { - "build_swagger": "node scripts/build_swagger.js" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/centrifuge/go-centrifuge.git" - }, - "author": "", - "bugs": { - "url": "https://github.com/centrifuge/go-centrifuge/issues" - }, - "homepage": "https://github.com/centrifuge/go-centrifuge#readme", - "devDependencies": { - "swagger-merge": "^0.3.3" - } -} diff --git a/build/privacy-enabled-erc721 b/build/privacy-enabled-erc721 new file mode 160000 index 000000000..d7941a61b --- /dev/null +++ b/build/privacy-enabled-erc721 @@ -0,0 +1 @@ +Subproject commit d7941a61b2f1f5d3ad7638b6f3b15a4572f9790d diff --git a/build/scripts/build_swagger.js b/build/scripts/build_swagger.js deleted file mode 100644 index 72454e501..000000000 --- a/build/scripts/build_swagger.js +++ /dev/null @@ -1,96 +0,0 @@ -const swaggermerge = require('swagger-merge'); -const fs = require('fs'); -const path = require('path'); - -const swaggerFilesPath = path.resolve(__dirname, '../../protobufs/gen/swagger'); -const swaggerJsonPath = path.resolve(__dirname, '../../protobufs/gen/swagger.json'); -const swaggerConfig = require(path.resolve(__dirname, '../swagger_config')); - -let authHeader = { - "name": "authorization", - "in": "header", - "description": "Hex encoded centrifuge ID of the account for the intended API action", - "required": true, - "type": "string" -}; - -/* # build_swagger.js - * - * This script recursively searches the swaggerFilesPath for any file ending in .swagger.json - * loading all matching files and merging them into one for easier portability. In the second - * step it then creates an html version of the documentation using spectacles. - * - * The defaults are defined in ../swagger_config.js - */ - - -// Find matching swagger files in path -// From: https://gist.github.com/kethinov/6658166 -let getSwaggerFiles = function(dir, filelist) { - let files = fs.readdirSync(dir); - filelist = filelist || []; - files.forEach(function(file) { - if (fs.statSync(dir + '/' + file).isDirectory()) { - getSwaggerFiles(dir + '/' + file, filelist); - } - else { - if (file.indexOf(".swagger.json") > 0) { - filelist.push(path.join(dir, file)); - } - } - }); - return filelist; -}; - -// Append auth header function -let addAuthHeader = function(obj) { - if (obj.hasOwnProperty("operationId") && obj.operationId === "ping") { - return - } - - if (!obj.hasOwnProperty("parameters")) { - obj.parameters = [] - } - - let foundAuth = false; - obj.parameters.forEach(function (p) { - if (p.name === "authorization") { - foundAuth = true; - } - }); - - if (!foundAuth){ - obj.parameters.push(authHeader); - } -}; - -let files = getSwaggerFiles(swaggerFilesPath); -// There is a default swagger definition in swaggerConfig.defaultSwagger which we add first -swaggerModules = [swaggerConfig.defaultSwagger,]; -files.forEach(function (f) { - swaggerModules.push(require(f)) -}); - -swaggermerge.on('warn', function (msg) { - console.log(msg) -}); - -swaggermerge.on('err', function (msg) { - console.error(msg); - process.exit(1) -}); - -let merged = swaggermerge.merge(swaggerModules, swaggerConfig.info, swaggerConfig.pathPrefix, swaggerConfig.host, swaggerConfig.schemes); - -let keys = Object.keys(merged.paths); - -keys.forEach(function (item) { - let itemKeys = Object.keys(merged.paths[item]); - itemKeys.forEach(function (valueItem) { - addAuthHeader(merged.paths[item][valueItem]) - }) -}); - -let json = JSON.stringify(merged); -console.log("Merged swagger.json, writing to:", swaggerJsonPath); -fs.writeFileSync(swaggerJsonPath, json); diff --git a/build/scripts/install_prototool.sh b/build/scripts/install_prototool.sh deleted file mode 100755 index dd9e7830d..000000000 --- a/build/scripts/install_prototool.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env sh - -PROTOTOOL_BIN=~/bin/$PROTOTOOL_VERSION -mkdir -p ~/bin/$PROTOTOOL_VERSION/ - -if [ -e "${PROTOTOOL_BIN}/prototool" ] -then - echo "Found existing prototool in ${PROTOTOOL_BIN}. Not downloading again - just linking." -else - echo "Downloading prototool" - curl -sSL https://github.com/uber/prototool/releases/download/v$PROTOTOOL_VERSION/prototool-$(uname -s)-$(uname -m) > $PROTOTOOL_BIN/prototool && chmod +x $PROTOTOOL_BIN/prototool && $PROTOTOOL_BIN/prototool -h -fi diff --git a/build/scripts/migrateDApp.sh b/build/scripts/migrateDApp.sh new file mode 100755 index 000000000..c9a5dff10 --- /dev/null +++ b/build/scripts/migrateDApp.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash + +set -e + +ANCHOR_ADDR=$1 +if [ -z ${ANCHOR_ADDR} ]; +then + echo "${ANCHOR_ADDR} not set" + exit 1 +fi + +# Allow passing parent directory as a parameter +PARENT_DIR=$2 +if [ -z ${PARENT_DIR} ]; +then + PARENT_DIR=`pwd` +fi + +source "${PARENT_DIR}/build/scripts/test-dependencies/test-ethereum/env_vars.sh" + +if [ -z ${CENT_ETHEREUM_DAPP_CONTRACTS_DIR} ]; then + CENT_ETHEREUM_DAPP_CONTRACTS_DIR=${PARENT_DIR}/build/privacy-enabled-erc721 +fi + +cd $CENT_ETHEREUM_DAPP_CONTRACTS_DIR + +dapp update +dapp build --extract + +export ETH_RPC_ACCOUNTS=true +export ETH_GAS=$CENT_ETHEREUM_GASLIMIT +export ETH_KEYSTORE="${PARENT_DIR}/build/scripts/test-dependencies/test-ethereum/migrateAccount.json" +export ETH_RPC_URL=$CENT_ETHEREUM_NODEURL +export ETH_PASSWORD="/dev/null" +export ETH_FROM="0x89b0a86583c4444acfd71b463e0d3c55ae1412a5" + +regAddr=$(dapp create "TestNFT" "$ANCHOR_ADDR") + +echo -n "genericNFT $regAddr" > $PARENT_DIR/localAddresses + +cd $PARENT_DIR \ No newline at end of file diff --git a/build/scripts/push_to_swagger.sh b/build/scripts/push_to_swagger.sh index 0b5198283..edfbbe79a 100755 --- a/build/scripts/push_to_swagger.sh +++ b/build/scripts/push_to_swagger.sh @@ -2,11 +2,11 @@ # push the swagger api json to swagger hub echo "pushing swagger.json to SwaggerHub" -VERSION=`jq -c '.info.version' protobufs/gen/swagger.json -r` +VERSION=`jq -c '.info.version' httpapi/swagger.json -r` curl -i -X POST \ - https://api.swaggerhub.com/apis/centrifuge.io/cent-node?version=$VERSION \ + https://api.swaggerhub.com/apis/centrifuge.io/cent-node?version=${VERSION} \ -H "Authorization: $SWAGGER_API_KEY" \ - -H "Content-Type: application/json" -d @./protobufs/gen/swagger.json + -H "Content-Type: application/json" -d @./httpapi/swagger.json exit $? diff --git a/build/scripts/run_swagger.sh b/build/scripts/run_swagger.sh deleted file mode 100755 index 62b780c9c..000000000 --- a/build/scripts/run_swagger.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh - -SWAGGER_PATH=$GOPATH/src/github.com/centrifuge/go-centrifuge/protobufs/gen - -echo "Launching swagger-ui docker container..." -echo "Loading swagger.json from: $SWAGGER_PATH" -echo "Go to http://localhost:8085/ to access it once it's done starting." -docker run --rm -p 8085:8080 -e SWAGGER_JSON=/data/swagger.json -v $SWAGGER_PATH:/data swaggerapi/swagger-ui - diff --git a/build/swagger_config.js b/build/swagger_config.js deleted file mode 100644 index 8e472866e..000000000 --- a/build/swagger_config.js +++ /dev/null @@ -1,19 +0,0 @@ -module.exports = { - info: { - version: "0.0.5", - title: "Centrifuge OS Node API", - description: "\n", - contact: { - name: "Centrifuge", - url: "https://github.com/centrifuge/go-centrifuge", - email: "hello@centrifuge.io", - } - }, - host: "localhost:8082", - pathPrefix: "", - schemes: ['http'], - defaultSwagger: { - consumes: ["application/json",], - produces: ["application/json",], - } -}; diff --git a/centerrors/error.go b/centerrors/error.go deleted file mode 100644 index 91c6d8a56..000000000 --- a/centerrors/error.go +++ /dev/null @@ -1,134 +0,0 @@ -package centerrors - -import ( - "fmt" - "reflect" - - "github.com/centrifuge/centrifuge-protobufs/gen/go/errors" - "github.com/centrifuge/go-centrifuge/code" - "github.com/go-errors/errors" -) - -const ( - // RequiredField error when required field is empty - // Deprecated: in favour of error types in each package - RequiredField = "Required field" - - // NilDocument error when document passed is Nil - // Deprecated: in favour of error types in each package - NilDocument = "Nil document" - - // IdentifierReUsed error when same identifier is re-used - // Deprecated: in favour of error types in each package - IdentifierReUsed = "Identifier re-used" - - // NilDocumentData error when document data is Nil - // Deprecated: in favour of error types in each package - NilDocumentData = "Nil document data" - - // RequirePositiveNumber error when amount or any such is zero or negative - // Deprecated: in favour of error types in each package - RequirePositiveNumber = "Require positive number" -) - -// errpb is the type alias for errorspb.Error -type errpb errorspb.Error - -// Error implements the error interface -// message format: [code]message: [sub errors if any] -func (err *errpb) Error() string { - if err.Errors == nil || len(err.Errors) == 0 { - return fmt.Sprintf("[%d]%s", err.Code, err.Message) - } - - return fmt.Sprintf("[%d]%s: %v", err.Code, err.Message, err.Errors) -} - -// New constructs a new error with code and error message -func New(code code.Code, message string) error { - return NewWithErrors(code, message, nil) -} - -// NewWithErrors constructs a new error with code, error message, and errors -func NewWithErrors(c code.Code, message string, errs map[string]string) error { - if c == code.Ok { - return nil - } - - return &errpb{ - Code: int32(c), - Message: message, - Errors: errs, - } -} - -// P2PError represents p2p error type -type P2PError struct { - err *errorspb.Error -} - -// FromError constructs and returns errorspb.Error -// if bool true, conversion to P2PError successful -// else failed and returns unknown P2PError -func FromError(err error) (*P2PError, bool) { - if err == nil { - return &P2PError{err: &errorspb.Error{Code: int32(code.Ok)}}, true - } - - errpb, ok := err.(*errpb) - if !ok { - return &P2PError{err: &errorspb.Error{Code: int32(code.Unknown), Message: err.Error()}}, false - } - - return &P2PError{err: (*errorspb.Error)(errpb)}, true -} - -// Code returns the error code -func (p2pErr *P2PError) Code() code.Code { - if p2pErr == nil || p2pErr.err == nil { - return code.Ok - } - - return code.To(p2pErr.err.Code) -} - -// Message returns error message -func (p2pErr *P2PError) Message() string { - if p2pErr == nil || p2pErr.err == nil { - return "" - } - - return p2pErr.err.Message -} - -// Errors returns map errors passed -func (p2pErr *P2PError) Errors() map[string]string { - if p2pErr == nil || p2pErr.err == nil { - return nil - } - - return p2pErr.err.Errors -} - -// NilError returns error with Type added to message -// Deprecated: in favour of functions in `github.com/centrifuge/go-centrifuge/errors` -func NilError(param interface{}) error { - return errors.Errorf("NIL %v provided", reflect.TypeOf(param)) -} - -// Wrap appends msg to errpb.Message if it is of type *errpb -// else appends the msg to error through fmt.Errorf -// Deprecated: this is intended for use within p2p or api handlers only, For services and internal errors use the Error type defined in `github.com/centrifuge/go-centrifuge/errors` -func Wrap(err error, msg string) error { - if err == nil { - return fmt.Errorf(msg) - } - - errpb, ok := err.(*errpb) - if !ok { - return fmt.Errorf("%s: %v", msg, err) - } - - errpb.Message = fmt.Sprintf("%s: %v", msg, errpb.Message) - return errpb -} diff --git a/centerrors/error_test.go b/centerrors/error_test.go deleted file mode 100644 index ce386f589..000000000 --- a/centerrors/error_test.go +++ /dev/null @@ -1,77 +0,0 @@ -// +build unit - -package centerrors - -import ( - "reflect" - "testing" - - "github.com/centrifuge/go-centrifuge/code" - "github.com/centrifuge/go-centrifuge/errors" - "github.com/magiconair/properties/assert" -) - -func TestP2PError(t *testing.T) { - tests := []struct { - code code.Code - msg string - errors map[string]string - }{ - { - code: code.AuthenticationFailed, - msg: "Node authentication failed", - }, - - { - code: code.DocumentNotFound, - msg: "Invalid document", - errors: map[string]string{ - "document_root": "root empty", - "next_identifier": "invalid identifier", - }, - }, - - { - code: code.Code(100), - msg: "Unknown error", - }, - } - - for _, c := range tests { - err := NewWithErrors(c.code, c.msg, c.errors) - p2perr, ok := FromError(err) - if !ok { - t.Fatalf("unexpected conversion error: %T", err) - } - - if got := p2perr.Message(); got != c.msg { - t.Fatalf("message mismatch: %s != %s", got, c.msg) - } - - if got := p2perr.Errors(); !reflect.DeepEqual(got, c.errors) { - t.Fatalf("errors mismatch: %v != %v", got, c.errors) - } - - want := code.To(int32(c.code)) - - if got := p2perr.Code(); got != want { - t.Fatalf("code mismatch: %v != %v", got, want) - } - } -} - -func TestWrap(t *testing.T) { - // simple error - err := errors.New("simple-error") - err = Wrap(err, "wrapped error") - assert.Equal(t, err.Error(), "wrapped error: simple-error") - - // p2p error - err = New(code.Unknown, "p2p-error") - err = Wrap(err, "wrapped error") - assert.Equal(t, err.Error(), "[1]wrapped error: p2p-error") - - // nil error - err = Wrap(nil, "nil error") - assert.Equal(t, err.Error(), "nil error") -} diff --git a/code/codes.go b/code/codes.go deleted file mode 100644 index 9e4d81534..000000000 --- a/code/codes.go +++ /dev/null @@ -1,68 +0,0 @@ -package code - -import "net/http" - -// Code represents an error code -// Alias for int32 -type Code int32 - -const ( - // Ok no error code - Ok Code = 0 - - // Unknown operation cancelled due unhandled error - Unknown Code = 1 - - // NetworkMismatch operation cancelled due to Node network mismatch - NetworkMismatch Code = 2 - - // VersionMismatch operation cancelled due to Node version mismatch - VersionMismatch Code = 3 - - // DocumentInvalid operation cancelled due to invalid document. Check for sub errors if any - DocumentInvalid Code = 4 - - // AuthenticationFailed operation called due to failed auth - AuthenticationFailed Code = 5 - - // AuthorizationFailed operation cancelled due to insufficient permissions - AuthorizationFailed Code = 6 - - // DocumentNotFound operation cancelled due to missing document - DocumentNotFound Code = 7 - - // maxCode for boundary limit. increment this to add new error code - maxCode Code = 8 -) - -// httpMapping maps known error codes to HTTP codes -var httpMapping = map[Code]int{ - Ok: http.StatusOK, - Unknown: http.StatusInternalServerError, - NetworkMismatch: http.StatusBadRequest, - VersionMismatch: http.StatusBadRequest, - DocumentInvalid: http.StatusBadRequest, - AuthenticationFailed: http.StatusUnauthorized, - AuthorizationFailed: http.StatusForbidden, - DocumentNotFound: http.StatusNotFound, -} - -// HTTPCode returns mapped HTTP code for error code -// returns 500 for unknown error -func HTTPCode(code Code) int { - if httpCode, ok := httpMapping[code]; ok { - return httpCode - } - - return http.StatusInternalServerError -} - -// To converts int32 to Code -// returns unknown if code >= maxCode -func To(code int32) Code { - if code >= int32(maxCode) { - return Unknown - } - - return Code(code) -} diff --git a/code/codes_test.go b/code/codes_test.go deleted file mode 100644 index 878793866..000000000 --- a/code/codes_test.go +++ /dev/null @@ -1,64 +0,0 @@ -// +build unit - -package code - -import ( - "net/http" - "testing" -) - -func TestHTTPCode(t *testing.T) { - tests := []struct { - code Code - want int - }{ - { - code: Ok, - want: http.StatusOK, - }, - - { - code: DocumentNotFound, - want: http.StatusNotFound, - }, - - { - code: Code(100), - want: http.StatusInternalServerError, - }, - } - - for _, c := range tests { - if got := HTTPCode(c.code); got != c.want { - t.Fatalf("HTTP code mismatch: %d != %d", got, c.want) - } - } -} - -func TestToCode(t *testing.T) { - tests := []struct { - code int32 - want Code - }{ - { - code: 0, - want: Ok, - }, - - { - code: 5, - want: AuthenticationFailed, - }, - - { - code: 10, - want: Unknown, - }, - } - - for _, c := range tests { - if got := To(c.code); got != c.want { - t.Fatalf("Error code mismatch: %d != %d", got, c.want) - } - } -} diff --git a/codecov.yml b/codecov.yml index eb55e8f67..c11c5c6ba 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,6 +1,13 @@ ignore: - - protobufs/gen/**/* - identity/ideth/factory_contract.go - identity/ideth/identity_contract.go - anchors/anchor_contract.go - nft/invoice_unpaid_contract.go + - documents/entity/test_entity.go + - documents/invoice/test_invoice.go + - documents/test_documents.go + - documents/generic/test_generic.go + - pending/test_pending.go + - httpapi/v2/test_v2.go + - migrations/files/* + - testworld/* \ No newline at end of file diff --git a/config/configstore/handler.go b/config/configstore/handler.go deleted file mode 100644 index 8e271e0e4..000000000 --- a/config/configstore/handler.go +++ /dev/null @@ -1,102 +0,0 @@ -package configstore - -import ( - "context" - - "github.com/centrifuge/go-centrifuge/errors" - - "github.com/centrifuge/go-centrifuge/config" - "github.com/centrifuge/go-centrifuge/protobufs/gen/go/account" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/golang/protobuf/ptypes/empty" - logging "github.com/ipfs/go-log" -) - -// ErrDerivingAccount used as generic account deriver type -const ErrDerivingAccount = errors.Error("error deriving account") - -var apiLog = logging.Logger("account-api") - -type grpcHandler struct { - service config.Service -} - -// GRPCAccountHandler returns an implementation of accountpb.AccountServiceServer -func GRPCAccountHandler(svc config.Service) accountpb.AccountServiceServer { - return &grpcHandler{service: svc} -} - -// deriveAllAccountResponse derives all valid accounts, will not return accounts that fail at load time -func (h grpcHandler) deriveAllAccountResponse(cfgs []config.Account) (*accountpb.GetAllAccountResponse, error) { - response := new(accountpb.GetAllAccountResponse) - for _, t := range cfgs { - tpb, err := t.CreateProtobuf() - if err != nil { - bID, err := t.GetIdentityID() - if err != nil { - apiLog.Errorf("%v", errors.NewTypedError(ErrDerivingAccount, errors.New("error getting ID: %v", err))) - } - apiLog.Errorf("%v", errors.NewTypedError(ErrDerivingAccount, errors.New("account [%s]: %v", hexutil.Encode(bID), err))) - continue - } - response.Data = append(response.Data, tpb) - } - return response, nil -} - -func (h grpcHandler) GetAccount(ctx context.Context, req *accountpb.GetAccountRequest) (*accountpb.AccountData, error) { - id, err := hexutil.Decode(req.AccountId) - if err != nil { - return nil, err - } - accountConfig, err := h.service.GetAccount(id) - if err != nil { - return nil, err - } - return accountConfig.CreateProtobuf() -} - -func (h grpcHandler) GetAllAccounts(ctx context.Context, req *empty.Empty) (*accountpb.GetAllAccountResponse, error) { - cfgs, err := h.service.GetAllAccounts() - if err != nil { - return nil, err - } - return h.deriveAllAccountResponse(cfgs) -} - -func (h grpcHandler) CreateAccount(ctx context.Context, data *accountpb.AccountData) (*accountpb.AccountData, error) { - apiLog.Infof("Creating account: %v", data) - accountConfig := new(Account) - err := accountConfig.loadFromProtobuf(data) - if err != nil { - return nil, err - } - tc, err := h.service.CreateAccount(accountConfig) - if err != nil { - return nil, err - } - return tc.CreateProtobuf() -} - -func (h grpcHandler) GenerateAccount(ctx context.Context, req *empty.Empty) (*accountpb.AccountData, error) { - apiLog.Infof("Generating account") - tc, err := h.service.GenerateAccount() - if err != nil { - return nil, err - } - return tc.CreateProtobuf() -} - -func (h grpcHandler) UpdateAccount(ctx context.Context, req *accountpb.UpdateAccountRequest) (*accountpb.AccountData, error) { - apiLog.Infof("Updating account: %v", req) - accountConfig := new(Account) - err := accountConfig.loadFromProtobuf(req.Data) - if err != nil { - return nil, err - } - tc, err := h.service.UpdateAccount(accountConfig) - if err != nil { - return nil, err - } - return tc.CreateProtobuf() -} diff --git a/config/configstore/handler_test.go b/config/configstore/handler_test.go deleted file mode 100644 index fc1ffd8e4..000000000 --- a/config/configstore/handler_test.go +++ /dev/null @@ -1,160 +0,0 @@ -// +build unit - -package configstore - -import ( - "context" - "testing" - - "github.com/centrifuge/go-centrifuge/config" - "github.com/centrifuge/go-centrifuge/protobufs/gen/go/account" - "github.com/centrifuge/go-centrifuge/testingutils/commons" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/stretchr/testify/assert" -) - -func TestGrpcHandler_GetAccountNotExist(t *testing.T) { - idService := &testingcommons.MockIdentityService{} - repo, _, err := getRandomStorage() - assert.Nil(t, err) - repo.RegisterAccount(&Account{}) - svc := DefaultService(repo, idService) - h := GRPCAccountHandler(svc) - readCfg, err := h.GetAccount(context.Background(), &accountpb.GetAccountRequest{AccountId: "0x123456789"}) - assert.NotNil(t, err) - assert.Nil(t, readCfg) -} - -func TestGrpcHandler_GetAccount(t *testing.T) { - idService := &testingcommons.MockIdentityService{} - repo, _, err := getRandomStorage() - assert.Nil(t, err) - repo.RegisterAccount(&Account{}) - svc := DefaultService(repo, idService) - h := GRPCAccountHandler(svc) - accountCfg, err := NewAccount("main", cfg) - assert.Nil(t, err) - accpb, err := accountCfg.CreateProtobuf() - assert.NoError(t, err) - _, err = h.CreateAccount(context.Background(), accpb) - assert.Nil(t, err) - accID, err := accountCfg.GetIdentityID() - assert.Nil(t, err) - readCfg, err := h.GetAccount(context.Background(), &accountpb.GetAccountRequest{AccountId: hexutil.Encode(accID)}) - assert.Nil(t, err) - assert.NotNil(t, readCfg) -} - -func TestGrpcHandler_deriveAllAccountResponseFailure(t *testing.T) { - idService := &testingcommons.MockIdentityService{} - repo, _, err := getRandomStorage() - assert.Nil(t, err) - repo.RegisterAccount(&Account{}) - svc := DefaultService(repo, idService) - h := GRPCAccountHandler(svc) - accountCfg1, err := NewAccount("main", cfg) - assert.NoError(t, err) - accountCfg2, err := NewAccount("main", cfg) - assert.NoError(t, err) - tco := accountCfg1.(*Account) - tco.EthereumAccount = nil - tcs := []config.Account{tco, accountCfg2} - hc := h.(*grpcHandler) - resp, err := hc.deriveAllAccountResponse(tcs) - assert.NoError(t, err) - assert.Equal(t, 1, len(resp.Data)) -} - -func TestGrpcHandler_GetAllAccounts(t *testing.T) { - idService := &testingcommons.MockIdentityService{} - repo, _, err := getRandomStorage() - assert.Nil(t, err) - repo.RegisterAccount(&Account{}) - svc := DefaultService(repo, idService) - h := GRPCAccountHandler(svc) - accountCfg1, err := NewAccount("main", cfg) - assert.NoError(t, err) - accountCfg2, err := NewAccount("main", cfg) - assert.NoError(t, err) - acc := accountCfg2.(*Account) - acc.IdentityID = []byte("0x123456789") - tc1pb, err := accountCfg1.CreateProtobuf() - assert.NoError(t, err) - _, err = h.CreateAccount(context.Background(), tc1pb) - assert.Nil(t, err) - accpb, err := acc.CreateProtobuf() - assert.NoError(t, err) - _, err = h.CreateAccount(context.Background(), accpb) - assert.Nil(t, err) - - resp, err := h.GetAllAccounts(context.Background(), nil) - assert.Nil(t, err) - assert.Equal(t, 2, len(resp.Data)) -} - -func TestGrpcHandler_CreateAccount(t *testing.T) { - idService := &testingcommons.MockIdentityService{} - repo, _, err := getRandomStorage() - assert.Nil(t, err) - repo.RegisterAccount(&Account{}) - svc := DefaultService(repo, idService) - h := GRPCAccountHandler(svc) - tc, err := NewAccount("main", cfg) - assert.Nil(t, err) - accpb, err := tc.CreateProtobuf() - assert.NoError(t, err) - _, err = h.CreateAccount(context.Background(), accpb) - assert.Nil(t, err) - - // Already exists - accpb, err = tc.CreateProtobuf() - assert.NoError(t, err) - _, err = h.CreateAccount(context.Background(), accpb) - assert.NotNil(t, err) -} - -func TestGrpcHandler_GenerateAccount(t *testing.T) { - s := MockService{} - t1, _ := NewAccount(cfg.GetEthereumDefaultAccountName(), cfg) - s.On("GenerateAccount").Return(t1, nil) - h := GRPCAccountHandler(s) - tc, err := h.GenerateAccount(context.Background(), nil) - assert.NoError(t, err) - assert.NotNil(t, tc) -} - -func TestGrpcHandler_UpdateAccount(t *testing.T) { - idService := &testingcommons.MockIdentityService{} - repo, _, err := getRandomStorage() - assert.Nil(t, err) - repo.RegisterAccount(&Account{}) - svc := DefaultService(repo, idService) - h := GRPCAccountHandler(svc) - tcfg, err := NewAccount("main", cfg) - assert.Nil(t, err) - - accID, err := tcfg.GetIdentityID() - assert.Nil(t, err) - - acc := tcfg.(*Account) - - // Config doesn't exist - accpb, err := tcfg.CreateProtobuf() - assert.NoError(t, err) - _, err = h.UpdateAccount(context.Background(), &accountpb.UpdateAccountRequest{AccountId: hexutil.Encode(accID), Data: accpb}) - assert.NotNil(t, err) - - accpb, err = tcfg.CreateProtobuf() - assert.NoError(t, err) - _, err = h.CreateAccount(context.Background(), accpb) - assert.Nil(t, err) - acc.EthereumDefaultAccountName = "other" - tccpb, err := acc.CreateProtobuf() - assert.NoError(t, err) - _, err = h.UpdateAccount(context.Background(), &accountpb.UpdateAccountRequest{AccountId: hexutil.Encode(accID), Data: tccpb}) - assert.Nil(t, err) - - readCfg, err := h.GetAccount(context.Background(), &accountpb.GetAccountRequest{AccountId: hexutil.Encode(accID)}) - assert.Nil(t, err) - assert.Equal(t, acc.EthereumDefaultAccountName, readCfg.EthDefaultAccountName) -} diff --git a/config/configstore/mock_service.go b/config/configstore/mock_service.go index c315f3db6..c1962b845 100644 --- a/config/configstore/mock_service.go +++ b/config/configstore/mock_service.go @@ -14,7 +14,8 @@ type MockService struct { func (m MockService) GenerateAccount() (config.Account, error) { args := m.Called() - return args.Get(0).(config.Account), args.Error(1) + acc, _ := args.Get(0).(config.Account) + return acc, args.Error(1) } func (m MockService) GetConfig() (config.Configuration, error) { @@ -28,10 +29,10 @@ func (m MockService) GetAccount(identifier []byte) (config.Account, error) { return acc, args.Error(1) } -func (m MockService) GetAllAccounts() ([]config.Account, error) { +func (m MockService) GetAccounts() ([]config.Account, error) { args := m.Called() v, _ := args.Get(0).([]config.Account) - return v, nil + return v, args.Error(1) } func (m MockService) CreateConfig(data config.Configuration) (config.Configuration, error) { @@ -41,12 +42,14 @@ func (m MockService) CreateConfig(data config.Configuration) (config.Configurati func (m MockService) CreateAccount(data config.Account) (config.Account, error) { args := m.Called(data) - return args.Get(0).(*Account), args.Error(0) + acc, _ := args.Get(0).(*Account) + return acc, args.Error(1) } func (m MockService) UpdateAccount(data config.Account) (config.Account, error) { args := m.Called(data) - return args.Get(0).(*Account), args.Error(0) + acc, _ := args.Get(0).(*Account) + return acc, args.Error(1) } func (m MockService) DeleteAccount(identifier []byte) error { diff --git a/config/configstore/model.go b/config/configstore/model.go index c4ba899b3..9ca8d111e 100644 --- a/config/configstore/model.go +++ b/config/configstore/model.go @@ -14,23 +14,19 @@ import ( "github.com/centrifuge/go-centrifuge/crypto/secp256k1" "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/go-centrifuge/identity" - "github.com/centrifuge/go-centrifuge/protobufs/gen/go/account" "github.com/centrifuge/go-centrifuge/utils" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" ) -// ErrNilParameter used as nil parameter type -const ErrNilParameter = errors.Error("nil parameter") - // KeyPair represents a key pair config type KeyPair struct { - Pub, Priv string + Pub string `json:"Pub"` + Pvt string `json:"Priv"` } // NewKeyPair creates a KeyPair func NewKeyPair(pub, priv string) KeyPair { - return KeyPair{Pub: pub, Priv: priv} + return KeyPair{Pub: pub, Pvt: priv} } // NodeConfig exposes configs specific to the node @@ -256,12 +252,12 @@ func (nc *NodeConfig) GetIdentityID() ([]byte, error) { // GetP2PKeyPair refer the interface func (nc *NodeConfig) GetP2PKeyPair() (pub, priv string) { - return nc.MainIdentity.P2PKeyPair.Pub, nc.MainIdentity.P2PKeyPair.Priv + return nc.MainIdentity.P2PKeyPair.Pub, nc.MainIdentity.P2PKeyPair.Pvt } // GetSigningKeyPair refer the interface func (nc *NodeConfig) GetSigningKeyPair() (pub, priv string) { - return nc.MainIdentity.SigningKeyPair.Pub, nc.MainIdentity.SigningKeyPair.Priv + return nc.MainIdentity.SigningKeyPair.Pub, nc.MainIdentity.SigningKeyPair.Pvt } // GetPrecommitEnabled refer the interface @@ -322,12 +318,12 @@ func NewNodeConfig(c config.Configuration) config.Configuration { IdentityID: mainIdentity, ReceiveEventNotificationEndpoint: c.GetReceiveEventNotificationEndpoint(), P2PKeyPair: KeyPair{ - Pub: p2pPub, - Priv: p2pPriv, + Pub: p2pPub, + Pvt: p2pPriv, }, SigningKeyPair: KeyPair{ - Pub: signPub, - Priv: signPriv, + Pub: signPub, + Pvt: signPriv, }, }, StoragePath: c.GetStoragePath(), @@ -409,18 +405,18 @@ func (acc *Account) GetReceiveEventNotificationEndpoint() string { } // GetIdentityID gets IdentityID -func (acc *Account) GetIdentityID() ([]byte, error) { - return acc.IdentityID, nil +func (acc *Account) GetIdentityID() []byte { + return acc.IdentityID } // GetP2PKeyPair gets P2PKeyPair func (acc *Account) GetP2PKeyPair() (pub, priv string) { - return acc.P2PKeyPair.Pub, acc.P2PKeyPair.Priv + return acc.P2PKeyPair.Pub, acc.P2PKeyPair.Pvt } // GetSigningKeyPair gets SigningKeyPair func (acc *Account) GetSigningKeyPair() (pub, priv string) { - return acc.SigningKeyPair.Pub, acc.SigningKeyPair.Priv + return acc.SigningKeyPair.Pub, acc.SigningKeyPair.Pvt } // GetEthereumContextWaitTimeout gets EthereumContextWaitTimeout @@ -440,11 +436,7 @@ func (acc *Account) SignMsg(msg []byte) (*coredocumentpb.Signature, error) { return nil, err } - did, err := acc.GetIdentityID() - if err != nil { - return nil, err - } - + did := acc.GetIdentityID() return &coredocumentpb.Signature{ SignatureId: append(did, signingKeyPair.PublicKey...), SignerId: did, @@ -510,15 +502,8 @@ func (acc *Account) GetKeys() (idKeys map[string]config.IDKey, err error) { PublicKey: address32Bytes[:], PrivateKey: sk} } - - id, err := acc.GetIdentityID() - if err != nil { - return idKeys, err - } - acc.IdentityID = id - + acc.IdentityID = acc.GetIdentityID() return acc.keys, nil - } // ID Get the ID of the document represented by this model @@ -541,64 +526,6 @@ func (acc *Account) FromJSON(data []byte) error { return json.Unmarshal(data, acc) } -// CreateProtobuf creates protobuf for config -func (acc *Account) CreateProtobuf() (*accountpb.AccountData, error) { - if acc.EthereumAccount == nil { - return nil, errors.New("nil EthereumAccount field") - } - return &accountpb.AccountData{ - EthAccount: &accountpb.EthereumAccount{ - Address: acc.EthereumAccount.Address, - Key: acc.EthereumAccount.Key, - Password: acc.EthereumAccount.Password, - }, - EthDefaultAccountName: acc.EthereumDefaultAccountName, - ReceiveEventNotificationEndpoint: acc.ReceiveEventNotificationEndpoint, - IdentityId: common.BytesToAddress(acc.IdentityID).Hex(), - P2PKeyPair: &accountpb.KeyPair{ - Pub: acc.P2PKeyPair.Pub, - Pvt: acc.P2PKeyPair.Priv, - }, - SigningKeyPair: &accountpb.KeyPair{ - Pub: acc.SigningKeyPair.Pub, - Pvt: acc.SigningKeyPair.Priv, - }, - }, nil -} - -func (acc *Account) loadFromProtobuf(data *accountpb.AccountData) error { - if data == nil { - return errors.NewTypedError(ErrNilParameter, errors.New("nil data")) - } - if data.EthAccount == nil { - return errors.NewTypedError(ErrNilParameter, errors.New("nil EthAccount field")) - } - if data.P2PKeyPair == nil { - return errors.NewTypedError(ErrNilParameter, errors.New("nil P2PKeyPair field")) - } - if data.SigningKeyPair == nil { - return errors.NewTypedError(ErrNilParameter, errors.New("nil SigningKeyPair field")) - } - acc.EthereumAccount = &config.AccountConfig{ - Address: data.EthAccount.Address, - Key: data.EthAccount.Key, - Password: data.EthAccount.Password, - } - acc.EthereumDefaultAccountName = data.EthDefaultAccountName - acc.IdentityID, _ = hexutil.Decode(data.IdentityId) - acc.ReceiveEventNotificationEndpoint = data.ReceiveEventNotificationEndpoint - acc.P2PKeyPair = KeyPair{ - Pub: data.P2PKeyPair.Pub, - Priv: data.P2PKeyPair.Pvt, - } - acc.SigningKeyPair = KeyPair{ - Pub: data.SigningKeyPair.Pub, - Priv: data.SigningKeyPair.Pvt, - } - - return nil -} - // NewAccount creates a new Account instance with configs func NewAccount(ethAccountName string, c config.Configuration) (config.Account, error) { if ethAccountName == "" { diff --git a/config/configstore/model_test.go b/config/configstore/model_test.go index 6017fa43d..b98490b77 100644 --- a/config/configstore/model_test.go +++ b/config/configstore/model_test.go @@ -10,10 +10,8 @@ import ( "github.com/centrifuge/go-centrifuge/config" "github.com/centrifuge/go-centrifuge/identity" - "github.com/centrifuge/go-centrifuge/protobufs/gen/go/account" "github.com/centrifuge/go-centrifuge/utils" "github.com/ethereum/go-ethereum/common" - "github.com/golang/protobuf/proto" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -277,91 +275,6 @@ func TestNewAccountConfig(t *testing.T) { c.AssertExpectations(t) } -func TestAccountProtobuf_validationFailures(t *testing.T) { - c := &mockConfig{} - c.On("GetEthereumAccount", "name").Return(&config.AccountConfig{}, nil) - c.On("GetEthereumDefaultAccountName").Return("dummyAcc") - c.On("GetReceiveEventNotificationEndpoint").Return("dummyNotifier") - c.On("GetIdentityID").Return(utils.RandomSlice(identity.DIDLength), nil) - c.On("GetP2PKeyPair").Return("pub", "priv") - c.On("GetSigningKeyPair").Return("pub", "priv") - c.On("GetEthereumContextWaitTimeout").Return(time.Second) - c.On("GetPrecommitEnabled").Return(true) - tc, err := NewAccount("name", c) - assert.Nil(t, err) - c.AssertExpectations(t) - - // Nil EthAccount - tco := tc.(*Account) - tco.EthereumAccount = nil - accpb, err := tco.CreateProtobuf() - assert.Error(t, err) - assert.Nil(t, accpb) - - // Nil payload - tc, err = NewAccount("name", c) - assert.Nil(t, err) - accpb, err = tc.CreateProtobuf() - assert.NoError(t, err) - tco = tc.(*Account) - err = tco.loadFromProtobuf(nil) - assert.Error(t, err) - - // Nil EthAccount - ethacc := proto.Clone(accpb.EthAccount) - accpb.EthAccount = nil - err = tco.loadFromProtobuf(accpb) - assert.Error(t, err) - accpb.EthAccount = ethacc.(*accountpb.EthereumAccount) - - // Nil P2PKeyPair - p2pKey := proto.Clone(accpb.P2PKeyPair) - accpb.P2PKeyPair = nil - err = tco.loadFromProtobuf(accpb) - assert.Error(t, err) - accpb.P2PKeyPair = p2pKey.(*accountpb.KeyPair) - - // Nil SigningKeyPair - signKey := proto.Clone(accpb.SigningKeyPair) - accpb.SigningKeyPair = nil - err = tco.loadFromProtobuf(accpb) - assert.Error(t, err) - accpb.SigningKeyPair = signKey.(*accountpb.KeyPair) - -} - -func TestAccountConfigProtobuf(t *testing.T) { - c := &mockConfig{} - c.On("GetEthereumAccount", "name").Return(&config.AccountConfig{}, nil).Once() - c.On("GetEthereumDefaultAccountName").Return("dummyAcc").Once() - c.On("GetReceiveEventNotificationEndpoint").Return("dummyNotifier").Once() - c.On("GetIdentityID").Return(utils.RandomSlice(identity.DIDLength), nil).Once() - c.On("GetP2PKeyPair").Return("pub", "priv").Once() - c.On("GetSigningKeyPair").Return("pub", "priv").Once() - c.On("GetEthereumContextWaitTimeout").Return(time.Second).Once() - c.On("GetPrecommitEnabled").Return(true).Once() - tc, err := NewAccount("name", c) - assert.Nil(t, err) - c.AssertExpectations(t) - - accpb, err := tc.CreateProtobuf() - assert.NoError(t, err) - assert.Equal(t, tc.GetReceiveEventNotificationEndpoint(), accpb.ReceiveEventNotificationEndpoint) - i, err := tc.GetIdentityID() - assert.Nil(t, err) - - assert.Equal(t, common.BytesToAddress(i).Hex(), common.HexToAddress(accpb.IdentityId).Hex()) - _, priv := tc.GetSigningKeyPair() - assert.Equal(t, priv, accpb.SigningKeyPair.Pvt) - - tcCopy := new(Account) - err = tcCopy.loadFromProtobuf(accpb) - assert.NoError(t, err) - assert.Equal(t, accpb.ReceiveEventNotificationEndpoint, tcCopy.ReceiveEventNotificationEndpoint) - assert.Equal(t, common.HexToAddress(accpb.IdentityId).Hex(), common.BytesToAddress(tcCopy.IdentityID).Hex()) - assert.Equal(t, accpb.SigningKeyPair.Pvt, tcCopy.SigningKeyPair.Priv) -} - func createMockConfig() *mockConfig { c := &mockConfig{} c.On("GetStoragePath").Return("dummyStorage").Once() diff --git a/config/configstore/repository.go b/config/configstore/repository.go index 271d87fec..342a14503 100644 --- a/config/configstore/repository.go +++ b/config/configstore/repository.go @@ -24,7 +24,7 @@ type Repository interface { // GetConfig returns the node config model GetConfig() (config.Configuration, error) - // GetAllAccounts returns a list of all account models in the config DB + // GetAccounts returns a list of all account models in the config DB GetAllAccounts() ([]config.Account, error) // Create creates the account model if not present in the DB. diff --git a/config/configstore/repository_test.go b/config/configstore/repository_test.go index 8129de790..451808c38 100644 --- a/config/configstore/repository_test.go +++ b/config/configstore/repository_test.go @@ -95,8 +95,7 @@ func TestAccountOperations(t *testing.T) { readaccount, err := repo.GetAccount(id) assert.Nil(t, err) assert.Equal(t, reflect.TypeOf(newaccount), readaccount.Type()) - i, err := readaccount.GetIdentityID() - assert.Nil(t, err) + i := readaccount.GetIdentityID() assert.Equal(t, newaccount.IdentityID, i) // Update account @@ -179,9 +178,9 @@ func TestLevelDBRepo_GetAllAccounts(t *testing.T) { accounts, err := repo.GetAllAccounts() assert.Nil(t, err) assert.Equal(t, 3, len(accounts)) - t0Id, _ := accounts[0].GetIdentityID() - t1Id, _ := accounts[1].GetIdentityID() - t2Id, _ := accounts[2].GetIdentityID() + t0Id := accounts[0].GetIdentityID() + t1Id := accounts[1].GetIdentityID() + t2Id := accounts[2].GetIdentityID() assert.Contains(t, ids, t0Id) assert.Contains(t, ids, t1Id) assert.Contains(t, ids, t2Id) @@ -191,7 +190,7 @@ func cleanupDBFiles() { for _, db := range dbFiles { err := os.RemoveAll(db) if err != nil { - apiLog.Warningf("Cleanup warn: %v", err) + accLog.Warningf("Cleanup warn: %v", err) } } } diff --git a/config/configstore/service.go b/config/configstore/service.go index 87d7361b4..b0743a600 100644 --- a/config/configstore/service.go +++ b/config/configstore/service.go @@ -7,6 +7,7 @@ import ( coredocumentpb "github.com/centrifuge/centrifuge-protobufs/gen/go/coredocument" "github.com/centrifuge/go-centrifuge/crypto" + "github.com/ipfs/go-log" "github.com/centrifuge/go-centrifuge/bootstrap" "github.com/centrifuge/go-centrifuge/config" @@ -20,6 +21,8 @@ const ( signingPrivKeyName = "signingKey.key.pem" ) +var accLog = log.Logger("accounts") + // ProtocolSetter sets the protocol on host for the centID type ProtocolSetter interface { InitProtocolForDID(DID *identity.DID) @@ -45,7 +48,7 @@ func (s service) GetAccount(identifier []byte) (config.Account, error) { return s.repo.GetAccount(identifier) } -func (s service) GetAllAccounts() ([]config.Account, error) { +func (s service) GetAccounts() ([]config.Account, error) { return s.repo.GetAllAccounts() } @@ -58,10 +61,7 @@ func (s service) CreateConfig(data config.Configuration) (config.Configuration, } func (s service) CreateAccount(data config.Account) (config.Account, error) { - id, err := data.GetIdentityID() - if err != nil { - return nil, err - } + id := data.GetIdentityID() return data, s.repo.CreateAccount(id, data) } @@ -118,10 +118,10 @@ func generateAccountKeys(keystore string, acc *Account, DID *identity.DID) (*Acc return nil, err } acc.SigningKeyPair = KeyPair{ - Pub: sPub, - Priv: sPriv, + Pub: sPub, + Pvt: sPriv, } - err = crypto.GenerateSigningKeyPair(acc.SigningKeyPair.Pub, acc.SigningKeyPair.Priv, crypto.CurveSecp256K1) + err = crypto.GenerateSigningKeyPair(acc.SigningKeyPair.Pub, acc.SigningKeyPair.Pvt, crypto.CurveSecp256K1) if err != nil { return nil, err } @@ -142,10 +142,7 @@ func createKeyPath(keyStorepath string, DID *identity.DID, keyName string) (stri } func (s service) UpdateAccount(data config.Account) (config.Account, error) { - id, err := data.GetIdentityID() - if err != nil { - return nil, err - } + id := data.GetIdentityID() return data, s.repo.UpdateAccount(id, data) } @@ -171,13 +168,13 @@ func RetrieveConfig(dbOnly bool, ctx map[string]interface{}) (config.Configurati // may be we need a way to detect a corrupted db here cfg, err = cfgService.GetConfig() if err != nil { - apiLog.Warningf("could not load config from db: %v", err) + accLog.Warningf("could not load config from db: %v", err) } return cfg, nil } // we have to allow loading from file in case this is coming from create config cmd where we don't add configs to db - if _, ok := ctx[bootstrap.BootstrappedConfig]; ok && cfg == nil && !dbOnly { + if _, ok := ctx[bootstrap.BootstrappedConfig]; ok && !dbOnly { cfg = ctx[bootstrap.BootstrappedConfig].(config.Configuration) } else { return nil, errors.NewTypedError(config.ErrConfigRetrieve, err) diff --git a/config/configstore/service_integration_test.go b/config/configstore/service_integration_test.go index 5034cffee..b6452f8ab 100644 --- a/config/configstore/service_integration_test.go +++ b/config/configstore/service_integration_test.go @@ -41,12 +41,11 @@ func TestMain(m *testing.M) { func TestService_GenerateAccountHappy(t *testing.T) { tct, err := cfgSvc.GenerateAccount() assert.NoError(t, err) - i, err := tct.GetIdentityID() - assert.NoError(t, err) + i := tct.GetIdentityID() tc, err := cfgSvc.GetAccount(i) assert.NoError(t, err) assert.NotNil(t, tc) - i, _ = tc.GetIdentityID() + i = tc.GetIdentityID() did, err := identity.NewDIDFromBytes(i) assert.NoError(t, err) assert.True(t, tc.GetEthereumDefaultAccountName() != "") diff --git a/config/configstore/service_test.go b/config/configstore/service_test.go index 1b929dd1c..a862c69d0 100644 --- a/config/configstore/service_test.go +++ b/config/configstore/service_test.go @@ -57,7 +57,7 @@ func TestService_GetAccount(t *testing.T) { svc := DefaultService(repo, idService) accountCfg, err := NewAccount("main", cfg) assert.Nil(t, err) - accID, _ := accountCfg.GetIdentityID() + accID := accountCfg.GetIdentityID() err = repo.CreateAccount(accID, accountCfg) assert.Nil(t, err) cfg, err := svc.GetAccount(accID) @@ -91,10 +91,8 @@ func TestService_Createaccount(t *testing.T) { assert.Nil(t, err) newCfg, err := svc.CreateAccount(accountCfg) assert.Nil(t, err) - i, err := newCfg.GetIdentityID() - assert.Nil(t, err) - accID, err := accountCfg.GetIdentityID() - assert.Nil(t, err) + i := newCfg.GetIdentityID() + accID := accountCfg.GetIdentityID() assert.Equal(t, accID, i) //account already exists @@ -117,10 +115,8 @@ func TestService_Updateaccount(t *testing.T) { newCfg, err = svc.CreateAccount(accountCfg) assert.Nil(t, err) - i, err := newCfg.GetIdentityID() - assert.Nil(t, err) - accID, err := accountCfg.GetIdentityID() - assert.Nil(t, err) + i := newCfg.GetIdentityID() + accID := accountCfg.GetIdentityID() assert.Equal(t, accID, i) acc := accountCfg.(*Account) @@ -138,8 +134,7 @@ func TestService_Deleteaccount(t *testing.T) { svc := DefaultService(repo, idService) accountCfg, err := NewAccount("main", cfg) assert.Nil(t, err) - accID, err := accountCfg.GetIdentityID() - assert.Nil(t, err) + accID := accountCfg.GetIdentityID() //No config, no error err = svc.DeleteAccount(accID) @@ -163,7 +158,7 @@ func TestGenerateaccountKeys(t *testing.T) { assert.NotNil(t, tc.SigningKeyPair) _, err = os.Stat(tc.SigningKeyPair.Pub) assert.False(t, os.IsNotExist(err)) - _, err = os.Stat(tc.SigningKeyPair.Priv) + _, err = os.Stat(tc.SigningKeyPair.Pvt) assert.False(t, os.IsNotExist(err)) } @@ -186,8 +181,7 @@ func TestService_Sign(t *testing.T) { assert.Nil(t, err) acc, err := svc.CreateAccount(accountCfg) assert.NoError(t, err) - accountID, err = acc.GetIdentityID() - assert.NoError(t, err) + accountID = acc.GetIdentityID() sig, err := svc.Sign(accountID, payload) assert.NoError(t, err) assert.Equal(t, sig.SignerId, accountID) diff --git a/config/configuration.go b/config/configuration.go index 3862ff8d7..49aec7fda 100644 --- a/config/configuration.go +++ b/config/configuration.go @@ -17,9 +17,7 @@ import ( "time" "github.com/centrifuge/centrifuge-protobufs/gen/go/coredocument" - "github.com/centrifuge/go-centrifuge/centerrors" "github.com/centrifuge/go-centrifuge/errors" - "github.com/centrifuge/go-centrifuge/protobufs/gen/go/account" "github.com/centrifuge/go-centrifuge/resources" "github.com/centrifuge/go-centrifuge/storage" "github.com/ethereum/go-ethereum/common" @@ -159,21 +157,18 @@ type Account interface { GetEthereumAccount() *AccountConfig GetEthereumDefaultAccountName() string GetReceiveEventNotificationEndpoint() string - GetIdentityID() ([]byte, error) + GetIdentityID() []byte GetP2PKeyPair() (pub, priv string) GetSigningKeyPair() (pub, priv string) GetEthereumContextWaitTimeout() time.Duration GetPrecommitEnabled() bool - - // CreateProtobuf creates protobuf - CreateProtobuf() (*accountpb.AccountData, error) } // Service exposes functions over the config objects type Service interface { GetConfig() (Configuration, error) GetAccount(identifier []byte) (Account, error) - GetAllAccounts() ([]Account, error) + GetAccounts() ([]Account, error) CreateConfig(data Configuration) (Configuration, error) CreateAccount(data Account) (Account, error) GenerateAccount() (Account, error) @@ -429,7 +424,7 @@ func (c *configuration) GetNetworkID() uint32 { func (c *configuration) GetIdentityID() ([]byte, error) { id, err := hexutil.Decode(c.GetString("identityId")) if err != nil { - return nil, centerrors.Wrap(err, "can't read identityId from config") + return nil, errors.New("can't read identityId from config %v", err) } return id, err } diff --git a/contextutil/context.go b/contextutil/context.go index ee9b467cf..56f145ecc 100644 --- a/contextutil/context.go +++ b/contextutil/context.go @@ -2,10 +2,7 @@ package contextutil import ( "context" - "fmt" - "github.com/centrifuge/go-centrifuge/centerrors" - "github.com/centrifuge/go-centrifuge/code" "github.com/centrifuge/go-centrifuge/config" "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/go-centrifuge/identity" @@ -52,10 +49,7 @@ func AccountDID(ctx context.Context) (identity.DID, error) { if err != nil { return identity.DID{}, err } - didBytes, err := acc.GetIdentityID() - if err != nil { - return identity.DID{}, err - } + didBytes := acc.GetIdentityID() did, err := identity.NewDIDFromBytes(didBytes) if err != nil { return identity.DID{}, err @@ -76,22 +70,22 @@ func Account(ctx context.Context) (config.Account, error) { func Context(ctx context.Context, cs config.Service) (context.Context, error) { tcIDHex, ok := ctx.Value(config.AccountHeaderKey).(string) if !ok { - return nil, centerrors.New(code.Unknown, fmt.Sprintf("failed to get header %v", config.AccountHeaderKey)) + return nil, errors.New("failed to get header %v", config.AccountHeaderKey) } tcID, err := hexutil.Decode(tcIDHex) if err != nil { - return nil, centerrors.New(code.Unknown, fmt.Sprintf("failed to get header: %v", err)) + return nil, errors.New("failed to get header: %v", err) } tc, err := cs.GetAccount(tcID) if err != nil { - return nil, centerrors.New(code.Unknown, fmt.Sprintf("failed to get header: %v", err)) + return nil, errors.New("failed to get header: %v", err) } ctxHeader, err := New(ctx, tc) if err != nil { - return nil, centerrors.New(code.Unknown, fmt.Sprintf("failed to get header: %v", err)) + return nil, errors.New("failed to get header: %v", err) } return ctxHeader, nil } diff --git a/documents/anchor.go b/documents/anchor.go index 33071cb94..d765d4385 100644 --- a/documents/anchor.go +++ b/documents/anchor.go @@ -70,6 +70,11 @@ func AnchorDocument(ctx context.Context, model Model, proc AnchorProcessor, upda return nil, errors.NewTypedError(ErrDocumentAnchoring, errors.New("failed to anchor document: %v", err)) } + // set the status to committed + if err = model.SetStatus(Committed); err != nil { + return nil, err + } + err = updater(id, model) if err != nil { return nil, errors.NewTypedError(ErrDocumentAnchoring, err) diff --git a/documents/anchor_task.go b/documents/anchor_task.go index 361d72e8a..6619b8cf9 100644 --- a/documents/anchor_task.go +++ b/documents/anchor_task.go @@ -2,10 +2,7 @@ package documents import ( "context" - "fmt" - "github.com/centrifuge/go-centrifuge/centerrors" - "github.com/centrifuge/go-centrifuge/code" "github.com/centrifuge/go-centrifuge/config" "github.com/centrifuge/go-centrifuge/contextutil" "github.com/centrifuge/go-centrifuge/errors" @@ -98,7 +95,7 @@ func (d *documentAnchorTask) RunTask() (res interface{}, err error) { tc, err := d.config.GetAccount(d.accountID[:]) if err != nil { log.Error(err) - return nil, centerrors.New(code.Unknown, fmt.Sprintf("failed to get header: %v", err)) + return nil, errors.New("failed to get header: %v", err) } jobCtx := contextutil.WithJob(context.Background(), d.JobID) ctxh, err := contextutil.New(jobCtx, tc) @@ -142,7 +139,7 @@ func initDocumentAnchorTask(jobMan jobs.Manager, tq queue.TaskQueuer, accountID } // CreateAnchorJob creates a job for anchoring a document using jobs manager -func CreateAnchorJob(parentCtx context.Context, jobsMan jobs.Manager, tq queue.TaskQueuer, self identity.DID, jobID jobs.JobID, documentID []byte) (jobs.JobID, chan bool, error) { +func CreateAnchorJob(parentCtx context.Context, jobsMan jobs.Manager, tq queue.TaskQueuer, self identity.DID, jobID jobs.JobID, documentID []byte) (jobs.JobID, chan error, error) { jobID, done, err := jobsMan.ExecuteWithinJob(contextutil.Copy(parentCtx), self, jobID, "anchor document", func(accountID identity.DID, jobID jobs.JobID, jobsMan jobs.Manager, errChan chan<- error) { tr, err := initDocumentAnchorTask(jobsMan, tq, accountID, documentID, jobID) if err != nil { diff --git a/documents/attribute.go b/documents/attribute.go index 96f90d090..b0dc11401 100644 --- a/documents/attribute.go +++ b/documents/attribute.go @@ -1,11 +1,13 @@ package documents import ( + "fmt" "strings" "time" "github.com/centrifuge/go-centrifuge/config" "github.com/centrifuge/go-centrifuge/crypto" + "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/go-centrifuge/identity" "github.com/centrifuge/go-centrifuge/utils" "github.com/ethereum/go-ethereum/common/hexutil" @@ -20,6 +22,14 @@ func (a AttributeType) String() string { return string(a) } +// MonetaryType represents the monetary type of the attribute +type MonetaryType string + +// String returns the readable name of the monetary type. +func (a MonetaryType) String() string { + return string(a) +} + const ( // AttrInt256 is the standard integer custom attribute type AttrInt256 AttributeType = "integer" @@ -38,12 +48,18 @@ const ( // AttrSigned is the custom signature attribute type AttrSigned AttributeType = "signed" + + // AttrMonetary is the monetary attribute type + AttrMonetary AttributeType = "monetary" + + // MonetaryToken is the monetary type for tokens + MonetaryToken MonetaryType = "token" ) // isAttrTypeAllowed checks if the given attribute type is implemented and returns its `reflect.Type` if allowed. func isAttrTypeAllowed(attr AttributeType) bool { switch attr { - case AttrInt256, AttrDecimal, AttrString, AttrBytes, AttrTimestamp, AttrSigned: + case AttrInt256, AttrDecimal, AttrString, AttrBytes, AttrTimestamp, AttrSigned, AttrMonetary: return true default: return false @@ -104,6 +120,27 @@ func (s Signed) String() string { return s.Identity.String() } +// Monetary is a custom attribute type for monetary values +type Monetary struct { + Value *Decimal + ChainID []byte + Type MonetaryType + ID []byte // Currency USD|0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2(DAI)|ETH +} + +// String returns the readable representation of the monetary value +func (m Monetary) String() string { + chStr := "" + if len(m.ChainID) > 0 { + chStr = "@" + hexutil.Encode(m.ChainID) + } + mID := string(m.ID) + if m.Type == MonetaryToken { + mID = hexutil.Encode(m.ID) + } + return fmt.Sprintf("%s %s%s", m.Value.String(), mID, chStr) +} + // AttrVal represents a strongly typed value of an attribute type AttrVal struct { Type AttributeType @@ -113,6 +150,7 @@ type AttrVal struct { Bytes []byte Timestamp *timestamp.Timestamp Signed Signed + Monetary Monetary } // AttrValFromString converts the string value to necessary type based on the attribute type. @@ -129,11 +167,10 @@ func AttrValFromString(attrType AttributeType, value string) (attrVal AttrVal, e attrVal.Bytes, err = hexutil.Decode(value) case AttrTimestamp: var t time.Time - t, err = time.Parse(time.RFC3339, value) + t, err = time.Parse(time.RFC3339Nano, value) if err != nil { return attrVal, err } - attrVal.Timestamp, err = utils.ToTimestamp(t.UTC()) default: return attrVal, ErrNotValidAttrType @@ -163,10 +200,11 @@ func (attrVal AttrVal) String() (str string, err error) { if err != nil { break } - - str = tp.UTC().Format(time.RFC3339) + str = tp.UTC().Format(time.RFC3339Nano) case AttrSigned: str = attrVal.Signed.String() + case AttrMonetary: + str = attrVal.Monetary.String() } return str, err @@ -179,8 +217,8 @@ type Attribute struct { Value AttrVal } -// NewAttribute creates a new custom attribute. -func NewAttribute(keyLabel string, attrType AttributeType, value string) (attr Attribute, err error) { +// NewStringAttribute creates a new custom attribute. +func NewStringAttribute(keyLabel string, attrType AttributeType, value string) (attr Attribute, err error) { attrKey, err := AttrKeyFromLabel(keyLabel) if err != nil { return attr, err @@ -198,6 +236,40 @@ func NewAttribute(keyLabel string, attrType AttributeType, value string) (attr A }, nil } +// NewMonetaryAttribute creates new instance of Monetary Attribute +func NewMonetaryAttribute(keyLabel string, value *Decimal, chainID []byte, id string) (attr Attribute, err error) { + if value == nil { + return attr, errors.NewTypedError(ErrWrongAttrFormat, errors.New("empty value field")) + } + + attrKey, err := AttrKeyFromLabel(keyLabel) + if err != nil { + return attr, err + } + + token := MonetaryToken + idb, err := hexutil.Decode(id) + if err != nil { + token = "" + idb = []byte(id) + } + + if len(idb) > monetaryIDLength { + return attr, errors.NewTypedError(ErrWrongAttrFormat, errors.New("monetaryIDLength exceeds 32 bytes")) + } + + attrVal := AttrVal{ + Type: AttrMonetary, + Monetary: Monetary{Value: value, Type: token, ChainID: chainID, ID: idb}, + } + + return Attribute{ + KeyLabel: keyLabel, + Key: attrKey, + Value: attrVal, + }, nil +} + // NewSignedAttribute returns a new signed attribute // takes keyLabel, signer identity, signer account, model and value // doc version is next version of the document since that is the document version in which the attribute is added. diff --git a/documents/attribute_test.go b/documents/attribute_test.go index 508082fd6..42a5cae3f 100644 --- a/documents/attribute_test.go +++ b/documents/attribute_test.go @@ -4,6 +4,7 @@ package documents import ( "encoding/json" + "fmt" "testing" "time" @@ -108,7 +109,7 @@ func TestNewAttribute(t *testing.T) { } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - attr, err := NewAttribute(test.readableKey, test.attrType, test.value) + attr, err := NewStringAttribute(test.readableKey, test.attrType, test.value) if test.errs { assert.Error(t, err) assert.Equal(t, test.errStr, err.Error()) @@ -179,7 +180,12 @@ func TestAttrValFromString(t *testing.T) { time.Now().UTC().Format(time.RFC3339), false, }, - + { + "timestamp_nano", + AttrTimestamp, + time.Now().UTC().Format(time.RFC3339Nano), + false, + }, { "unknown type", AttributeType("some type"), @@ -261,3 +267,49 @@ func TestNewSignedAttribute(t *testing.T) { acc.AssertExpectations(t) model.AssertExpectations(t) } + +func TestNewMonetaryAttribute(t *testing.T) { + dec, err := NewDecimal("1001.1001") + assert.NoError(t, err) + + // empty label + _, err = NewMonetaryAttribute("", dec, nil, "") + assert.Error(t, err) + assert.True(t, errors.IsOfType(ErrEmptyAttrLabel, err)) + + // monetary ID exceeded length + label := "invoice_amount" + chainID := []byte{1} + idd := "0x9f8f72aa9304c8b593d555f12ef6589cc3a579a29f8f72aa9304c8b593d555f12ef6589cc3a579a2" // 40 bytes + _, err = NewMonetaryAttribute(label, dec, chainID, idd) + assert.Error(t, err) + assert.True(t, errors.IsOfType(ErrWrongAttrFormat, err)) + + // success fiat + idd = "USD" + attr, err := NewMonetaryAttribute(label, dec, chainID, idd) + assert.NoError(t, err) + assert.Equal(t, AttrMonetary, attr.Value.Type) + attrKey, err := AttrKeyFromLabel(label) + assert.NoError(t, err) + assert.Equal(t, attrKey, attr.Key) + assert.Equal(t, []byte(idd), attr.Value.Monetary.ID) + assert.Equal(t, chainID, attr.Value.Monetary.ChainID) + assert.Equal(t, "", attr.Value.Monetary.Type.String()) + assert.Equal(t, fmt.Sprintf("%s %s@%s", dec.String(), idd, hexutil.Encode(chainID)), attr.Value.Monetary.String()) + + // success erc20 + idd = "0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2" + attr, err = NewMonetaryAttribute(label, dec, chainID, idd) + assert.NoError(t, err) + assert.Equal(t, AttrMonetary, attr.Value.Type) + attrKey, err = AttrKeyFromLabel(label) + assert.NoError(t, err) + assert.Equal(t, attrKey, attr.Key) + decIdd, err := hexutil.Decode(idd) + assert.NoError(t, err) + assert.Equal(t, decIdd, attr.Value.Monetary.ID) + assert.Equal(t, chainID, attr.Value.Monetary.ChainID) + assert.Equal(t, MonetaryToken, attr.Value.Monetary.Type) + assert.Equal(t, fmt.Sprintf("%s %s@%s", dec.String(), idd, hexutil.Encode(chainID)), attr.Value.Monetary.String()) +} diff --git a/documents/bootstrapper.go b/documents/bootstrapper.go index b232d6450..0d4f793d5 100644 --- a/documents/bootstrapper.go +++ b/documents/bootstrapper.go @@ -39,7 +39,6 @@ func (Bootstrapper) Bootstrap(ctx map[string]interface{}) error { } repo := NewDBRepository(ldb) - anchorRepo, ok := ctx[anchors.BootstrappedAnchorRepo].(anchors.AnchorRepository) if !ok { return errors.New("anchor repository not initialised") @@ -55,7 +54,17 @@ func (Bootstrapper) Bootstrap(ctx map[string]interface{}) error { return ErrDocumentConfigNotInitialised } - ctx[BootstrappedDocumentService] = DefaultService(cfg, repo, anchorRepo, registry, didService) + queueSrv, ok := ctx[bootstrap.BootstrappedQueueServer].(*queue.Server) + if !ok { + return errors.New("queue server not initialised") + } + + jobManager, ok := ctx[jobs.BootstrappedService].(jobs.Manager) + if !ok { + return errors.New("transaction service not initialised") + } + + ctx[BootstrappedDocumentService] = DefaultService(cfg, repo, anchorRepo, registry, didService, queueSrv, jobManager) ctx[BootstrappedRegistry] = registry ctx[BootstrappedDocumentRepository] = repo return nil diff --git a/documents/bootstrapper_test.go b/documents/bootstrapper_test.go index bca8d6079..289904bb7 100644 --- a/documents/bootstrapper_test.go +++ b/documents/bootstrapper_test.go @@ -10,11 +10,13 @@ import ( "github.com/centrifuge/go-centrifuge/identity" "github.com/centrifuge/go-centrifuge/jobs" "github.com/centrifuge/go-centrifuge/jobs/jobsv1" + "github.com/centrifuge/go-centrifuge/queue" "github.com/centrifuge/go-centrifuge/storage" "github.com/centrifuge/go-centrifuge/storage/leveldb" "github.com/centrifuge/go-centrifuge/testingutils/anchors" "github.com/centrifuge/go-centrifuge/testingutils/commons" "github.com/centrifuge/go-centrifuge/testingutils/config" + "github.com/centrifuge/go-centrifuge/testingutils/testingjobs" "github.com/stretchr/testify/assert" ) @@ -29,6 +31,8 @@ func TestBootstrapper_Bootstrap(t *testing.T) { ctx[jobs.BootstrappedService] = jobsv1.NewManager(&testingconfig.MockConfig{}, jobsv1.NewRepository(repo)) ctx[anchors.BootstrappedAnchorRepo] = new(testinganchors.MockAnchorRepo) ctx[identity.BootstrappedDIDService] = new(testingcommons.MockIdentityService) + ctx[jobs.BootstrappedService] = new(testingjobs.MockJobManager) + ctx[bootstrap.BootstrappedQueueServer] = new(queue.Server) err = Bootstrapper{}.Bootstrap(ctx) assert.Nil(t, err) diff --git a/documents/converters.go b/documents/converters.go index d1ad878d1..a61c558df 100644 --- a/documents/converters.go +++ b/documents/converters.go @@ -1,18 +1,26 @@ package documents import ( + "bytes" + "encoding/binary" "strings" "time" "github.com/centrifuge/centrifuge-protobufs/gen/go/common" "github.com/centrifuge/centrifuge-protobufs/gen/go/coredocument" - "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/go-centrifuge/identity" - "github.com/centrifuge/go-centrifuge/protobufs/gen/go/document" "github.com/centrifuge/go-centrifuge/utils/byteutils" "github.com/centrifuge/go-centrifuge/utils/timeutils" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/golang/protobuf/ptypes/timestamp" +) + +const ( + // maxTimeByteLength is the max length of the byte representation of a timestamp attribute + maxTimeByteLength = 12 + // monetaryChainIDLength is the fixed length of the byte representation of ChainID + monetaryChainIDLength = 4 + // monetaryIDLength is the fixed length of the byte representation of monetary ID + monetaryIDLength = 32 ) // BinaryAttachment represent a single file attached to invoice. @@ -20,17 +28,17 @@ type BinaryAttachment struct { Name string `json:"name"` FileType string `json:"file_type"` // mime type of attached file Size int `json:"size"` // in bytes - Data byteutils.HexBytes `json:"data"` - Checksum byteutils.HexBytes `json:"checksum"` // the md5 checksum of the original file for easier verification + Data byteutils.HexBytes `json:"data" swaggertype:"primitive,string"` + Checksum byteutils.HexBytes `json:"checksum" swaggertype:"primitive,string"` // the md5 checksum of the original file for easier verification } // PaymentDetails holds the payment related details for invoice. type PaymentDetails struct { ID string `json:"id"` // identifying this payment. could be a sequential number, could be a transaction hash of the crypto payment - DateExecuted *time.Time `json:"date_executed"` - Payee *identity.DID `json:"payee"` // centrifuge id of payee - Payer *identity.DID `json:"payer"` // centrifuge id of payer - Amount *Decimal `json:"amount"` + DateExecuted *time.Time `json:"date_executed" swaggertype:"primitive,string"` + Payee *identity.DID `json:"payee" swaggertype:"primitive,string"` // centrifuge id of payee + Payer *identity.DID `json:"payer" swaggertype:"primitive,string"` // centrifuge id of payer + Amount *Decimal `json:"amount" swaggertype:"primitive,string"` Currency string `json:"currency"` Reference string `json:"reference"` // payment reference (e.g. reference field on bank transfer) BankName string `json:"bank_name"` @@ -47,31 +55,6 @@ type PaymentDetails struct { CryptoTo string `json:"crypto_to"` // to address } -// ToClientAttachments converts Attachments to Client Attachments. -func ToClientAttachments(atts []*BinaryAttachment) []*documentpb.BinaryAttachment { - var catts []*documentpb.BinaryAttachment - for _, att := range atts { - var data, checksum string - if len(att.Data) > 0 { - data = hexutil.Encode(att.Data) - } - - if len(att.Checksum) > 0 { - checksum = hexutil.Encode(att.Checksum) - } - - catts = append(catts, &documentpb.BinaryAttachment{ - Name: att.Name, - FileType: att.FileType, - Size: uint64(att.Size), - Data: data, - Checksum: checksum, - }) - } - - return catts -} - // ToProtocolAttachments converts Binary Attchments to protocol attachments. func ToProtocolAttachments(atts []*BinaryAttachment) []*commonpb.BinaryAttachment { var patts []*commonpb.BinaryAttachment @@ -88,38 +71,6 @@ func ToProtocolAttachments(atts []*BinaryAttachment) []*commonpb.BinaryAttachmen return patts } -// FromClientAttachments converts Client Attachments to Binary Attachments -func FromClientAttachments(catts []*documentpb.BinaryAttachment) ([]*BinaryAttachment, error) { - var atts []*BinaryAttachment - for _, att := range catts { - var data, checksum []byte - var err error - if s := strings.TrimSpace(att.Data); s != "" { - data, err = hexutil.Decode(s) - if err != nil { - return nil, err - } - } - - if s := strings.TrimSpace(att.Checksum); s != "" { - checksum, err = hexutil.Decode(s) - if err != nil { - return nil, err - } - } - - atts = append(atts, &BinaryAttachment{ - Name: att.Name, - FileType: att.FileType, - Size: int(att.Size), - Data: data, - Checksum: checksum, - }) - } - - return atts, nil -} - // FromProtocolAttachments converts Protocol attachments to Binary Attachments func FromProtocolAttachments(patts []*commonpb.BinaryAttachment) []*BinaryAttachment { var atts []*BinaryAttachment @@ -136,42 +87,6 @@ func FromProtocolAttachments(patts []*commonpb.BinaryAttachment) []*BinaryAttach return atts } -// ToClientPaymentDetails converts PaymentDetails to client payment details. -func ToClientPaymentDetails(details []*PaymentDetails) ([]*documentpb.PaymentDetails, error) { - var cdetails []*documentpb.PaymentDetails - for _, detail := range details { - decs := DecimalsToStrings(detail.Amount) - dids := identity.DIDsToStrings(detail.Payee, detail.Payer) - tms, err := timeutils.ToProtoTimestamps(detail.DateExecuted) - if err != nil { - return nil, err - } - - cdetails = append(cdetails, &documentpb.PaymentDetails{ - Id: detail.ID, - DateExecuted: tms[0], - Payee: dids[0], - Payer: dids[1], - Amount: decs[0], - Currency: detail.Currency, - Reference: detail.Reference, - BankName: detail.BankName, - BankAddress: detail.BankAddress, - BankAccountCurrency: detail.BankAccountCurrency, - BankAccountHolderName: detail.BankAccountHolderName, - BankAccountNumber: detail.BankAccountNumber, - BankCountry: detail.BankCountry, - BankKey: detail.BankKey, - CryptoChainUri: detail.CryptoChainURI, - CryptoFrom: detail.CryptoFrom, - CryptoTo: detail.CryptoTo, - CryptoTransactionId: detail.CryptoTransactionID, - }) - } - - return cdetails, nil -} - // ToProtocolPaymentDetails converts payment details to protocol payment details func ToProtocolPaymentDetails(details []*PaymentDetails) ([]*commonpb.PaymentDetails, error) { var pdetails []*commonpb.PaymentDetails @@ -212,50 +127,6 @@ func ToProtocolPaymentDetails(details []*PaymentDetails) ([]*commonpb.PaymentDet return pdetails, nil } -// FromClientPaymentDetails converts Client PaymentDetails to PaymentDetails -func FromClientPaymentDetails(cdetails []*documentpb.PaymentDetails) ([]*PaymentDetails, error) { - var details []*PaymentDetails - for _, detail := range cdetails { - decs, err := StringsToDecimals(detail.Amount) - if err != nil { - return nil, err - } - - dids, err := identity.StringsToDIDs(detail.Payee, detail.Payer) - if err != nil { - return nil, err - } - - pts, err := timeutils.FromProtoTimestamps(detail.DateExecuted) - if err != nil { - return nil, err - } - - details = append(details, &PaymentDetails{ - ID: detail.Id, - DateExecuted: pts[0], - Payee: dids[0], - Payer: dids[1], - Amount: decs[0], - Currency: detail.Currency, - Reference: detail.Reference, - BankName: detail.BankName, - BankAddress: detail.BankAddress, - BankAccountCurrency: detail.BankAccountCurrency, - BankAccountHolderName: detail.BankAccountHolderName, - BankAccountNumber: detail.BankAccountNumber, - BankCountry: detail.BankCountry, - BankKey: detail.BankKey, - CryptoChainURI: detail.CryptoChainUri, - CryptoFrom: detail.CryptoFrom, - CryptoTo: detail.CryptoTo, - CryptoTransactionID: detail.CryptoTransactionId, - }) - } - - return details, nil -} - // FromProtocolPaymentDetails converts protocol payment details to PaymentDetails func FromProtocolPaymentDetails(pdetails []*commonpb.PaymentDetails) ([]*PaymentDetails, error) { var details []*PaymentDetails @@ -299,173 +170,6 @@ func FromProtocolPaymentDetails(pdetails []*commonpb.PaymentDetails) ([]*Payment return details, nil } -// FromClientCollaboratorAccess converts client collaborator access to CollaboratorsAccess -func FromClientCollaboratorAccess(racess, waccess []string) (ca CollaboratorsAccess, err error) { - wmap, rmap := make(map[string]struct{}), make(map[string]struct{}) - var wcs, rcs []string - if waccess != nil { - for _, c := range waccess { - c = strings.TrimSpace(strings.ToLower(c)) - if c == "" { - continue - } - - if _, ok := wmap[c]; ok { - continue - } - - wmap[c] = struct{}{} - wcs = append(wcs, c) - } - } - - if racess != nil { - for _, c := range racess { - c = strings.TrimSpace(strings.ToLower(c)) - if c == "" { - continue - } - - if _, ok := wmap[c]; ok { - continue - } - - if _, ok := rmap[c]; ok { - continue - } - - rcs = append(rcs, c) - rmap[c] = struct{}{} - } - } - - rdids, err := identity.StringsToDIDs(rcs...) - if err != nil { - return ca, err - } - - wdids, err := identity.StringsToDIDs(wcs...) - if err != nil { - return ca, err - } - - return CollaboratorsAccess{ - ReadCollaborators: identity.FromPointerDIDs(rdids...), - ReadWriteCollaborators: identity.FromPointerDIDs(wdids...), - }, nil -} - -// ToClientCollaboratorAccess converts CollaboratorAccess to client collaborator access -func ToClientCollaboratorAccess(ca CollaboratorsAccess) (readAccess, writeAccess []string) { - rcs := identity.DIDsToStrings(identity.DIDsPointers(ca.ReadCollaborators...)...) - wcs := identity.DIDsToStrings(identity.DIDsPointers(ca.ReadWriteCollaborators...)...) - return rcs, wcs -} - -// ToClientAttributes converts attribute map to the client api format -func ToClientAttributes(attributes []Attribute) (map[string]*documentpb.Attribute, error) { - if len(attributes) < 1 { - return nil, nil - } - - m := make(map[string]*documentpb.Attribute) - for _, v := range attributes { - val, err := v.Value.String() - if err != nil { - return nil, errors.NewTypedError(ErrCDAttribute, err) - } - - m[v.KeyLabel] = &documentpb.Attribute{ - Key: v.Key.String(), - Type: v.Value.Type.String(), - Value: val, - } - } - - return m, nil -} - -// FromClientAttributes converts the api attributes type to local Attributes map. -func FromClientAttributes(attrs map[string]*documentpb.Attribute) (map[AttrKey]Attribute, error) { - if len(attrs) < 1 { - return nil, nil - } - - m := make(map[AttrKey]Attribute) - for k, at := range attrs { - attr, err := NewAttribute(k, AttributeType(at.Type), at.Value) - if err != nil { - return nil, errors.NewTypedError(ErrCDAttribute, err) - } - - m[attr.Key] = attr - } - - return m, nil -} - -// DeriveResponseHeader derives common response header for model -func DeriveResponseHeader(tokenRegistry TokenRegistry, model Model) (*documentpb.ResponseHeader, error) { - cs, err := model.GetCollaborators() - if err != nil { - return nil, errors.NewTypedError(ErrCollaborators, err) - } - - // we ignore error here because it can happen when a model is first created but its not anchored yet - a, _ := model.Author() - author := a.String() - - // we ignore error here because it can happen when a model is first created but its not anchored yet - time := "" - t, err := model.Timestamp() - if err == nil { - time = t.UTC().String() - } - - nfts := model.NFTs() - cnfts, err := convertNFTs(tokenRegistry, nfts) - if err != nil { - // this could be a temporary failure, so we ignore but warn about the error - log.Warningf("errors encountered when trying to set nfts to the response: %v", errors.NewTypedError(ErrNftNotFound, err)) - } - - rcs, wcs := ToClientCollaboratorAccess(cs) - return &documentpb.ResponseHeader{ - DocumentId: hexutil.Encode(model.ID()), - VersionId: hexutil.Encode(model.CurrentVersion()), - Author: author, - CreatedAt: time, - ReadAccess: rcs, - WriteAccess: wcs, - Nfts: cnfts, - }, nil -} - -func convertNFTs(tokenRegistry TokenRegistry, nfts []*coredocumentpb.NFT) (nnfts []*documentpb.NFT, err error) { - for _, n := range nfts { - regAddress := common.BytesToAddress(n.RegistryId[:common.AddressLength]) - i, errn := tokenRegistry.CurrentIndexOfToken(regAddress, n.TokenId) - if errn != nil || i == nil { - err = errors.AppendError(err, errors.New("token index received is nil or other error: %v", errn)) - continue - } - - o, errn := tokenRegistry.OwnerOf(regAddress, n.TokenId) - if errn != nil { - err = errors.AppendError(err, errn) - continue - } - - nnfts = append(nnfts, &documentpb.NFT{ - Registry: regAddress.Hex(), - Owner: o.Hex(), - TokenId: hexutil.Encode(n.TokenId), - TokenIndex: hexutil.Encode(i.Bytes()), - }) - } - return nnfts, err -} - // toProtocolAttributes convert model attributes to p2p attributes // since the protocol representation of attributes is a list, we will always sort the keys and then insert to the list. func toProtocolAttributes(attrs map[AttrKey]Attribute) (pattrs []*coredocumentpb.Attribute, err error) { @@ -503,7 +207,17 @@ func toProtocolAttributes(attrs map[AttrKey]Attribute) (pattrs []*coredocumentpb case AttrBytes: pattr.Value = &coredocumentpb.Attribute_ByteVal{ByteVal: attr.Value.Bytes} case AttrTimestamp: - pattr.Value = &coredocumentpb.Attribute_TimeVal{TimeVal: attr.Value.Timestamp} + buf := new(bytes.Buffer) + err := binary.Write(buf, binary.BigEndian, attr.Value.Timestamp.Seconds) + if err != nil { + return nil, err + } + err = binary.Write(buf, binary.BigEndian, attr.Value.Timestamp.Nanos) + if err != nil { + return nil, err + } + b := append(make([]byte, maxTimeByteLength-len(buf.Bytes())), buf.Bytes()...) + pattr.Value = &coredocumentpb.Attribute_ByteVal{ByteVal: b} case AttrSigned: signed := attr.Value.Signed pattr.Value = &coredocumentpb.Attribute_SignedVal{ @@ -515,6 +229,20 @@ func toProtocolAttributes(attrs map[AttrKey]Attribute) (pattrs []*coredocumentpb Identity: signed.Identity[:], }, } + case AttrMonetary: + monetary := attr.Value.Monetary + decBytes, err := monetary.Value.Bytes() + if err != nil { + return nil, err + } + pattr.Value = &coredocumentpb.Attribute_MonetaryVal{ + MonetaryVal: &coredocumentpb.Monetary{ + Type: getProtocolMonetaryType(monetary.Type), + Value: decBytes, + Chain: append(make([]byte, monetaryChainIDLength-len(monetary.ChainID)), monetary.ChainID...), + Id: append(make([]byte, monetaryIDLength-len(monetary.ID)), monetary.ID...), + }, + } } pattrs = append(pattrs, pattr) @@ -535,6 +263,22 @@ func getAttributeTypeFromProtocolType(attrType coredocumentpb.AttributeType) Att return AttributeType(strings.ToLower(strings.TrimPrefix(str, attributeProtocolPrefix))) } +func getProtocolMonetaryType(mType MonetaryType) []byte { + ret := []byte{1} + if mType == MonetaryToken { + ret = []byte{2} + } + return ret +} + +func getMonetaryTypeFromProtocolType(mType []byte) MonetaryType { + var ret MonetaryType + if bytes.Equal(mType, []byte{2}) { + ret = MonetaryToken + } + return ret +} + // fromProtocolAttributes converts protocol attribute list to model attribute map func fromProtocolAttributes(pattrs []*coredocumentpb.Attribute) (map[AttrKey]Attribute, error) { m := make(map[AttrKey]Attribute) @@ -577,7 +321,19 @@ func attrValFromProtocolAttribute(attrType AttributeType, attribute *coredocumen case AttrBytes: attrVal.Bytes = attribute.GetByteVal() case AttrTimestamp: - attrVal.Timestamp = attribute.GetTimeVal() + var ns int64 + var nn int32 + bs := bytes.NewBuffer(attribute.GetByteVal()[:maxTimeByteLength-4]) + bn := bytes.NewBuffer(attribute.GetByteVal()[maxTimeByteLength-4:]) + err := binary.Read(bs, binary.BigEndian, &ns) + if err != nil { + return attrVal, err + } + err = binary.Read(bn, binary.BigEndian, &nn) + if err != nil { + return attrVal, err + } + attrVal.Timestamp = ×tamp.Timestamp{Seconds: ns, Nanos: nn} case AttrSigned: val := attribute.GetSignedVal() did, err := identity.NewDIDFromBytes(val.Identity) @@ -592,6 +348,18 @@ func attrValFromProtocolAttribute(attrType AttributeType, attribute *coredocumen Value: val.Value, Signature: val.Signature, } + case AttrMonetary: + val := attribute.GetMonetaryVal() + dec, err := DecimalFromBytes(val.Value) + if err != nil { + return attrVal, err + } + attrVal.Monetary = Monetary{ + Type: getMonetaryTypeFromProtocolType(val.Type), + Value: dec, + ChainID: bytes.TrimLeft(val.Chain, "\x00"), + ID: bytes.TrimLeft(val.Id, "\x00"), + } } return attrVal, err diff --git a/documents/converters_test.go b/documents/converters_test.go index 21f0d0a87..74af88ad7 100644 --- a/documents/converters_test.go +++ b/documents/converters_test.go @@ -3,21 +3,15 @@ package documents import ( - "math/big" - "strings" "testing" "time" "github.com/centrifuge/centrifuge-protobufs/gen/go/coredocument" "github.com/centrifuge/go-centrifuge/errors" - "github.com/centrifuge/go-centrifuge/identity" - "github.com/centrifuge/go-centrifuge/protobufs/gen/go/document" "github.com/centrifuge/go-centrifuge/testingutils/identity" "github.com/centrifuge/go-centrifuge/utils" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" ) func TestBinaryAttachments(t *testing.T) { @@ -39,23 +33,10 @@ func TestBinaryAttachments(t *testing.T) { }, } - catts := ToClientAttachments(atts) patts := ToProtocolAttachments(atts) - fcatts, err := FromClientAttachments(catts) - assert.NoError(t, err) - assert.Equal(t, atts, fcatts) - fpatts := FromProtocolAttachments(patts) assert.Equal(t, atts, fpatts) - - catts[0].Checksum = "some checksum" - _, err = FromClientAttachments(catts) - assert.Error(t, err) - - catts[0].Data = "some data" - _, err = FromClientAttachments(catts) - assert.Error(t, err) } func TestPaymentDetails(t *testing.T) { @@ -71,402 +52,40 @@ func TestPaymentDetails(t *testing.T) { }, } - cdetails, err := ToClientPaymentDetails(details) - assert.NoError(t, err) pdetails, err := ToProtocolPaymentDetails(details) assert.NoError(t, err) - fcdetails, err := FromClientPaymentDetails(cdetails) - assert.NoError(t, err) fpdetails, err := FromProtocolPaymentDetails(pdetails) assert.NoError(t, err) - assert.Equal(t, details, fcdetails) assert.Equal(t, details, fpdetails) - cdetails[0].Payee = "some did" - _, err = FromClientPaymentDetails(cdetails) - assert.Error(t, err) - - cdetails[0].Amount = "0.1.1" - _, err = FromClientPaymentDetails(cdetails) - assert.Error(t, err) - pdetails[0].Amount = utils.RandomSlice(40) _, err = FromProtocolPaymentDetails(pdetails) assert.Error(t, err) } -func TestCollaboratorAccess(t *testing.T) { - id1 := testingidentity.GenerateRandomDID() - id2 := testingidentity.GenerateRandomDID() - id3 := testingidentity.GenerateRandomDID() - rcs := []string{id1.String(), id2.String(), ""} - wcs := []string{id2.String(), id3.String(), ""} - - ca, err := FromClientCollaboratorAccess(rcs, wcs) - assert.NoError(t, err) - assert.Len(t, ca.ReadCollaborators, 1) - assert.Len(t, ca.ReadWriteCollaborators, 2) - - grcs, gwcs := ToClientCollaboratorAccess(ca) - assert.Len(t, grcs, 1) - assert.Equal(t, grcs[0], id1.String()) - assert.Len(t, gwcs, 2) - assert.Contains(t, gwcs, id2.String(), id3.String()) - - wcs[0] = "wrg id" - _, err = FromClientCollaboratorAccess(rcs, wcs) - assert.Error(t, err) - - rcs[0] = "wrg id" - _, err = FromClientCollaboratorAccess(rcs, wcs) - assert.Error(t, err) -} - -func TestDeriveResponseHeader(t *testing.T) { - model := new(mockModel) - model.On("GetCollaborators", mock.Anything).Return(CollaboratorsAccess{}, errors.New("error fetching collaborators")).Once() - _, err := DeriveResponseHeader(nil, model) - assert.Error(t, err) - assert.Contains(t, err.Error(), "error fetching collaborators") - model.AssertExpectations(t) - - id := utils.RandomSlice(32) - did1 := testingidentity.GenerateRandomDID() - did2 := testingidentity.GenerateRandomDID() - ca := CollaboratorsAccess{ - ReadCollaborators: []identity.DID{did1}, - ReadWriteCollaborators: []identity.DID{did2}, - } - model = new(mockModel) - model.On("GetCollaborators", mock.Anything).Return(ca, nil).Once() - model.On("ID").Return(id).Once() - model.On("CurrentVersion").Return(id).Once() - model.On("Author").Return(nil, errors.New("somerror")) - model.On("Timestamp").Return(nil, errors.New("somerror")) - model.On("NFTs").Return(nil) - resp, err := DeriveResponseHeader(nil, model) - assert.NoError(t, err) - assert.Equal(t, hexutil.Encode(id), resp.DocumentId) - assert.Equal(t, hexutil.Encode(id), resp.VersionId) - assert.Len(t, resp.ReadAccess, 1) - assert.Equal(t, resp.ReadAccess[0], did1.String()) - assert.Len(t, resp.WriteAccess, 1) - assert.Equal(t, resp.WriteAccess[0], did2.String()) - model.AssertExpectations(t) -} - -func TestConvertNFTs(t *testing.T) { - regIDs := [][]byte{ - utils.RandomSlice(32), - utils.RandomSlice(32), - } - tokIDs := [][]byte{ - utils.RandomSlice(32), - utils.RandomSlice(32), - } - tokIDx := []*big.Int{ - big.NewInt(1), - big.NewInt(2), - } - addrs := []common.Address{ - common.BytesToAddress(utils.RandomSlice(20)), - common.BytesToAddress(utils.RandomSlice(20)), - } - tests := []struct { - name string - TR func() TokenRegistry - NFTs func() []*coredocumentpb.NFT - isErr bool - errLen int - errMsg string - nftLen int - expectedNFTs []*documentpb.NFT - }{ - { - name: "1 nft, no error", - TR: func() TokenRegistry { - m := new(mockRegistry) - m.On("OwnerOf", mock.Anything, mock.Anything).Return(addrs[0], nil).Once() - m.On("CurrentIndexOfToken", mock.Anything, mock.Anything).Return(tokIDx[0], nil).Once() - return m - }, - NFTs: func() []*coredocumentpb.NFT { - return []*coredocumentpb.NFT{ - { - RegistryId: regIDs[0], - TokenId: tokIDs[0], - }, - } - }, - isErr: false, - nftLen: 1, - expectedNFTs: []*documentpb.NFT{ - { - Registry: hexutil.Encode(regIDs[0][:20]), - Owner: addrs[0].Hex(), - TokenId: hexutil.Encode(tokIDs[0]), - TokenIndex: hexutil.Encode(tokIDx[0].Bytes()), - }, - }, - }, - { - name: "2 nft, no error", - TR: func() TokenRegistry { - m := new(mockRegistry) - m.On("OwnerOf", mock.Anything, mock.Anything).Return(addrs[0], nil).Once() - m.On("OwnerOf", mock.Anything, mock.Anything).Return(addrs[1], nil).Once() - m.On("CurrentIndexOfToken", mock.Anything, mock.Anything).Return(tokIDx[0], nil).Once() - m.On("CurrentIndexOfToken", mock.Anything, mock.Anything).Return(tokIDx[1], nil).Once() - return m - }, - NFTs: func() []*coredocumentpb.NFT { - return []*coredocumentpb.NFT{ - { - RegistryId: regIDs[0], - TokenId: tokIDs[0], - }, - { - RegistryId: regIDs[1], - TokenId: tokIDs[1], - }, - } - }, - isErr: false, - nftLen: 2, - expectedNFTs: []*documentpb.NFT{ - { - Registry: hexutil.Encode(regIDs[0][:20]), - Owner: addrs[0].Hex(), - TokenId: hexutil.Encode(tokIDs[0]), - TokenIndex: hexutil.Encode(tokIDx[0].Bytes()), - }, - { - Registry: hexutil.Encode(regIDs[1][:20]), - Owner: addrs[1].Hex(), - TokenId: hexutil.Encode(tokIDs[1]), - TokenIndex: hexutil.Encode(tokIDx[1].Bytes()), - }, - }, - }, - { - name: "2 nft, ownerOf error", - TR: func() TokenRegistry { - m := new(mockRegistry) - m.On("OwnerOf", mock.Anything, mock.Anything).Return(addrs[0], errors.New("owner error")).Once() - m.On("OwnerOf", mock.Anything, mock.Anything).Return(addrs[1], nil).Once() - m.On("CurrentIndexOfToken", mock.Anything, mock.Anything).Return(tokIDx[0], nil).Once() - m.On("CurrentIndexOfToken", mock.Anything, mock.Anything).Return(tokIDx[1], nil).Once() - return m - }, - NFTs: func() []*coredocumentpb.NFT { - return []*coredocumentpb.NFT{ - { - RegistryId: regIDs[0], - TokenId: tokIDs[0], - }, - { - RegistryId: regIDs[1], - TokenId: tokIDs[1], - }, - } - }, - isErr: true, - errLen: 1, - errMsg: "owner", - nftLen: 1, - expectedNFTs: []*documentpb.NFT{ - { - Registry: hexutil.Encode(regIDs[1][:20]), - Owner: addrs[1].Hex(), - TokenId: hexutil.Encode(tokIDs[1]), - TokenIndex: hexutil.Encode(tokIDx[1].Bytes()), - }, - }, - }, - { - name: "2 nft, CurrentIndexOfToken error", - TR: func() TokenRegistry { - m := new(mockRegistry) - m.On("OwnerOf", mock.Anything, mock.Anything).Return(addrs[1], nil).Once() - m.On("CurrentIndexOfToken", mock.Anything, mock.Anything).Return(tokIDx[0], errors.New("CurrentIndexOfToken error")).Once() - m.On("CurrentIndexOfToken", mock.Anything, mock.Anything).Return(tokIDx[1], nil).Once() - return m - }, - NFTs: func() []*coredocumentpb.NFT { - return []*coredocumentpb.NFT{ - { - RegistryId: regIDs[0], - TokenId: tokIDs[0], - }, - { - RegistryId: regIDs[1], - TokenId: tokIDs[1], - }, - } - }, - isErr: true, - errLen: 1, - errMsg: "CurrentIndexOfToken", - nftLen: 1, - expectedNFTs: []*documentpb.NFT{ - { - Registry: hexutil.Encode(regIDs[1][:20]), - Owner: addrs[1].Hex(), - TokenId: hexutil.Encode(tokIDs[1]), - TokenIndex: hexutil.Encode(tokIDx[1].Bytes()), - }, - }, - }, - { - name: "2 nft, 2 CurrentIndexOfToken error", - TR: func() TokenRegistry { - m := new(mockRegistry) - m.On("CurrentIndexOfToken", mock.Anything, mock.Anything).Return(tokIDx[0], errors.New("CurrentIndexOfToken error")).Once() - m.On("CurrentIndexOfToken", mock.Anything, mock.Anything).Return(tokIDx[1], errors.New("CurrentIndexOfToken error")).Once() - return m - }, - NFTs: func() []*coredocumentpb.NFT { - return []*coredocumentpb.NFT{ - { - RegistryId: regIDs[0], - TokenId: tokIDs[0], - }, - { - RegistryId: regIDs[1], - TokenId: tokIDs[1], - }, - } - }, - isErr: true, - errLen: 2, - errMsg: "CurrentIndexOfToken", - nftLen: 0, - }, - { - name: "2 nft, ownerOf and CurrentIndexOfToken error", - TR: func() TokenRegistry { - m := new(mockRegistry) - m.On("OwnerOf", mock.Anything, mock.Anything).Return(addrs[0], errors.New("owner error")).Once() - m.On("OwnerOf", mock.Anything, mock.Anything).Return(addrs[1], nil).Once() - m.On("CurrentIndexOfToken", mock.Anything, mock.Anything).Return(tokIDx[0], nil).Once() - m.On("CurrentIndexOfToken", mock.Anything, mock.Anything).Return(tokIDx[1], errors.New("CurrentIndexOfToken error")).Once() - return m - }, - NFTs: func() []*coredocumentpb.NFT { - return []*coredocumentpb.NFT{ - { - RegistryId: regIDs[0], - TokenId: tokIDs[0], - }, - { - RegistryId: regIDs[1], - TokenId: tokIDs[1], - }, - } - }, - isErr: true, - errLen: 2, - errMsg: "owner", - nftLen: 0, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - n, err := convertNFTs(test.TR(), test.NFTs()) - if test.isErr { - assert.Error(t, err) - assert.Equal(t, errors.Len(err), test.errLen) - assert.Contains(t, err.Error(), test.errMsg) - } else { - assert.NoError(t, err) - } - assert.Len(t, n, test.nftLen) - if test.nftLen > 0 { - for i, nn := range n { - assert.Equal(t, strings.ToLower(nn.Registry), strings.ToLower(test.expectedNFTs[i].Registry)) - assert.Equal(t, strings.ToLower(nn.TokenIndex), strings.ToLower(test.expectedNFTs[i].TokenIndex)) - assert.Equal(t, strings.ToLower(nn.TokenId), strings.ToLower(test.expectedNFTs[i].TokenId)) - assert.Equal(t, strings.ToLower(nn.Owner), strings.ToLower(test.expectedNFTs[i].Owner)) - } - } - }) - } -} - -func attrsToList(attrs map[AttrKey]Attribute) []Attribute { - var attrsList []Attribute - for _, v := range attrs { - attrsList = append(attrsList, v) - } - - return attrsList +type attribute struct { + Type, Value string } -func TestAttributes(t *testing.T) { - attrs := map[string]*documentpb.Attribute{ - "time_test": { - Type: AttrTimestamp.String(), - Value: time.Now().UTC().Format(time.RFC3339), - }, - - "string_test": { - Type: AttrString.String(), - Value: "some string", - }, - - "bytes_test": { - Type: AttrBytes.String(), - Value: hexutil.Encode([]byte("some bytes data")), - }, - - "int256_test": { - Type: AttrInt256.String(), - Value: "1000000001", - }, - - "decimal_test": { - Type: AttrDecimal.String(), - Value: "1000.000001", - }, - } - - attrMap, err := FromClientAttributes(attrs) - assert.NoError(t, err) +func toAttrsMap(t *testing.T, cattrs map[string]attribute) map[AttrKey]Attribute { + m := make(map[AttrKey]Attribute) + for k, at := range cattrs { + attr, err := NewStringAttribute(k, AttributeType(at.Type), at.Value) + assert.NoError(t, err) - cattrs, err := ToClientAttributes(attrsToList(attrMap)) - assert.NoError(t, err) - for k := range cattrs { - cattrs[k].Key = "" + m[attr.Key] = attr } - assert.Equal(t, attrs, cattrs) - - // failed ToClient - var key AttrKey - var attr Attribute - for k := range attrMap { - key = k - attr = attrMap[k] - break - } - - attr.Value.Type = AttributeType("some type") - attrMap[key] = attr - cattrs, err = ToClientAttributes(attrsToList(attrMap)) - assert.Error(t, err) - // failed FromClient - attrs[""] = &documentpb.Attribute{} - attrMap, err = FromClientAttributes(attrs) - assert.Error(t, err) + return m } func TestP2PAttributes(t *testing.T) { - cattrs := map[string]*documentpb.Attribute{ + cattrs := map[string]attribute{ "time_test": { Type: AttrTimestamp.String(), - Value: time.Now().UTC().Format(time.RFC3339), + Value: time.Now().UTC().Format(time.RFC3339Nano), }, "string_test": { @@ -490,9 +109,7 @@ func TestP2PAttributes(t *testing.T) { }, } - attrs, err := FromClientAttributes(cattrs) - assert.NoError(t, err) - + attrs := toAttrsMap(t, cattrs) pattrs, err := toProtocolAttributes(attrs) assert.NoError(t, err) @@ -523,10 +140,10 @@ func TestP2PAttributes(t *testing.T) { } func TestAttributes_signed(t *testing.T) { - cattrs := map[string]*documentpb.Attribute{ + cattrs := map[string]attribute{ "time_test": { Type: AttrTimestamp.String(), - Value: time.Now().UTC().Format(time.RFC3339), + Value: time.Now().UTC().Format(time.RFC3339Nano), }, "string_test": { @@ -550,9 +167,7 @@ func TestAttributes_signed(t *testing.T) { }, } - attrs, err := FromClientAttributes(cattrs) - assert.NoError(t, err) - + attrs := toAttrsMap(t, cattrs) label := "signed_label" did := testingidentity.GenerateRandomDID() id := utils.RandomSlice(32) @@ -579,7 +194,10 @@ func TestAttributes_signed(t *testing.T) { pattrs, err := toProtocolAttributes(attrs) assert.NoError(t, err) - + assert.Equal(t, "decimal_test", string(pattrs[3].KeyLabel)) + assert.Len(t, pattrs[3].GetByteVal(), maxDecimalByteLength) //decimal length padded to 32 bytes + assert.Equal(t, "time_test", string(pattrs[0].KeyLabel)) + assert.Len(t, pattrs[0].GetByteVal(), maxTimeByteLength) //timestamp length padded to 12 bytes gattrs, err := fromProtocolAttributes(pattrs) assert.NoError(t, err) assert.Equal(t, attrs, gattrs) @@ -590,3 +208,53 @@ func TestAttributes_signed(t *testing.T) { _, err = fromProtocolAttributes(pattrs) assert.Error(t, err) } + +func TestAttributes_monetary(t *testing.T) { + dec, err := NewDecimal("1001.1001") + assert.NoError(t, err) + + tests_monetary := []struct { + label string + dec *Decimal + chainID []byte + id string + }{ + { + label: "invoice_amount", + dec: dec, + chainID: []byte{1}, + id: "USD", + }, + { + label: "invoice_amount_erc20", + dec: dec, + chainID: []byte{1}, + id: "0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2", + }, + } + + for _, v := range tests_monetary { + mon, err := NewMonetaryAttribute(v.label, v.dec, v.chainID, v.id) + assert.NoError(t, err) + m := map[AttrKey]Attribute{ + mon.Key: mon, + } + // convert to protocol + pattrs, err := toProtocolAttributes(m) + assert.NoError(t, err) + assert.Len(t, pattrs, 1) + assert.Equal(t, []byte(v.label), pattrs[0].KeyLabel) + assert.Equal(t, coredocumentpb.AttributeType_ATTRIBUTE_TYPE_MONETARY, pattrs[0].Type) + assert.Len(t, pattrs[0].GetMonetaryVal().Value, 32) + assert.Len(t, pattrs[0].GetMonetaryVal().Id, 32) + assert.Len(t, pattrs[0].GetMonetaryVal().Chain, 4) + + // convert from protocol + cAttr, err := fromProtocolAttributes(pattrs) + assert.NoError(t, err) + assert.Equal(t, mon.Value.Monetary.Value, cAttr[mon.Key].Value.Monetary.Value) + assert.Equal(t, mon.Value.Monetary.ChainID, cAttr[mon.Key].Value.Monetary.ChainID) + assert.Equal(t, mon.Value.Monetary.ID, cAttr[mon.Key].Value.Monetary.ID) + } + +} diff --git a/documents/coredocument.go b/documents/coredocument.go index 2cc71f217..a0a61730f 100644 --- a/documents/coredocument.go +++ b/documents/coredocument.go @@ -13,7 +13,6 @@ import ( "github.com/centrifuge/go-centrifuge/crypto" "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/go-centrifuge/identity" - "github.com/centrifuge/go-centrifuge/protobufs/gen/go/document" "github.com/centrifuge/go-centrifuge/utils" "github.com/centrifuge/precise-proofs/proofs" "github.com/centrifuge/precise-proofs/proofs/proto" @@ -54,8 +53,20 @@ const ( // SignaturesTreePrefix is the human readable prefix for signature props SignaturesTreePrefix = "signatures_tree" + + // Pending status represents document is in pending state + Pending Status = "pending" + + // Committing status represents document is being committed. + Committing Status = "committing" + + // Committed status represents document is committed/anchored. + Committed Status = "committed" ) +// Status represents the document status. +type Status string + // CompactProperties returns the compact property for a given prefix func CompactProperties(key string) []byte { switch key { @@ -90,6 +101,9 @@ type CoreDocument struct { // Attributes are the custom attributes added to the document Attributes map[AttrKey]Attribute + // Status represents document status. + Status Status + Document coredocumentpb.CoreDocument } @@ -109,7 +123,12 @@ func newCoreDocument() (*CoreDocument, error) { return nil, err } - return &CoreDocument{Document: cd, Modified: true, Attributes: make(map[AttrKey]Attribute)}, nil + return &CoreDocument{ + Document: cd, + Modified: true, + Attributes: make(map[AttrKey]Attribute), + Status: Pending, + }, nil } // NewCoreDocumentFromProtobuf returns CoreDocument from the CoreDocument Protobuf. @@ -120,6 +139,11 @@ func NewCoreDocumentFromProtobuf(cd coredocumentpb.CoreDocument) (coreDoc *CoreD return coreDoc, err } +// AccessTokenParams holds details of Grantee and DocumentIdentifier. +type AccessTokenParams struct { + Grantee, DocumentIdentifier string +} + // NewCoreDocument generates new core document with a document type specified by the prefix: po or invoice. // It then adds collaborators, adds read rules and fills salts. func NewCoreDocument(documentPrefix []byte, collaborators CollaboratorsAccess, attributes map[AttrKey]Attribute) (*CoreDocument, error) { @@ -139,7 +163,7 @@ func NewCoreDocument(documentPrefix []byte, collaborators CollaboratorsAccess, a // NewCoreDocumentWithAccessToken generates a new core document with a document type specified by the prefix. // It also adds the targetID as a read collaborator, and adds an access token on this document for the document specified in the documentID parameter -func NewCoreDocumentWithAccessToken(ctx context.Context, documentPrefix []byte, params documentpb.AccessTokenParams) (*CoreDocument, error) { +func NewCoreDocumentWithAccessToken(ctx context.Context, documentPrefix []byte, params AccessTokenParams) (*CoreDocument, error) { did, err := identity.StringsToDIDs(params.Grantee) if err != nil { return nil, err @@ -191,6 +215,22 @@ func (cd *CoreDocument) NextVersion() []byte { return cd.Document.NextVersion } +// GetStatus returns document status +func (cd *CoreDocument) GetStatus() Status { + return cd.Status +} + +// SetStatus set the status of the document. +// if the document is already committed, returns an error if set status is called. +func (cd *CoreDocument) SetStatus(st Status) error { + if cd.Status == Committed && st != Committed { + return ErrCDStatus + } + + cd.Status = st + return nil +} + // AppendSignatures appends signatures to core document. func (cd *CoreDocument) AppendSignatures(signs ...*coredocumentpb.Signature) { if cd.Document.SignatureData == nil { @@ -200,6 +240,45 @@ func (cd *CoreDocument) AppendSignatures(signs ...*coredocumentpb.Signature) { cd.Modified = true } +// Patch overrides only core document data without provisioning new versions since for document updates +func (cd *CoreDocument) Patch(documentPrefix []byte, collaborators CollaboratorsAccess, attrs map[AttrKey]Attribute) (*CoreDocument, error) { + if cd.Status == Committing || cd.Status == Committed { + return nil, ErrDocumentNotInAllowedState + } + + cdp := coredocumentpb.CoreDocument{ + DocumentIdentifier: cd.Document.DocumentIdentifier, + CurrentVersion: cd.Document.CurrentVersion, + PreviousVersion: cd.Document.PreviousVersion, + NextVersion: cd.Document.NextVersion, + CurrentPreimage: cd.Document.CurrentPreimage, + NextPreimage: cd.Document.NextPreimage, + Nfts: cd.Document.Nfts, + AccessTokens: cd.Document.AccessTokens, + SignatureData: new(coredocumentpb.SignatureData), + } + // TODO convert it back to override when we have implemented add/delete for collaborators in API + // for now it always overrides + rcs := collaborators.ReadCollaborators + wcs := collaborators.ReadWriteCollaborators + rcs = append(rcs, wcs...) + + ncd := &CoreDocument{Document: cdp, Status: Pending} + ncd.addCollaboratorsToReadSignRules(rcs) + ncd.addCollaboratorsToTransitionRules(documentPrefix, wcs) + // TODO convert it back to override when we have implemented add/delete for attributes in API + // for now it always overrides + p2pAttrs, attrs, err := updateAttributes(nil, attrs) + if err != nil { + return nil, errors.NewTypedError(ErrCDNewVersion, err) + } + + ncd.Document.Attributes = p2pAttrs + ncd.Attributes = attrs + ncd.Modified = true + return ncd, nil +} + // PrepareNewVersion prepares the next version of the CoreDocument // if initSalts is true, salts will be generated for new version. func (cd *CoreDocument) PrepareNewVersion(documentPrefix []byte, collaborators CollaboratorsAccess, attrs map[AttrKey]Attribute) (*CoreDocument, error) { @@ -228,7 +307,7 @@ func (cd *CoreDocument) PrepareNewVersion(documentPrefix []byte, collaborators C return nil, errors.NewTypedError(ErrCDNewVersion, err) } - ncd := &CoreDocument{Document: cdp} + ncd := &CoreDocument{Document: cdp, Status: Pending} ncd.addCollaboratorsToReadSignRules(rcs) ncd.addCollaboratorsToTransitionRules(documentPrefix, wcs) p2pAttrs, attrs, err := updateAttributes(cd.Document.Attributes, attrs) @@ -242,7 +321,7 @@ func (cd *CoreDocument) PrepareNewVersion(documentPrefix []byte, collaborators C return ncd, nil } -// updateAttributes updates the p2p attributes with new ones and returns the both the formats +// updateAttributes updates the p2p attributes with new ones and returns both formats func updateAttributes(oldAttrs []*coredocumentpb.Attribute, newAttrs map[AttrKey]Attribute) ([]*coredocumentpb.Attribute, map[AttrKey]Attribute, error) { oldAttrsMap, err := fromProtocolAttributes(oldAttrs) if err != nil { @@ -293,6 +372,7 @@ func newTreeProof(t *proofs.DocumentTree, th [][]byte) *TreeProof { // CreateProofs takes document data tree and list to fields and generates proofs. // we will try generating proofs from the dataTree. If failed, we will generate proofs from CoreDocument. // errors out when the proof generation is failed on core document tree. +// It only generates proofs up to the signingRoot level func (cd *CoreDocument) CreateProofs(docType string, dataTree *proofs.DocumentTree, fields []string) (prfs []*proofspb.Proof, err error) { treeProofs := make(map[string]*TreeProof, 3) drTree, err := cd.DocumentRootTree(docType, dataTree.RootHash()) @@ -324,12 +404,12 @@ func (cd *CoreDocument) CreateProofs(docType string, dataTree *proofs.DocumentTr } treeProofs[DRTreePrefix] = newTreeProof(drTree, nil) - // (dataProof => dataRoot) + cdRoot+ signatureRoot = documentRoot - treeProofs[dataPrefix] = newTreeProof(dataTree, append([][]byte{cdRoot}, signatureTree.RootHash())) + // (dataProof => dataRoot) + cdRoot = signingRoot + treeProofs[dataPrefix] = newTreeProof(dataTree, append([][]byte{cdRoot})) // (signatureProof => signatureRoot) + signingRoot = documentRoot treeProofs[SignaturesTreePrefix] = newTreeProof(signatureTree, [][]byte{signingRoot}) - // (cdProof => cdRoot) + dataRoot + signatureRoot = documentRoot - treeProofs[CDTreePrefix] = newTreeProof(cdTree, append([][]byte{dataRoot}, signatureTree.RootHash())) + // (cdProof => cdRoot) + dataRoot = signingRoot + treeProofs[CDTreePrefix] = newTreeProof(cdTree, append([][]byte{dataRoot})) return generateProofs(fields, treeProofs) } @@ -379,7 +459,10 @@ func (cd *CoreDocument) CalculateSignaturesRoot() ([]byte, error) { // getSignatureDataTree returns the merkle tree for the Signature Data root. func (cd *CoreDocument) getSignatureDataTree() (tree *proofs.DocumentTree, err error) { - tree = cd.DefaultTreeWithPrefix(SignaturesTreePrefix, CompactProperties(SignaturesTreePrefix)) + tree, err = cd.DefaultTreeWithPrefix(SignaturesTreePrefix, CompactProperties(SignaturesTreePrefix)) + if err != nil { + return nil, err + } err = tree.AddLeavesFromDocument(cd.Document.SignatureData) if err != nil { return nil, err @@ -400,7 +483,10 @@ func (cd *CoreDocument) DocumentRootTree(docType string, dataRoot []byte) (tree return nil, err } - tree = cd.DefaultTreeWithPrefix(DRTreePrefix, CompactProperties(DRTreePrefix)) + tree, err = cd.DefaultTreeWithPrefix(DRTreePrefix, CompactProperties(DRTreePrefix)) + if err != nil { + return nil, err + } // The first leave added is the signing_root err = tree.AddLeaf(proofs.LeafNode{ @@ -444,7 +530,11 @@ func (cd *CoreDocument) signingRootTree(docType string, dataRoot []byte) (tree * } // create the signing tree with data root and coredoc root as siblings - tree = cd.DefaultTreeWithPrefix(SigningTreePrefix, CompactProperties(SigningTreePrefix)) + tree, err = cd.DefaultTreeWithPrefix(SigningTreePrefix, CompactProperties(SigningTreePrefix)) + if err != nil { + return nil, err + } + err = tree.AddLeaves([]proofs.LeafNode{ { Property: NewLeafProperty(fmt.Sprintf("%s.%s", SigningTreePrefix, DataRootField), append(CompactProperties(SigningTreePrefix), CompactProperties(DataRootField)...)), @@ -472,7 +562,10 @@ func (cd *CoreDocument) signingRootTree(docType string, dataRoot []byte) (tree * // coredocTree returns the merkle tree of the CoreDocument. func (cd *CoreDocument) coredocTree(docType string) (tree *proofs.DocumentTree, err error) { - tree = cd.DefaultTreeWithPrefix(CDTreePrefix, CompactProperties(CDTreePrefix)) + tree, err = cd.DefaultTreeWithPrefix(CDTreePrefix, CompactProperties(CDTreePrefix)) + if err != nil { + return nil, err + } err = tree.AddLeavesFromDocument(&cd.Document) if err != nil { return nil, err diff --git a/documents/coredocument_test.go b/documents/coredocument_test.go index 3f2eb1922..5176e4aab 100644 --- a/documents/coredocument_test.go +++ b/documents/coredocument_test.go @@ -16,10 +16,10 @@ import ( "github.com/centrifuge/go-centrifuge/bootstrap/bootstrappers/testlogging" "github.com/centrifuge/go-centrifuge/config" "github.com/centrifuge/go-centrifuge/config/configstore" + "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/go-centrifuge/ethereum" "github.com/centrifuge/go-centrifuge/identity" "github.com/centrifuge/go-centrifuge/jobs/jobsv1" - "github.com/centrifuge/go-centrifuge/protobufs/gen/go/document" "github.com/centrifuge/go-centrifuge/queue" "github.com/centrifuge/go-centrifuge/storage/leveldb" "github.com/centrifuge/go-centrifuge/testingutils/commons" @@ -33,8 +33,8 @@ import ( ) var ctx map[string]interface{} -var ConfigService config.Service var cfg config.Configuration +var did = testingidentity.GenerateRandomDID() func TestMain(m *testing.M) { ctx = make(map[string]interface{}) @@ -54,13 +54,12 @@ func TestMain(m *testing.M) { ctx[identity.BootstrappedDIDService] = &testingcommons.MockIdentityService{} ctx[identity.BootstrappedDIDFactory] = &testingcommons.MockIdentityFactory{} bootstrap.RunTestBootstrappers(ibootstappers, ctx) - ConfigService = ctx[config.BootstrappedConfigStorage].(config.Service) cfg = ctx[bootstrap.BootstrappedConfig].(config.Configuration) - cfg.Set("keys.p2p.publicKey", "../build/resources/p2pKey.pub.pem") cfg.Set("keys.p2p.privateKey", "../build/resources/p2pKey.key.pem") cfg.Set("keys.signing.publicKey", "../build/resources/signingKey.pub.pem") cfg.Set("keys.signing.privateKey", "../build/resources/signingKey.key.pem") + cfg.Set("identityId", did.String()) result := m.Run() bootstrap.RunTestTeardown(ibootstappers) os.Exit(result) @@ -184,27 +183,27 @@ func TestNewCoreDocumentWithAccessToken(t *testing.T) { did1 := testingidentity.GenerateRandomDID() // wrong granteeID format - at := &documentpb.AccessTokenParams{ + at := AccessTokenParams{ Grantee: "random string", DocumentIdentifier: id, } - ncd, err := NewCoreDocumentWithAccessToken(ctxh, CompactProperties("inv"), *at) + ncd, err := NewCoreDocumentWithAccessToken(ctxh, CompactProperties("inv"), at) assert.Error(t, err) // wrong docID - at = &documentpb.AccessTokenParams{ + at = AccessTokenParams{ Grantee: did1.String(), DocumentIdentifier: "random string", } - ncd, err = NewCoreDocumentWithAccessToken(ctxh, CompactProperties("inv"), *at) + ncd, err = NewCoreDocumentWithAccessToken(ctxh, CompactProperties("inv"), at) assert.Error(t, err) // correct access token params - at = &documentpb.AccessTokenParams{ + at = AccessTokenParams{ Grantee: did1.String(), DocumentIdentifier: id, } - ncd, err = NewCoreDocumentWithAccessToken(ctxh, CompactProperties("inv"), *at) + ncd, err = NewCoreDocumentWithAccessToken(ctxh, CompactProperties("inv"), at) assert.NoError(t, err) token := ncd.Document.AccessTokens[0] @@ -276,6 +275,64 @@ func TestCoreDocument_PrepareNewVersion(t *testing.T) { assert.Equal(t, ncd.GetTestCoreDocWithReset().Roles[1].Collaborators[1], c4[:]) } +func TestCoreDocument_Patch(t *testing.T) { + cd, err := newCoreDocument() + assert.NoError(t, err) + + // not in allowed status error + err = cd.SetStatus(Committed) + assert.NoError(t, err) + ncd, err := cd.Patch(nil, CollaboratorsAccess{}, nil) + assert.Error(t, err) + assert.Nil(t, ncd) + + cd, err = newCoreDocument() + assert.NoError(t, err) + h := sha256.New() + h.Write(cd.GetTestCoreDocWithReset().CurrentPreimage) + var expectedCurrentVersion []byte + expectedCurrentVersion = h.Sum(expectedCurrentVersion) + assert.Equal(t, expectedCurrentVersion, cd.GetTestCoreDocWithReset().CurrentVersion) + c1 := testingidentity.GenerateRandomDID() + c2 := testingidentity.GenerateRandomDID() + attr, err := NewStringAttribute("test", AttrString, "value") + assert.NoError(t, err) + attrs := map[AttrKey]Attribute{ + attr.Key: attr, + } + + ncd, err = cd.Patch(nil, CollaboratorsAccess{[]identity.DID{c1, c2}, nil}, attrs) + assert.NoError(t, err) + assert.NotNil(t, ncd) + assert.Equal(t, cd.CurrentVersion(), ncd.CurrentVersion()) + assert.Equal(t, cd.NextVersion(), ncd.NextVersion()) + collabs, err := ncd.GetCollaborators() + assert.NoError(t, err) + assert.Len(t, collabs.ReadCollaborators, 2) + assert.Equal(t, c1, collabs.ReadCollaborators[0]) + assert.Len(t, ncd.Attributes, 1) + assert.Equal(t, ncd.Attributes[attr.Key].Value, attr.Value) + + // Override existing collaborators and attribute + c3 := testingidentity.GenerateRandomDID() + attr, err = NewStringAttribute("test1", AttrString, "value1") + assert.NoError(t, err) + attrs = map[AttrKey]Attribute{ + attr.Key: attr, + } + oncd, err := ncd.Patch(nil, CollaboratorsAccess{[]identity.DID{c3}, nil}, attrs) + assert.NoError(t, err) + assert.NotNil(t, oncd) + assert.Equal(t, cd.CurrentVersion(), ncd.CurrentVersion()) + assert.Equal(t, cd.NextVersion(), ncd.NextVersion()) + collabs, err = oncd.GetCollaborators() + assert.NoError(t, err) + assert.Len(t, collabs.ReadCollaborators, 1) + assert.Equal(t, c3, collabs.ReadCollaborators[0]) + assert.Len(t, oncd.Attributes, 1) + assert.Equal(t, oncd.Attributes[attr.Key].Value, attr.Value) +} + func TestCoreDocument_newRoleWithCollaborators(t *testing.T) { did1 := testingidentity.GenerateRandomDID() did2 := testingidentity.GenerateRandomDID() @@ -417,7 +474,8 @@ func TestCoreDocument_GenerateProofs(t *testing.T) { h := sha256.New() cd, err := newCoreDocument() assert.NoError(t, err) - testTree := cd.DefaultTreeWithPrefix("prefix", []byte{1, 0, 0, 0}) + testTree, err := cd.DefaultTreeWithPrefix("prefix", []byte{1, 0, 0, 0}) + assert.NoError(t, err) props := []proofs.Property{NewLeafProperty("prefix.sample_field", []byte{1, 0, 0, 0, 0, 0, 0, 200}), NewLeafProperty("prefix.sample_field2", []byte{1, 0, 0, 0, 0, 0, 0, 202})} compactProps := [][]byte{props[0].Compact, props[1].Compact} err = testTree.AddLeaf(proofs.LeafNode{Hash: utils.RandomSlice(32), Hashed: true, Property: props[0]}) @@ -434,9 +492,7 @@ func TestCoreDocument_GenerateProofs(t *testing.T) { cd, err = newCoreDocument() assert.NoError(t, err) cd.GetTestCoreDocWithReset().EmbeddedData = docAny - _, err = cd.CalculateSigningRoot(documenttypes.InvoiceDataTypeUrl, testTree.RootHash()) - assert.NoError(t, err) - docRoot, err := cd.CalculateDocumentRoot(documenttypes.InvoiceDataTypeUrl, testTree.RootHash()) + signingRoot, err := cd.CalculateSigningRoot(documenttypes.InvoiceDataTypeUrl, testTree.RootHash()) assert.NoError(t, err) cdTree, err := cd.coredocTree(documenttypes.InvoiceDataTypeUrl) @@ -449,22 +505,22 @@ func TestCoreDocument_GenerateProofs(t *testing.T) { { "prefix.sample_field", false, - 3, + 2, }, { CDTreePrefix + ".document_identifier", true, - 6, + 5, }, { "prefix.sample_field2", false, - 3, + 2, }, { CDTreePrefix + ".next_version", true, - 6, + 5, }, } for _, test := range tests { @@ -485,7 +541,7 @@ func TestCoreDocument_GenerateProofs(t *testing.T) { assert.NoError(t, err) assert.True(t, valid) } - valid, err := proofs.ValidateProofSortedHashes(l.Hash, p[0].SortedHashes, docRoot, h) + valid, err := proofs.ValidateProofSortedHashes(l.Hash, p[0].SortedHashes, signingRoot, h) assert.NoError(t, err) assert.True(t, valid) }) @@ -638,7 +694,7 @@ func TestCoreDocument_Attribute(t *testing.T) { assert.Error(t, err) // success - attr, err := NewAttribute(label, AttrString, value) + attr, err := NewStringAttribute(label, AttrString, value) assert.NoError(t, err) cd, err = cd.AddAttributes(CollaboratorsAccess{}, true, nil, attr) assert.NoError(t, err) @@ -658,7 +714,7 @@ func TestCoreDocument_Attribute(t *testing.T) { // update nvalue := "2000" - attr, err = NewAttribute(label, AttrDecimal, nvalue) + attr, err = NewStringAttribute(label, AttrDecimal, nvalue) assert.NoError(t, err) cd, err = cd.AddAttributes(CollaboratorsAccess{}, true, nil, attr) assert.NoError(t, err) @@ -691,7 +747,7 @@ func TestCoreDocument_SetUsedAnchorRepoAddress(t *testing.T) { } func TestCoreDocument_UpdateAttributes_both(t *testing.T) { - oldCAttrs := map[string]*documentpb.Attribute{ + oldCAttrs := map[string]attribute{ "time_test": { Type: AttrTimestamp.String(), Value: time.Now().UTC().Format(time.RFC3339), @@ -718,7 +774,7 @@ func TestCoreDocument_UpdateAttributes_both(t *testing.T) { }, } - updates := map[string]*documentpb.Attribute{ + updates := map[string]attribute{ "time_test": { Type: AttrTimestamp.String(), Value: time.Now().Add(60 * time.Hour).UTC().Format(time.RFC3339), @@ -750,11 +806,8 @@ func TestCoreDocument_UpdateAttributes_both(t *testing.T) { }, } - oldAttrs, err := FromClientAttributes(oldCAttrs) - assert.NoError(t, err) - - newAttrs, err := FromClientAttributes(updates) - assert.NoError(t, err) + oldAttrs := toAttrsMap(t, oldCAttrs) + newAttrs := toAttrsMap(t, updates) newPattrs, err := toProtocolAttributes(newAttrs) assert.NoError(t, err) @@ -774,7 +827,7 @@ func TestCoreDocument_UpdateAttributes_both(t *testing.T) { } func TestCoreDocument_UpdateAttributes_old_nil(t *testing.T) { - updates := map[string]*documentpb.Attribute{ + updates := map[string]attribute{ "time_test": { Type: AttrTimestamp.String(), Value: time.Now().Add(60 * time.Hour).UTC().Format(time.RFC3339), @@ -806,9 +859,7 @@ func TestCoreDocument_UpdateAttributes_old_nil(t *testing.T) { }, } - newAttrs, err := FromClientAttributes(updates) - assert.NoError(t, err) - + newAttrs := toAttrsMap(t, updates) newPattrs, err := toProtocolAttributes(newAttrs) assert.NoError(t, err) @@ -820,7 +871,7 @@ func TestCoreDocument_UpdateAttributes_old_nil(t *testing.T) { } func TestCoreDocument_UpdateAttributes_updates_nil(t *testing.T) { - oldCAttrs := map[string]*documentpb.Attribute{ + oldCAttrs := map[string]attribute{ "time_test": { Type: AttrTimestamp.String(), Value: time.Now().UTC().Format(time.RFC3339), @@ -847,9 +898,7 @@ func TestCoreDocument_UpdateAttributes_updates_nil(t *testing.T) { }, } - oldAttrs, err := FromClientAttributes(oldCAttrs) - assert.NoError(t, err) - + oldAttrs := toAttrsMap(t, oldCAttrs) oldPattrs, err := toProtocolAttributes(oldAttrs) assert.NoError(t, err) @@ -866,3 +915,20 @@ func TestCoreDocument_UpdateAttributes_both_nil(t *testing.T) { assert.Len(t, upattrs, 0) assert.Len(t, uattrs, 0) } + +func TestCoreDocument_Status(t *testing.T) { + cd, err := NewCoreDocument(nil, CollaboratorsAccess{}, nil) + assert.NoError(t, err) + assert.Equal(t, cd.GetStatus(), Pending) + + // set status to Committed + err = cd.SetStatus(Committed) + assert.NoError(t, err) + assert.Equal(t, cd.GetStatus(), Committed) + + // try to update status to Committing + err = cd.SetStatus(Committing) + assert.Error(t, err) + assert.True(t, errors.IsOfType(ErrCDStatus, err)) + assert.Equal(t, cd.GetStatus(), Committed) +} diff --git a/documents/decimal.go b/documents/decimal.go index fcd984a8d..0cc5b8968 100644 --- a/documents/decimal.go +++ b/documents/decimal.go @@ -110,9 +110,9 @@ func (d *Decimal) Bytes() (decimal []byte, err error) { return nil, err } - // integer bytes should be atleast 8 byte, prepend Zeroes if not - if len(integer) < maxFractionByteLength { - integer = append(make([]byte, maxFractionByteLength-len(integer)), integer...) + // integer bytes should be atleast 32 byte, prepend Zeroes if len(integer)+sign(1) < maxDecimalByteLength + if len(integer) < maxDecimalByteLength { + integer = append(make([]byte, maxDecimalByteLength-(len(integer)+1)), integer...) } decimal = append(decimal, integer...) @@ -128,7 +128,7 @@ func (d *Decimal) Bytes() (decimal []byte, err error) { // SetBytes parse the bytes to Decimal. func (d *Decimal) SetBytes(dec []byte) error { - if len(dec) < minDecimalByteLength || len(dec) > maxDecimalByteLength { + if len(dec) != maxDecimalByteLength { return ErrInvalidDecimal } @@ -140,8 +140,8 @@ func (d *Decimal) SetBytes(dec []byte) error { s := i.String() // edge case for only fractions - // if dec is 8 bytes, then its just a fraction, so convert to string and prepend required zeroes - if len(dec) == maxFractionByteLength && decimalPrecision-len(s) > 0 { + // if s is less than 18 bytes, then its just a fraction, so convert to string and prepend required zeroes + if decimalPrecision-len(s) > 0 { s = strings.Repeat("0", decimalPrecision-len(s)) + s } diff --git a/documents/documents_test/anchor_test.go b/documents/documents_test/anchor_test.go index 8e24bddd7..6c4dcec50 100644 --- a/documents/documents_test/anchor_test.go +++ b/documents/documents_test/anchor_test.go @@ -12,7 +12,6 @@ import ( "github.com/centrifuge/go-centrifuge/documents" "github.com/centrifuge/go-centrifuge/identity" "github.com/centrifuge/go-centrifuge/testingutils/config" - "github.com/centrifuge/go-centrifuge/testingutils/documents" "github.com/centrifuge/go-centrifuge/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -84,7 +83,7 @@ func TestAnchorDocument(t *testing.T) { // prepare fails id := utils.RandomSlice(32) - m := &testingdocuments.MockModel{} + m := &documents.MockModel{} m.On("CurrentVersion").Return(id).Once() proc := &mockAnchorProcessor{} proc.On("PrepareForSignatureRequests", m).Return(errors.New("error")).Once() @@ -96,7 +95,7 @@ func TestAnchorDocument(t *testing.T) { assert.Contains(t, err.Error(), "failed to prepare document for signatures") // request signatures failed - m = &testingdocuments.MockModel{} + m = &documents.MockModel{} m.On("CurrentVersion").Return(id).Once() proc = &mockAnchorProcessor{} proc.On("PrepareForSignatureRequests", m).Return(nil).Once() @@ -110,7 +109,7 @@ func TestAnchorDocument(t *testing.T) { assert.Contains(t, err.Error(), "failed to collect signatures") // prepare for anchoring fails - m = &testingdocuments.MockModel{} + m = &documents.MockModel{} m.On("CurrentVersion").Return(id).Once() proc = &mockAnchorProcessor{} proc.On("PrepareForSignatureRequests", m).Return(nil).Once() @@ -125,7 +124,7 @@ func TestAnchorDocument(t *testing.T) { assert.Contains(t, err.Error(), "failed to prepare for anchoring") // anchor fails - m = &testingdocuments.MockModel{} + m = &documents.MockModel{} m.On("CurrentVersion").Return(id).Once() proc = &mockAnchorProcessor{} proc.On("PrepareForSignatureRequests", m).Return(nil).Once() @@ -141,8 +140,9 @@ func TestAnchorDocument(t *testing.T) { assert.Contains(t, err.Error(), "failed to anchor document") // send failed - m = &testingdocuments.MockModel{} + m = &documents.MockModel{} m.On("CurrentVersion").Return(id).Once() + m.On("SetStatus", documents.Committed).Return(nil) proc = &mockAnchorProcessor{} proc.On("PrepareForSignatureRequests", m).Return(nil).Once() proc.On("RequestSignatures", ctxh, m).Return(nil).Once() @@ -158,8 +158,9 @@ func TestAnchorDocument(t *testing.T) { assert.Contains(t, err.Error(), "failed to send anchored document") // success - m = &testingdocuments.MockModel{} + m = &documents.MockModel{} m.On("CurrentVersion").Return(id).Once() + m.On("SetStatus", documents.Committed).Return(nil) proc = &mockAnchorProcessor{} proc.On("PrepareForSignatureRequests", m).Return(nil).Once() proc.On("RequestSignatures", ctxh, m).Return(nil).Once() diff --git a/documents/documents_test/service_test.go b/documents/documents_test/service_test.go index 42937fb77..303ece460 100644 --- a/documents/documents_test/service_test.go +++ b/documents/documents_test/service_test.go @@ -58,7 +58,7 @@ func TestMain(m *testing.M) { } func TestService_ReceiveAnchoredDocument(t *testing.T) { - srv := documents.DefaultService(cfg, nil, nil, documents.NewServiceRegistry(), nil) + srv := documents.DefaultService(cfg, nil, nil, documents.NewServiceRegistry(), nil, nil, nil) // self failed err := srv.ReceiveAnchoredDocument(context.Background(), nil, did) @@ -89,7 +89,7 @@ func TestService_ReceiveAnchoredDocument(t *testing.T) { nextAid, err := anchors.ToAnchorID(doc.NextVersion()) ar.On("GetAnchorData", nextAid).Return(zeroRoot, time.Now(), errors.New("missing")) ar.On("GetAnchorData", mock.Anything).Return(dr, time.Now(), nil) - srv = documents.DefaultService(cfg, testRepo(), ar, documents.NewServiceRegistry(), idSrv) + srv = documents.DefaultService(cfg, testRepo(), ar, documents.NewServiceRegistry(), idSrv, nil, nil) err = srv.ReceiveAnchoredDocument(ctxh, doc, did) assert.Error(t, err) assert.True(t, errors.IsOfType(documents.ErrDocumentPersistence, err)) @@ -109,7 +109,7 @@ func TestService_ReceiveAnchoredDocument(t *testing.T) { assert.NoError(t, err) ar.On("GetAnchorData", nextAid).Return(zeroRoot, time.Now(), errors.New("missing")) ar.On("GetAnchorData", mock.Anything).Return(dr, time.Now(), nil) - srv = documents.DefaultService(cfg, testRepo(), ar, documents.NewServiceRegistry(), idSrv) + srv = documents.DefaultService(cfg, testRepo(), ar, documents.NewServiceRegistry(), idSrv, nil, nil) err = srv.ReceiveAnchoredDocument(ctxh, doc, did) assert.NoError(t, err) ar.AssertExpectations(t) @@ -150,7 +150,7 @@ func TestService_ReceiveAnchoredDocument(t *testing.T) { ar.On("GetAnchorData", nextAid).Return(zeroRoot, time.Now(), errors.New("missing")) ar.On("GetAnchorData", mock.Anything).Return(dr, time.Now(), nil) - srv = documents.DefaultService(cfg, testRepo(), ar, documents.NewServiceRegistry(), idSrv) + srv = documents.DefaultService(cfg, testRepo(), ar, documents.NewServiceRegistry(), idSrv, nil, nil) err = srv.ReceiveAnchoredDocument(ctxh, doc, id2) assert.NoError(t, err) ar.AssertExpectations(t) @@ -162,7 +162,7 @@ func getServiceWithMockedLayers() (documents.Service, testingcommons.MockIdentit idService := testingcommons.MockIdentityService{} idService.On("ValidateSignature", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once() mockAnchor = &mockAnchorRepo{} - return documents.DefaultService(cfg, repo, mockAnchor, documents.NewServiceRegistry(), &idService), idService + return documents.DefaultService(cfg, repo, mockAnchor, documents.NewServiceRegistry(), &idService, nil, nil), idService } type mockAnchorRepo struct { @@ -264,7 +264,7 @@ func TestService_RequestDocumentSignature(t *testing.T) { doc, _ := createCDWithEmbeddedInvoice(t, ctxh, []identity.DID{id}, false) idSrv := new(testingcommons.MockIdentityService) idSrv.On("ValidateSignature", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) - srv = documents.DefaultService(cfg, testRepo(), mockAnchor, documents.NewServiceRegistry(), idSrv) + srv = documents.DefaultService(cfg, testRepo(), mockAnchor, documents.NewServiceRegistry(), idSrv, nil, nil) // prepare a new version err = doc.AddNFT(true, testingidentity.GenerateRandomDID().ToAddress(), utils.RandomSlice(32)) @@ -384,7 +384,7 @@ func TestService_GetCurrentVersion_error(t *testing.T) { //document is not existing ctxh := testingconfig.CreateAccountContext(t, cfg) _, err := service.GetCurrentVersion(ctxh, documentIdentifier) - assert.True(t, errors.IsOfType(documents.ErrDocumentVersionNotFound, err)) + assert.True(t, errors.IsOfType(documents.ErrDocumentNotFound, err)) cd := coredocumentpb.CoreDocument{ DocumentIdentifier: documentIdentifier, @@ -463,7 +463,7 @@ func TestService_Exists(t *testing.T) { //document is not existing _, err := service.GetCurrentVersion(ctxh, documentIdentifier) - assert.True(t, errors.IsOfType(documents.ErrDocumentVersionNotFound, err)) + assert.True(t, errors.IsOfType(documents.ErrDocumentNotFound, err)) cd := coredocumentpb.CoreDocument{ DocumentIdentifier: documentIdentifier, @@ -496,7 +496,7 @@ func TestService_CreateModel(t *testing.T) { invSrv.On("CreateModel", mock.Anything, mock.Anything).Return(m, jobs.NewJobID(), nil).Once() err := reg.Register("invoice", invSrv) assert.NoError(t, err) - srv := documents.DefaultService(cfg, nil, nil, reg, nil) + srv := documents.DefaultService(cfg, nil, nil, reg, nil, nil, nil) // unknown scheme payload := documents.CreatePayload{Scheme: "invalid_scheme"} @@ -519,7 +519,7 @@ func TestService_UpdateModel(t *testing.T) { invSrv.On("UpdateModel", mock.Anything, mock.Anything).Return(m, jobs.NewJobID(), nil).Once() err := reg.Register("invoice", invSrv) assert.NoError(t, err) - srv := documents.DefaultService(cfg, nil, nil, reg, nil) + srv := documents.DefaultService(cfg, nil, nil, reg, nil, nil, nil) // unknown scheme payload := documents.UpdatePayload{CreatePayload: documents.CreatePayload{Scheme: "unknown_service"}} @@ -536,19 +536,8 @@ func TestService_UpdateModel(t *testing.T) { } func createCDWithEmbeddedInvoice(t *testing.T, ctx context.Context, collaborators []identity.DID, skipSave bool) (documents.Model, coredocumentpb.CoreDocument) { - i := new(invoice.Invoice) - data := testingdocuments.CreateInvoicePayload() - if len(collaborators) > 0 { - var cs []string - for _, c := range collaborators { - cs = append(cs, c.String()) - } - data.WriteAccess = cs - } - - err := i.InitInvoiceInput(data, did) - assert.NoError(t, err) - err = i.AddUpdateLog(did) + i, _ := invoice.CreateInvoiceWithEmbedCD(t, nil, did, collaborators) + err := i.AddUpdateLog(did) assert.NoError(t, err) _, err = i.CalculateDataRoot() assert.NoError(t, err) diff --git a/documents/entity/bootstrapper.go b/documents/entity/bootstrapper.go index 7e2443b67..3b2d9c5ed 100644 --- a/documents/entity/bootstrapper.go +++ b/documents/entity/bootstrapper.go @@ -4,7 +4,6 @@ import ( "github.com/centrifuge/centrifuge-protobufs/documenttypes" "github.com/centrifuge/go-centrifuge/anchors" "github.com/centrifuge/go-centrifuge/bootstrap" - "github.com/centrifuge/go-centrifuge/config" "github.com/centrifuge/go-centrifuge/documents" "github.com/centrifuge/go-centrifuge/documents/entityrelationship" "github.com/centrifuge/go-centrifuge/errors" @@ -14,9 +13,6 @@ import ( ) const ( - // BootstrappedEntityHandler maps to grpc handler for entities - BootstrappedEntityHandler string = "BootstrappedEntityHandler" - // BootstrappedEntityService maps to the service for entities BootstrappedEntityService string = "BootstrappedEntityService" ) @@ -52,11 +48,6 @@ func (Bootstrapper) Bootstrap(ctx map[string]interface{}) error { return errors.New("transaction service not initialised") } - cfgSrv, ok := ctx[config.BootstrappedConfigStorage].(config.Service) - if !ok { - return errors.New("config service not initialised") - } - factory, ok := ctx[identity.BootstrappedDIDFactory].(identity.Factory) if !ok { return errors.New("identity factory not initialised") @@ -86,7 +77,7 @@ func (Bootstrapper) Bootstrap(ctx map[string]interface{}) error { srv := DefaultService( docSrv, repo, - queueSrv, jobManager, factory, erService, didService, anchorRepo, processor, func() documents.ValidatorGroup { + queueSrv, jobManager, factory, erService, anchorRepo, processor, func() documents.ValidatorGroup { return documents.PostAnchoredValidator(didService, anchorRepo) }) @@ -95,13 +86,12 @@ func (Bootstrapper) Bootstrap(ctx map[string]interface{}) error { return errors.New("failed to register entity service: %v", err) } - err = registry.Register(scheme, srv) + err = registry.Register(Scheme, srv) if err != nil { return errors.New("failed to register entity service: %v", err) } ctx[BootstrappedEntityService] = srv - ctx[BootstrappedEntityHandler] = GRPCHandler(cfgSrv, srv) return nil } diff --git a/documents/entity/handler.go b/documents/entity/handler.go deleted file mode 100644 index 70ca5bddb..000000000 --- a/documents/entity/handler.go +++ /dev/null @@ -1,247 +0,0 @@ -package entity - -import ( - "github.com/centrifuge/go-centrifuge/centerrors" - "github.com/centrifuge/go-centrifuge/config" - "github.com/centrifuge/go-centrifuge/contextutil" - cliententitypb "github.com/centrifuge/go-centrifuge/protobufs/gen/go/entity" - "github.com/ethereum/go-ethereum/common/hexutil" - logging "github.com/ipfs/go-log" - "golang.org/x/net/context" -) - -var apiLog = logging.Logger("entity-api") - -// grpcHandler handles all the entity document related actions -// anchoring, sending, finding stored entity document -type grpcHandler struct { - service Service - config config.Service -} - -// GRPCHandler returns an implementation of entity.DocumentServiceServer -func GRPCHandler(config config.Service, srv Service) cliententitypb.EntityServiceServer { - return &grpcHandler{ - service: srv, - config: config, - } -} - -// Create handles the creation of the entities and anchoring the documents on chain -func (h *grpcHandler) Create(ctx context.Context, req *cliententitypb.EntityCreatePayload) (*cliententitypb.EntityResponse, error) { - apiLog.Debugf("Create request %v", req) - cctx, err := contextutil.Context(ctx, h.config) - if err != nil { - apiLog.Error(err) - return nil, err - } - - m, err := h.service.DeriveFromCreatePayload(cctx, req) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "could not derive create payload") - } - - // validate and persist - m, jobID, _, err := h.service.Create(cctx, m) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "could not create document") - } - - resp, err := h.service.DeriveEntityResponse(cctx, m) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "could not derive response") - } - - resp.Header.JobId = jobID.String() - return resp, nil -} - -// Update handles the document update and anchoring -func (h *grpcHandler) Update(ctx context.Context, payload *cliententitypb.EntityUpdatePayload) (*cliententitypb.EntityResponse, error) { - apiLog.Debugf("Update request %v", payload) - ctxHeader, err := contextutil.Context(ctx, h.config) - if err != nil { - apiLog.Error(err) - return nil, err - } - - doc, err := h.service.DeriveFromUpdatePayload(ctxHeader, payload) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "could not derive update payload") - } - - doc, jobID, _, err := h.service.Update(ctxHeader, doc) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "could not update document") - } - - resp, err := h.service.DeriveEntityResponse(ctxHeader, doc) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "could not derive response") - } - - resp.Header.JobId = jobID.String() - return resp, nil -} - -// GetVersion returns the requested version of the document -func (h *grpcHandler) GetVersion(ctx context.Context, getVersionRequest *cliententitypb.GetVersionRequest) (*cliententitypb.EntityResponse, error) { - apiLog.Debugf("Get version request %v", getVersionRequest) - ctxHeader, err := contextutil.Context(ctx, h.config) - if err != nil { - apiLog.Error(err) - return nil, err - } - - identifier, err := hexutil.Decode(getVersionRequest.Identifier) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "identifier is invalid") - } - - version, err := hexutil.Decode(getVersionRequest.Version) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "version is invalid") - } - - model, err := h.service.GetVersion(ctxHeader, identifier, version) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "document not found") - } - - resp, err := h.service.DeriveEntityResponse(ctxHeader, model) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "could not derive response") - } - - return resp, nil -} - -// Get returns the entity the latest version of the document with given identifier -func (h *grpcHandler) Get(ctx context.Context, getRequest *cliententitypb.GetRequest) (*cliententitypb.EntityResponse, error) { - apiLog.Debugf("Get request %v", getRequest) - ctxHeader, err := contextutil.Context(ctx, h.config) - if err != nil { - apiLog.Error(err) - return nil, err - } - - identifier, err := hexutil.Decode(getRequest.DocumentId) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "identifier is an invalid hex string") - } - - model, err := h.service.GetCurrentVersion(ctxHeader, identifier) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "document not found") - } - - resp, err := h.service.DeriveEntityResponse(ctxHeader, model) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "could not derive response") - } - - return resp, nil -} - -// GetEntityByRelationship returns the entity model from database or requests from granter -func (h *grpcHandler) GetEntityByRelationship(ctx context.Context, getRequest *cliententitypb.GetRequestRelationship) (*cliententitypb.EntityResponse, error) { - apiLog.Debugf("Get request %v", getRequest) - ctxHeader, err := contextutil.Context(ctx, h.config) - if err != nil { - apiLog.Error(err) - return nil, err - } - - relationshipIdentifier, err := hexutil.Decode(getRequest.RelationshipId) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "identifier is an invalid hex string") - } - - model, err := h.service.GetEntityByRelationship(ctxHeader, relationshipIdentifier) - if err != nil { - return nil, err - } - - resp, err := h.service.DeriveEntityResponse(ctxHeader, model) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "could not derive response") - } - - return resp, nil -} - -func (h *grpcHandler) Share(ctx context.Context, req *cliententitypb.RelationshipPayload) (*cliententitypb.RelationshipResponse, error) { - apiLog.Debugf("Share request %v", req) - cctx, err := contextutil.Context(ctx, h.config) - if err != nil { - apiLog.Error(err) - return nil, err - } - - m, err := h.service.DeriveFromSharePayload(cctx, req) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "could not derive share payload") - } - - // validate and persist - m, jobID, _, err := h.service.Share(cctx, m) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "could not create document") - } - - resp, err := h.service.DeriveEntityRelationshipResponse(m) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "could not derive response") - } - - resp.Header.JobId = jobID.String() - return resp, nil -} - -func (h *grpcHandler) Revoke(ctx context.Context, payload *cliententitypb.RelationshipPayload) (*cliententitypb.RelationshipResponse, error) { - apiLog.Debugf("Revoke request %v", payload) - ctxHeader, err := contextutil.Context(ctx, h.config) - if err != nil { - apiLog.Error(err) - return nil, err - } - - m, err := h.service.DeriveFromRevokePayload(ctxHeader, payload) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "could not derive revoke payload") - } - - m, jobID, _, err := h.service.Revoke(ctxHeader, m) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "could not update document") - } - - resp, err := h.service.DeriveEntityRelationshipResponse(m) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "could not derive response") - } - - resp.Header.JobId = jobID.String() - return resp, nil -} diff --git a/documents/entity/handler_test.go b/documents/entity/handler_test.go deleted file mode 100644 index 73293cd94..000000000 --- a/documents/entity/handler_test.go +++ /dev/null @@ -1,338 +0,0 @@ -// +build unit - -package entity - -import ( - "context" - "testing" - - "github.com/centrifuge/go-centrifuge/contextutil" - "github.com/centrifuge/go-centrifuge/documents" - "github.com/centrifuge/go-centrifuge/errors" - "github.com/centrifuge/go-centrifuge/jobs" - "github.com/centrifuge/go-centrifuge/protobufs/gen/go/document" - cliententitypb "github.com/centrifuge/go-centrifuge/protobufs/gen/go/entity" - "github.com/centrifuge/go-centrifuge/testingutils/config" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - -type mockService struct { - Service - mock.Mock -} - -func (m *mockService) DeriveFromCreatePayload(ctx context.Context, payload *cliententitypb.EntityCreatePayload) (documents.Model, error) { - args := m.Called(ctx, payload) - model, _ := args.Get(0).(documents.Model) - return model, args.Error(1) -} - -func (m *mockService) DeriveFromSharePayload(ctx context.Context, payload *cliententitypb.RelationshipPayload) (documents.Model, error) { - args := m.Called(ctx, payload) - model, _ := args.Get(0).(documents.Model) - return model, args.Error(1) -} - -func (m *mockService) Create(ctx context.Context, model documents.Model) (documents.Model, jobs.JobID, chan bool, error) { - args := m.Called(ctx, model) - model, _ = args.Get(0).(documents.Model) - return model, contextutil.Job(ctx), nil, args.Error(2) -} - -func (m *mockService) Share(ctx context.Context, model documents.Model) (documents.Model, jobs.JobID, chan bool, error) { - args := m.Called(ctx, model) - model, _ = args.Get(0).(documents.Model) - return model, contextutil.Job(ctx), nil, args.Error(2) -} - -func (m *mockService) GetCurrentVersion(ctx context.Context, documentID []byte) (documents.Model, error) { - args := m.Called(ctx, documentID) - data, _ := args.Get(0).(documents.Model) - return data, args.Error(1) -} - -func (m *mockService) GetVersion(ctx context.Context, documentID []byte, version []byte) (documents.Model, error) { - args := m.Called(ctx, documentID, version) - data, _ := args.Get(0).(documents.Model) - return data, args.Error(1) -} - -func (m *mockService) DeriveEntityData(doc documents.Model) (*cliententitypb.EntityData, error) { - args := m.Called(doc) - data, _ := args.Get(0).(*cliententitypb.EntityData) - return data, args.Error(1) -} - -func (m *mockService) DeriveEntityResponse(ctx context.Context, doc documents.Model) (*cliententitypb.EntityResponse, error) { - args := m.Called(ctx, doc) - data, _ := args.Get(0).(*cliententitypb.EntityResponse) - return data, args.Error(1) -} - -func (m *mockService) DeriveEntityRelationshipResponse(doc documents.Model) (*cliententitypb.RelationshipResponse, error) { - args := m.Called(doc) - data, _ := args.Get(0).(*cliententitypb.RelationshipResponse) - return data, args.Error(1) -} - -func (m *mockService) Update(ctx context.Context, model documents.Model) (documents.Model, jobs.JobID, chan bool, error) { - args := m.Called(ctx, model) - doc1, _ := args.Get(0).(documents.Model) - return doc1, contextutil.Job(ctx), nil, args.Error(2) -} - -func (m *mockService) Revoke(ctx context.Context, model documents.Model) (documents.Model, jobs.JobID, chan bool, error) { - args := m.Called(ctx, model) - doc1, _ := args.Get(0).(documents.Model) - return doc1, contextutil.Job(ctx), nil, args.Error(2) -} - -func (m *mockService) DeriveFromUpdatePayload(ctx context.Context, payload *cliententitypb.EntityUpdatePayload) (documents.Model, error) { - args := m.Called(ctx, payload) - doc, _ := args.Get(0).(documents.Model) - return doc, args.Error(1) -} - -func (m *mockService) DeriveFromRevokePayload(ctx context.Context, payload *cliententitypb.RelationshipPayload) (documents.Model, error) { - args := m.Called(ctx, payload) - doc, _ := args.Get(0).(documents.Model) - return doc, args.Error(1) -} - -func getHandler() *grpcHandler { - return &grpcHandler{service: &mockService{}, config: configService} -} - -func TestGRPCHandler_Create_derive_fail(t *testing.T) { - // DeriveFrom payload fails - h := getHandler() - srv := h.service.(*mockService) - srv.On("DeriveFromCreatePayload", mock.Anything, mock.Anything).Return(nil, errors.New("derive failed")).Once() - _, err := h.Create(testingconfig.HandlerContext(configService), nil) - srv.AssertExpectations(t) - assert.Error(t, err, "must be non nil") - assert.Contains(t, err.Error(), "derive failed") -} - -func TestGRPCHandler_Create_create_fail(t *testing.T) { - h := getHandler() - srv := h.service.(*mockService) - srv.On("DeriveFromCreatePayload", mock.Anything, mock.Anything).Return(new(Entity), nil).Once() - srv.On("Create", mock.Anything, mock.Anything).Return(nil, jobs.NilJobID().String(), errors.New("create failed")).Once() - payload := &cliententitypb.EntityCreatePayload{Data: &cliententitypb.EntityData{LegalName: "test company"}} - _, err := h.Create(testingconfig.HandlerContext(configService), payload) - srv.AssertExpectations(t) - assert.Error(t, err, "must be non nil") - assert.Contains(t, err.Error(), "create failed") -} - -func TestGRPCHandler_Create_DeriveEntityResponse_fail(t *testing.T) { - h := getHandler() - srv := h.service.(*mockService) - model := new(Entity) - srv.On("DeriveFromCreatePayload", mock.Anything, mock.Anything).Return(model, nil).Once() - srv.On("Create", mock.Anything, mock.Anything).Return(model, jobs.NilJobID().String(), nil).Once() - srv.On("DeriveEntityResponse", mock.Anything, mock.Anything).Return(nil, errors.New("derive response failed")) - payload := &cliententitypb.EntityCreatePayload{Data: &cliententitypb.EntityData{LegalName: "test company"}} - _, err := h.Create(testingconfig.HandlerContext(configService), payload) - srv.AssertExpectations(t) - assert.Error(t, err, "must be non nil") - assert.Contains(t, err.Error(), "derive response failed") -} - -func TestGrpcHandler_Create(t *testing.T) { - h := getHandler() - srv := h.service.(*mockService) - model := new(Entity) - jobID := jobs.NewJobID() - payload := &cliententitypb.EntityCreatePayload{ - Data: &cliententitypb.EntityData{LegalName: "test company"}, - WriteAccess: []string{"0x010203040506"}, - } - response := &cliententitypb.EntityResponse{Header: &documentpb.ResponseHeader{}} - srv.On("DeriveFromCreatePayload", mock.Anything, mock.Anything).Return(model, nil).Once() - srv.On("Create", mock.Anything, mock.Anything).Return(model, jobID.String(), nil).Once() - srv.On("DeriveEntityResponse", mock.Anything, model).Return(response, nil) - res, err := h.Create(testingconfig.HandlerContext(configService), payload) - srv.AssertExpectations(t) - assert.Nil(t, err, "must be nil") - assert.NotNil(t, res, "must be non nil") - assert.Equal(t, res, response) -} - -func TestGrpcHandler_Share(t *testing.T) { - h := getHandler() - srv := h.service.(*mockService) - model := &mockModel{} - jobID := jobs.NewJobID() - payload := &cliententitypb.RelationshipPayload{ - DocumentId: "0x010203", - TargetIdentity: "some DID", - } - response := &cliententitypb.RelationshipResponse{Header: &documentpb.ResponseHeader{}} - srv.On("DeriveFromSharePayload", mock.Anything, mock.Anything).Return(model, nil).Once() - srv.On("Share", mock.Anything, mock.Anything).Return(model, jobID.String(), nil).Once() - srv.On("DeriveEntityRelationshipResponse", model).Return(response, nil) - res, err := h.Share(testingconfig.HandlerContext(configService), payload) - srv.AssertExpectations(t) - assert.Nil(t, err, "must be nil") - assert.NotNil(t, res, "must be non nil") - assert.Equal(t, res, response) -} - -func TestGrpcHandler_Get_invalid_input(t *testing.T) { - identifier := "0x01010101" - identifierBytes, err := hexutil.Decode(identifier) - assert.NoError(t, err) - h := getHandler() - srv := h.service.(*mockService) - payload := &cliententitypb.GetRequest{DocumentId: "invalid"} - - res, err := h.Get(testingconfig.HandlerContext(configService), payload) - assert.Nil(t, res) - assert.EqualError(t, err, "identifier is an invalid hex string: hex string without 0x prefix") - - payload.DocumentId = identifier - srv.On("GetCurrentVersion", mock.Anything, identifierBytes).Return(nil, errors.New("not found")) - res, err = h.Get(testingconfig.HandlerContext(configService), payload) - srv.AssertExpectations(t) - assert.Nil(t, res) - assert.EqualError(t, err, "document not found: not found") -} - -func TestGrpcHandler_Get(t *testing.T) { - identifier := "0x01010101" - identifierBytes, err := hexutil.Decode(identifier) - assert.NoError(t, err) - h := getHandler() - srv := h.service.(*mockService) - model := new(mockModel) - payload := &cliententitypb.GetRequest{DocumentId: identifier} - response := &cliententitypb.EntityResponse{} - srv.On("GetCurrentVersion", mock.Anything, identifierBytes).Return(model, nil) - srv.On("DeriveEntityResponse", mock.Anything, model).Return(response, nil) - res, err := h.Get(testingconfig.HandlerContext(configService), payload) - model.AssertExpectations(t) - srv.AssertExpectations(t) - assert.Nil(t, err, "must be nil") - assert.NotNil(t, res, "must be non nil") - assert.Equal(t, res, response) -} - -func TestGrpcHandler_GetVersion_invalid_input(t *testing.T) { - h := getHandler() - srv := h.service.(*mockService) - payload := &cliententitypb.GetVersionRequest{Identifier: "0x0x", Version: "0x00"} - res, err := h.GetVersion(testingconfig.HandlerContext(configService), payload) - assert.EqualError(t, err, "identifier is invalid: invalid hex string") - payload.Version = "0x0x" - payload.Identifier = "0x01" - - res, err = h.GetVersion(testingconfig.HandlerContext(configService), payload) - assert.EqualError(t, err, "version is invalid: invalid hex string") - payload.Version = "0x00" - payload.Identifier = "0x01" - - mockErr := errors.New("not found") - srv.On("GetVersion", mock.Anything, []byte{0x01}, []byte{0x00}).Return(nil, mockErr) - res, err = h.GetVersion(testingconfig.HandlerContext(configService), payload) - srv.AssertExpectations(t) - assert.EqualError(t, err, "document not found: not found") - assert.Nil(t, res) -} - -func TestGrpcHandler_GetVersion(t *testing.T) { - h := getHandler() - srv := h.service.(*mockService) - model := new(mockModel) - payload := &cliententitypb.GetVersionRequest{Identifier: "0x01", Version: "0x00"} - - response := &cliententitypb.EntityResponse{} - srv.On("GetVersion", mock.Anything, []byte{0x01}, []byte{0x00}).Return(model, nil) - srv.On("DeriveEntityResponse", mock.Anything, model).Return(response, nil) - res, err := h.GetVersion(testingconfig.HandlerContext(configService), payload) - model.AssertExpectations(t) - srv.AssertExpectations(t) - assert.Nil(t, err) - assert.NotNil(t, res) - assert.Equal(t, res, response) -} - -func TestGrpcHandler_Update_derive_fail(t *testing.T) { - h := getHandler() - srv := h.service.(*mockService) - payload := &cliententitypb.EntityUpdatePayload{DocumentId: "0x010201"} - srv.On("DeriveFromUpdatePayload", mock.Anything, payload).Return(nil, errors.New("derive error")).Once() - res, err := h.Update(testingconfig.HandlerContext(configService), payload) - srv.AssertExpectations(t) - assert.Error(t, err) - assert.Contains(t, err.Error(), "derive error") - assert.Nil(t, res) -} - -func TestGrpcHandler_Update_update_fail(t *testing.T) { - h := getHandler() - srv := h.service.(*mockService) - model := new(mockModel) - ctx := testingconfig.HandlerContext(configService) - payload := &cliententitypb.EntityUpdatePayload{DocumentId: "0x010201"} - srv.On("DeriveFromUpdatePayload", mock.Anything, payload).Return(model, nil).Once() - srv.On("Update", mock.Anything, model).Return(nil, jobs.NilJobID().String(), errors.New("update error")).Once() - res, err := h.Update(ctx, payload) - srv.AssertExpectations(t) - assert.Error(t, err) - assert.Contains(t, err.Error(), "update error") - assert.Nil(t, res) -} - -func TestGrpcHandler_Update_derive_response_fail(t *testing.T) { - h := getHandler() - srv := h.service.(*mockService) - model := new(mockModel) - ctx := testingconfig.HandlerContext(configService) - payload := &cliententitypb.EntityUpdatePayload{DocumentId: "0x010201"} - srv.On("DeriveFromUpdatePayload", mock.Anything, payload).Return(model, nil).Once() - srv.On("Update", mock.Anything, model).Return(model, jobs.NilJobID().String(), nil).Once() - srv.On("DeriveEntityResponse", mock.Anything, model).Return(nil, errors.New("derive response error")).Once() - res, err := h.Update(ctx, payload) - srv.AssertExpectations(t) - assert.Error(t, err) - assert.Contains(t, err.Error(), "derive response error") - assert.Nil(t, res) -} - -func TestGrpcHandler_Update(t *testing.T) { - h := getHandler() - srv := h.service.(*mockService) - model := new(mockModel) - ctx := testingconfig.HandlerContext(configService) - jobID := jobs.NewJobID() - payload := &cliententitypb.EntityUpdatePayload{DocumentId: "0x010201"} - resp := &cliententitypb.EntityResponse{Header: new(documentpb.ResponseHeader)} - srv.On("DeriveFromUpdatePayload", mock.Anything, payload).Return(model, nil).Once() - srv.On("Update", mock.Anything, model).Return(model, jobID.String(), nil).Once() - srv.On("DeriveEntityResponse", mock.Anything, model).Return(resp, nil).Once() - res, err := h.Update(ctx, payload) - srv.AssertExpectations(t) - assert.Nil(t, err) - assert.Equal(t, resp, res) -} - -func TestGrpcHandler_Revoke(t *testing.T) { - h := getHandler() - srv := h.service.(*mockService) - model := new(mockModel) - ctx := testingconfig.HandlerContext(configService) - jobID := jobs.NewJobID() - payload := &cliententitypb.RelationshipPayload{DocumentId: "0x010201", TargetIdentity: "some DID"} - resp := &cliententitypb.RelationshipResponse{Header: new(documentpb.ResponseHeader)} - srv.On("DeriveFromRevokePayload", mock.Anything, payload).Return(model, nil).Once() - srv.On("Revoke", mock.Anything, model).Return(model, jobID.String(), nil).Once() - srv.On("DeriveEntityRelationshipResponse", model).Return(resp, nil).Once() - res, err := h.Revoke(ctx, payload) - srv.AssertExpectations(t) - assert.Nil(t, err) - assert.Equal(t, resp, res) -} diff --git a/documents/entity/model.go b/documents/entity/model.go index fe85fd5ed..820257137 100644 --- a/documents/entity/model.go +++ b/documents/entity/model.go @@ -1,9 +1,9 @@ package entity import ( + "context" "encoding/json" "reflect" - "strings" "github.com/centrifuge/centrifuge-protobufs/documenttypes" "github.com/centrifuge/centrifuge-protobufs/gen/go/coredocument" @@ -11,18 +11,20 @@ import ( "github.com/centrifuge/go-centrifuge/documents" "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/go-centrifuge/identity" - cliententitypb "github.com/centrifuge/go-centrifuge/protobufs/gen/go/entity" "github.com/centrifuge/go-centrifuge/utils/byteutils" "github.com/centrifuge/precise-proofs/proofs" "github.com/centrifuge/precise-proofs/proofs/proto" "github.com/ethereum/go-ethereum/common" "github.com/golang/protobuf/proto" "github.com/golang/protobuf/ptypes/any" + "github.com/jinzhu/copier" ) const ( prefix string = "entity" - scheme = prefix + + // Scheme is entity scheme. + Scheme = prefix // ErrMultiplePaymentMethodsSet is a sentinel error when multiple payment methods are set in a single payment detail. ErrMultiplePaymentMethodsSet = errors.Error("multiple payment methods are set") @@ -54,7 +56,7 @@ type Address struct { // BankPaymentMethod holds the bank details of the entity. type BankPaymentMethod struct { - Identifier byteutils.HexBytes `json:"identifier"` + Identifier byteutils.HexBytes `json:"identifier" swaggertype:"primitive,string"` Address Address `json:"address"` HolderName string `json:"holder_name"` BankKey string `json:"bank_key"` @@ -64,7 +66,7 @@ type BankPaymentMethod struct { // CryptoPaymentMethod holds the crypto details of the entity. type CryptoPaymentMethod struct { - Identifier byteutils.HexBytes `json:"identifier"` + Identifier byteutils.HexBytes `json:"identifier" swaggertype:"primitive,string"` To string `json:"to"` ChainURI string `json:"chain_uri"` SupportedCurrency string `json:"supported_currency"` @@ -72,7 +74,7 @@ type CryptoPaymentMethod struct { // OtherPaymentMethod represents any other payment methods entity accepts. type OtherPaymentMethod struct { - Identifier byteutils.HexBytes `json:"identifier"` + Identifier byteutils.HexBytes `json:"identifier" swaggertype:"primitive,string"` Type string `json:"type"` PayTo string `json:"pay_to"` SupportedCurrency string `json:"supported_currency"` @@ -98,7 +100,7 @@ type Contact struct { // Data represents the entity data. type Data struct { - Identity *identity.DID `json:"identity"` + Identity *identity.DID `json:"identity" swaggertype:"primitive,string"` LegalName string `json:"legal_name"` Addresses []Address `json:"addresses"` PaymentDetails []PaymentDetail `json:"payment_details"` @@ -112,19 +114,6 @@ type Entity struct { Data Data } -// getClientData returns the client data from the entity model -func (e *Entity) getClientData() *cliententitypb.EntityData { - d := e.Data - dids := identity.DIDsToStrings(d.Identity) - return &cliententitypb.EntityData{ - Identity: dids[0], - LegalName: d.LegalName, - Addresses: toProtoAddresses(d.Addresses), - PaymentDetails: toProtoPaymentDetails(d.PaymentDetails), - Contacts: toProtoContacts(d.Contacts), - } -} - // createP2PProtobuf returns centrifuge protobuf specific entityData func (e *Entity) createP2PProtobuf() *entitypb.Entity { d := e.Data @@ -138,55 +127,6 @@ func (e *Entity) createP2PProtobuf() *entitypb.Entity { } } -// InitEntityInput initialize the model based on the received parameters from the rest api call -func (e *Entity) InitEntityInput(payload *cliententitypb.EntityCreatePayload, self identity.DID) error { - err := e.initEntityFromData(payload.Data) - if err != nil { - return err - } - - ca, err := documents.FromClientCollaboratorAccess(payload.ReadAccess, payload.WriteAccess) - if err != nil { - return errors.New("failed to decode collaborator: %v", err) - } - - ca.ReadWriteCollaborators = append(ca.ReadWriteCollaborators, self) - attrs, err := documents.FromClientAttributes(payload.Attributes) - if err != nil { - return err - } - - cd, err := documents.NewCoreDocument(compactPrefix(), ca, attrs) - if err != nil { - return errors.New("failed to init core document: %v", err) - } - - e.CoreDocument = cd - return nil -} - -// initEntityFromData initialises entity from entityData -func (e *Entity) initEntityFromData(data *cliententitypb.EntityData) error { - data.Identity = strings.TrimSpace(data.Identity) - if data.Identity == "" { - return identity.ErrMalformedAddress - } - - dids, err := identity.StringsToDIDs(data.Identity) - if err != nil { - return errors.NewTypedError(identity.ErrMalformedAddress, err) - } - - var d Data - d.Identity = dids[0] - d.LegalName = data.LegalName - d.Addresses = fromProtoAddresses(data.Addresses) - d.PaymentDetails = fromProtoPaymentDetails(data.PaymentDetails) - d.Contacts = fromProtoContacts(data.Contacts) - e.Data = d - return nil -} - // loadFromP2PProtobuf loads the entity from centrifuge protobuf entity data func (e *Entity) loadFromP2PProtobuf(data *entitypb.Entity) error { dids, err := identity.BytesToDIDs(data.Identity) @@ -278,7 +218,11 @@ func (e *Entity) getDocumentDataTree() (tree *proofs.DocumentTree, err error) { if e.CoreDocument == nil { return nil, errors.New("getDocumentDataTree error CoreDocument not set") } - t := e.CoreDocument.DefaultTreeWithPrefix(prefix, compactPrefix()) + t, err := e.CoreDocument.DefaultTreeWithPrefix(prefix, compactPrefix()) + if err != nil { + return nil, err + } + err = t.AddLeavesFromDocument(eProto) if err != nil { return nil, errors.New("getDocumentDataTree error %v", err) @@ -324,22 +268,6 @@ func (*Entity) DocumentType() string { return documenttypes.EntityDataTypeUrl } -// PrepareNewVersion prepares new version from the old entity. -func (e *Entity) PrepareNewVersion(old documents.Model, data *cliententitypb.EntityData, collaborators documents.CollaboratorsAccess, attrs map[documents.AttrKey]documents.Attribute) error { - err := e.initEntityFromData(data) - if err != nil { - return err - } - - oldCD := old.(*Entity).CoreDocument - e.CoreDocument, err = oldCD.PrepareNewVersion(compactPrefix(), collaborators, attrs) - if err != nil { - return err - } - - return nil -} - // AddNFT adds NFT to the Entity. func (e *Entity) AddNFT(grantReadAccess bool, registry common.Address, tokenID []byte) error { cd, err := e.CoreDocument.AddNFT(grantReadAccess, registry, tokenID) @@ -459,9 +387,8 @@ func isOnlyOneSet(methods ...interface{}) error { // loadData unmarshals json blob to Data. // Only one of the payment method has to be set. // errors out if multiple payment methods are set or none is set. -func (e *Entity) loadData(data []byte) error { - var d Data - err := json.Unmarshal(data, &d) +func loadData(data []byte, d *Data) error { + err := json.Unmarshal(data, d) if err != nil { return err } @@ -474,29 +401,30 @@ func (e *Entity) loadData(data []byte) error { } } - e.Data = d return nil } -// unpackFromCreatePayload unpacks the entity data from the Payload. -func (e *Entity) unpackFromCreatePayload(did identity.DID, payload documents.CreatePayload) error { - if err := e.loadData(payload.Data); err != nil { +// DeriveFromCreatePayload unpacks the entity data from the Payload. +func (e *Entity) DeriveFromCreatePayload(_ context.Context, payload documents.CreatePayload) error { + var d Data + if err := loadData(payload.Data, &d); err != nil { return errors.NewTypedError(ErrEntityInvalidData, err) } - payload.Collaborators.ReadWriteCollaborators = append(payload.Collaborators.ReadWriteCollaborators, did) cd, err := documents.NewCoreDocument(compactPrefix(), payload.Collaborators, payload.Attributes) if err != nil { return errors.NewTypedError(documents.ErrCDCreate, err) } + e.Data = d e.CoreDocument = cd return nil } // unpackFromUpdatePayload unpacks the update payload and prepares a new version. func (e *Entity) unpackFromUpdatePayload(old *Entity, payload documents.UpdatePayload) error { - if err := e.loadData(payload.Data); err != nil { + var d Data + if err := loadData(payload.Data, &d); err != nil { return errors.NewTypedError(ErrEntityInvalidData, err) } @@ -505,11 +433,61 @@ func (e *Entity) unpackFromUpdatePayload(old *Entity, payload documents.UpdatePa return err } + e.Data = d + e.CoreDocument = ncd + return nil +} + +// DeriveFromUpdatePayload unpacks the update payload and prepares a new version. +func (e *Entity) DeriveFromUpdatePayload(_ context.Context, payload documents.UpdatePayload) (documents.Model, error) { + d, err := e.patch(payload) + if err != nil { + return nil, err + } + + ncd, err := e.CoreDocument.PrepareNewVersion(compactPrefix(), payload.Collaborators, payload.Attributes) + if err != nil { + return nil, err + } + + return &Entity{ + Data: d, + CoreDocument: ncd, + }, nil +} + +func (e *Entity) patch(payload documents.UpdatePayload) (Data, error) { + var d Data + err := copier.Copy(&d, &e.Data) + if err != nil { + return d, err + } + + if err := loadData(payload.Data, &d); err != nil { + return d, errors.NewTypedError(ErrEntityInvalidData, err) + } + + return d, nil +} + +// Patch merges payload data into model +func (e *Entity) Patch(payload documents.UpdatePayload) error { + d, err := e.patch(payload) + if err != nil { + return err + } + + ncd, err := e.CoreDocument.Patch(compactPrefix(), payload.Collaborators, payload.Attributes) + if err != nil { + return err + } + + e.Data = d e.CoreDocument = ncd return nil } // Scheme returns the entity scheme. func (e *Entity) Scheme() string { - return scheme + return Scheme } diff --git a/documents/entity/model_test.go b/documents/entity/model_test.go index bb2e2a1db..6856bb9b2 100644 --- a/documents/entity/model_test.go +++ b/documents/entity/model_test.go @@ -3,6 +3,8 @@ package entity import ( + "context" + "crypto/sha256" "encoding/json" "fmt" "os" @@ -11,7 +13,6 @@ import ( "github.com/centrifuge/centrifuge-protobufs/documenttypes" "github.com/centrifuge/centrifuge-protobufs/gen/go/coredocument" - "github.com/centrifuge/centrifuge-protobufs/gen/go/entity" "github.com/centrifuge/go-centrifuge/anchors" "github.com/centrifuge/go-centrifuge/bootstrap" "github.com/centrifuge/go-centrifuge/bootstrap/bootstrappers/testlogging" @@ -25,7 +26,6 @@ import ( "github.com/centrifuge/go-centrifuge/identity/ideth" "github.com/centrifuge/go-centrifuge/jobs" "github.com/centrifuge/go-centrifuge/p2p" - cliententitypb "github.com/centrifuge/go-centrifuge/protobufs/gen/go/entity" "github.com/centrifuge/go-centrifuge/queue" "github.com/centrifuge/go-centrifuge/storage/leveldb" "github.com/centrifuge/go-centrifuge/testingutils/config" @@ -41,7 +41,6 @@ import ( var ctx = map[string]interface{}{} var cfg config.Configuration -var configService config.Service var ( did = testingidentity.GenerateRandomDID() @@ -73,7 +72,7 @@ func TestMain(m *testing.M) { ctx[ethereum.BootstrappedEthereumClient] = ethClient jobMan := &testingjobs.MockJobManager{} ctx[jobs.BootstrappedService] = jobMan - done := make(chan bool) + done := make(chan error) jobMan.On("ExecuteWithinJob", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(jobs.NilJobID(), done, nil) ctx[bootstrap.BootstrappedInvoiceUnpaid] = new(testingdocuments.MockRegistry) ibootstrappers := []bootstrap.TestBootstrapper{ @@ -92,7 +91,6 @@ func TestMain(m *testing.M) { bootstrap.RunTestBootstrappers(ibootstrappers, ctx) cfg = ctx[bootstrap.BootstrappedConfig].(config.Configuration) cfg.Set("identityId", did.String()) - configService = ctx[config.BootstrappedConfigStorage].(config.Service) result := m.Run() bootstrap.RunTestTeardown(ibootstrappers) os.Exit(result) @@ -103,21 +101,17 @@ func TestEntity_PackCoreDocument(t *testing.T) { did, err := contextutil.AccountDID(ctx) assert.NoError(t, err) - entity := new(Entity) - assert.NoError(t, entity.InitEntityInput(testingdocuments.CreateEntityPayload(), did)) - + entity, _ := CreateEntityWithEmbedCD(t, ctx, did, nil) cd, err := entity.PackCoreDocument() assert.NoError(t, err) assert.NotNil(t, cd.EmbeddedData) } func TestEntity_JSON(t *testing.T) { - entity := new(Entity) ctx := testingconfig.CreateAccountContext(t, cfg) did, err := contextutil.AccountDID(ctx) assert.NoError(t, err) - assert.NoError(t, entity.InitEntityInput(testingdocuments.CreateEntityPayload(), did)) - + entity, _ := CreateEntityWithEmbedCD(t, ctx, did, nil) cd, err := entity.PackCoreDocument() assert.NoError(t, err) jsonBytes, err := entity.JSON() @@ -155,99 +149,47 @@ func TestEntityModel_UnpackCoreDocument(t *testing.T) { assert.Error(t, err) // successful - entity, cd := createCDWithEmbeddedEntity(t) + entity, cd := CreateEntityWithEmbedCD(t, testingconfig.CreateAccountContext(t, cfg), did, nil) err = model.UnpackCoreDocument(cd) assert.NoError(t, err) - d := model.getClientData() - d1 := entity.(*Entity).getClientData() + d := model.Data + d1 := entity.Data assert.Equal(t, d.Addresses[0], d1.Addresses[0]) assert.Equal(t, model.ID(), entity.ID()) assert.Equal(t, model.CurrentVersion(), entity.CurrentVersion()) assert.Equal(t, model.PreviousVersion(), entity.PreviousVersion()) } -func TestEntityModel_getClientData(t *testing.T) { - entityData := testingdocuments.CreateEntityData() - entity := new(Entity) - entity.CoreDocument = new(documents.CoreDocument) - err := entity.loadFromP2PProtobuf(&entityData) - assert.NoError(t, err) - - data := entity.getClientData() - assert.NotNil(t, data, "entity data should not be nil") - assert.Equal(t, data.Addresses, entityData.Addresses, "addresses should match") - assert.Equal(t, data.Contacts, entityData.Contacts, "contacts should match") - assert.Equal(t, data.LegalName, entityData.LegalName, "legal name should match") -} - -func TestEntityModel_InitEntityInput(t *testing.T) { - ctx := testingconfig.CreateAccountContext(t, cfg) - did, err := contextutil.AccountDID(ctx) - assert.NoError(t, err) - - // fail recipient - data := &cliententitypb.EntityData{ - Identity: testingidentity.GenerateRandomDID().ToAddress().String(), - LegalName: "Company Test", - Contacts: []*entitypb.Contact{{Name: "Satoshi Nakamoto"}}, - Addresses: []*entitypb.Address{{IsMain: true, - AddressLine1: "Sample Street 1", - Zip: "12345", - State: "Germany", - }, {IsMain: false, State: "US"}}, - } - e := new(Entity) - err = e.InitEntityInput(&cliententitypb.EntityCreatePayload{Data: data}, did) - assert.Nil(t, err, "should be successful") - - e = new(Entity) - collabs := []string{"0x010102040506", "some id"} - err = e.InitEntityInput(&cliententitypb.EntityCreatePayload{Data: data, WriteAccess: collabs}, did) - assert.Error(t, err) - assert.Contains(t, err.Error(), "failed to decode collaborator") - - collab1, err := identity.NewDIDFromString("0xBAEb33a61f05e6F269f1c4b4CFF91A901B54DaF7") - assert.NoError(t, err) - collab2, err := identity.NewDIDFromString("0xBAEb33a61f05e6F269f1c4b4CFF91A901B54DaF3") - assert.NoError(t, err) - collabs = []string{collab1.String(), collab2.String()} - err = e.InitEntityInput(&cliententitypb.EntityCreatePayload{Data: data, WriteAccess: collabs}, did) - assert.Nil(t, err, "must be nil") - -} - func TestEntityModel_calculateDataRoot(t *testing.T) { ctx := testingconfig.CreateAccountContext(t, cfg) did, err := contextutil.AccountDID(ctx) assert.NoError(t, err) - m := new(Entity) - err = m.InitEntityInput(testingdocuments.CreateEntityPayload(), did) - assert.Nil(t, err, "Init must pass") - m.GetTestCoreDocWithReset() + entity, _ := CreateEntityWithEmbedCD(t, ctx, did, nil) - dr, err := m.CalculateDataRoot() + dr, err := entity.CalculateDataRoot() assert.Nil(t, err, "calculate must pass") assert.False(t, utils.IsEmptyByteSlice(dr)) } func TestEntity_CreateProofs(t *testing.T) { - e := createEntity(t) + ctx := testingconfig.CreateAccountContext(t, cfg) + e, _ := CreateEntityWithEmbedCD(t, ctx, did, nil) rk := e.Document.Roles[0].RoleKey pf := fmt.Sprintf(documents.CDTreePrefix+".roles[%s].collaborators[0]", hexutil.Encode(rk)) proof, err := e.CreateProofs([]string{"entity.legal_name", pf, documents.CDTreePrefix + ".document_type"}) assert.NoError(t, err) assert.NotNil(t, proof) - tree, err := e.DocumentRootTree() + signingRoot, err := e.CalculateSigningRoot() assert.NoError(t, err) // Validate entity_number - valid, err := tree.ValidateProof(proof[0]) + valid, err := documents.ValidateProof(proof[0], signingRoot, sha256.New()) assert.Nil(t, err) assert.True(t, valid) // Validate roles - valid, err = tree.ValidateProof(proof[1]) + valid, err = documents.ValidateProof(proof[1], signingRoot, sha256.New()) assert.Nil(t, err) assert.True(t, valid) @@ -257,38 +199,27 @@ func TestEntity_CreateProofs(t *testing.T) { assert.True(t, e.AccountCanRead(acc)) // Validate document_type - valid, err = tree.ValidateProof(proof[2]) + valid, err = documents.ValidateProof(proof[2], signingRoot, sha256.New()) assert.Nil(t, err) assert.True(t, valid) } -func createEntity(t *testing.T) *Entity { - e := new(Entity) - err := e.InitEntityInput(testingdocuments.CreateEntityPayload(), did) - assert.NoError(t, err) - e.GetTestCoreDocWithReset() - _, err = e.CalculateDataRoot() - assert.NoError(t, err) - _, err = e.CalculateSigningRoot() - assert.NoError(t, err) - _, err = e.CalculateDocumentRoot() - assert.NoError(t, err) - return e -} - func TestEntityModel_createProofsFieldDoesNotExist(t *testing.T) { - e := createEntity(t) + ctx := testingconfig.CreateAccountContext(t, cfg) + e, _ := CreateEntityWithEmbedCD(t, ctx, did, nil) _, err := e.CreateProofs([]string{"nonexisting"}) assert.NotNil(t, err) } func TestEntityModel_GetDocumentID(t *testing.T) { - e := createEntity(t) + ctx := testingconfig.CreateAccountContext(t, cfg) + e, _ := CreateEntityWithEmbedCD(t, ctx, did, nil) assert.Equal(t, e.CoreDocument.ID(), e.ID()) } func TestEntityModel_getDocumentDataTree(t *testing.T) { - e := createEntity(t) + ctx := testingconfig.CreateAccountContext(t, cfg) + e, _ := CreateEntityWithEmbedCD(t, ctx, did, nil) tree, err := e.getDocumentDataTree() assert.Nil(t, err, "tree should be generated without error") _, leaf := tree.GetLeafByProperty("entity.legal_name") @@ -297,7 +228,8 @@ func TestEntityModel_getDocumentDataTree(t *testing.T) { } func TestEntity_CollaboratorCanUpdate(t *testing.T) { - entity := createEntity(t) + ctx := testingconfig.CreateAccountContext(t, cfg) + entity, _ := CreateEntityWithEmbedCD(t, ctx, did, nil) id1 := did id2 := testingidentity.GenerateRandomDID() id3 := testingidentity.GenerateRandomDID() @@ -312,9 +244,19 @@ func TestEntity_CollaboratorCanUpdate(t *testing.T) { model, err := testRepo().Get(id1[:], entity.CurrentVersion()) assert.NoError(t, err) oldEntity := model.(*Entity) - data := oldEntity.getClientData() + data := oldEntity.Data data.LegalName = "new legal name" - err = entity.PrepareNewVersion(entity, data, documents.CollaboratorsAccess{ReadWriteCollaborators: []identity.DID{id3}}, oldEntity.Attributes) + d, err := json.Marshal(data) + assert.NoError(t, err) + err = entity.unpackFromUpdatePayload(entity, documents.UpdatePayload{ + DocumentID: entity.ID(), + CreatePayload: documents.CreatePayload{ + Data: d, + Collaborators: documents.CollaboratorsAccess{ + ReadWriteCollaborators: []identity.DID{id3}, + }, + }, + }) assert.NoError(t, err) // id1 should have permission @@ -332,10 +274,17 @@ func TestEntity_CollaboratorCanUpdate(t *testing.T) { model, err = testRepo().Get(id1[:], entity.CurrentVersion()) assert.NoError(t, err) oldEntity = model.(*Entity) - data = oldEntity.getClientData() + data = oldEntity.Data data.LegalName = "second new legal name" data.Contacts = nil - err = entity.PrepareNewVersion(entity, data, documents.CollaboratorsAccess{}, oldEntity.Attributes) + d, err = json.Marshal(data) + assert.NoError(t, err) + err = entity.unpackFromUpdatePayload(entity, documents.UpdatePayload{ + DocumentID: entity.ID(), + CreatePayload: documents.CreatePayload{ + Data: d, + }, + }) assert.NoError(t, err) // id1 should have permission @@ -380,26 +329,11 @@ func testRepo() documents.Repository { return testRepoGlobal } -func createCDWithEmbeddedEntity(t *testing.T) (documents.Model, coredocumentpb.CoreDocument) { - e := new(Entity) - err := e.InitEntityInput(testingdocuments.CreateEntityPayload(), did) - assert.NoError(t, err) - _, err = e.CalculateDataRoot() - assert.NoError(t, err) - _, err = e.CalculateSigningRoot() - assert.NoError(t, err) - _, err = e.CalculateDocumentRoot() - assert.NoError(t, err) - cd, err := e.PackCoreDocument() - assert.NoError(t, err) - return e, cd -} - func TestEntity_AddAttributes(t *testing.T) { - e, _ := createCDWithEmbeddedEntity(t) + e, _ := CreateEntityWithEmbedCD(t, testingconfig.CreateAccountContext(t, cfg), did, nil) label := "some key" value := "some value" - attr, err := documents.NewAttribute(label, documents.AttrString, value) + attr, err := documents.NewStringAttribute(label, documents.AttrString, value) assert.NoError(t, err) // success @@ -418,10 +352,10 @@ func TestEntity_AddAttributes(t *testing.T) { } func TestEntity_DeleteAttribute(t *testing.T) { - e, _ := createCDWithEmbeddedEntity(t) + e, _ := CreateEntityWithEmbedCD(t, testingconfig.CreateAccountContext(t, cfg), did, nil) label := "some key" value := "some value" - attr, err := documents.NewAttribute(label, documents.AttrString, value) + attr, err := documents.NewStringAttribute(label, documents.AttrString, value) assert.NoError(t, err) // failed @@ -436,7 +370,7 @@ func TestEntity_DeleteAttribute(t *testing.T) { } func TestEntity_GetData(t *testing.T) { - e := createEntity(t) + e, _ := CreateEntityWithEmbedCD(t, testingconfig.CreateAccountContext(t, cfg), did, nil) data := e.GetData() assert.Equal(t, e.Data, data) } @@ -528,8 +462,10 @@ func validDataWithIdentity(t *testing.T) []byte { } func checkEntityPayloadDataError(t *testing.T, e *Entity, payload documents.CreatePayload) { - err := e.loadData(payload.Data) + var d Data + err := loadData(payload.Data, &d) assert.Error(t, err) + e.Data = d } func TestEntity_loadData(t *testing.T) { @@ -554,8 +490,10 @@ func TestEntity_loadData(t *testing.T) { // valid data payload.Data = validData(t) - err := e.loadData(payload.Data) + var d Data + err := loadData(payload.Data, &d) assert.NoError(t, err) + e.Data = d data := e.GetData().(Data) assert.Equal(t, data.LegalName, "Hello, World!") assert.Len(t, data.PaymentDetails, 1) @@ -566,18 +504,20 @@ func TestEntity_loadData(t *testing.T) { assert.Equal(t, data.PaymentDetails[0].BankPaymentMethod.Identifier.String(), "0xbaeb33a61f05e6f269f1c4b4cff91a901b54daf7") } -func TestEntity_unpackFromCreatePayload(t *testing.T) { +func TestEntity_DeriveFromCreatePayload(t *testing.T) { payload := documents.CreatePayload{} e := new(Entity) + ctx := context.Background() // invalid data payload.Data = invalidDIDData(t) - err := e.unpackFromCreatePayload(did, payload) + payload.Collaborators.ReadWriteCollaborators = append(payload.Collaborators.ReadWriteCollaborators, did) + err := e.DeriveFromCreatePayload(ctx, payload) assert.Error(t, err) assert.True(t, errors.IsOfType(ErrEntityInvalidData, err)) // invalid attributes - attr, err := documents.NewAttribute("test", documents.AttrString, "value") + attr, err := documents.NewStringAttribute("test", documents.AttrString, "value") assert.NoError(t, err) val := attr.Value val.Type = documents.AttributeType("some type") @@ -586,7 +526,7 @@ func TestEntity_unpackFromCreatePayload(t *testing.T) { attr.Key: attr, } payload.Data = validData(t) - err = e.unpackFromCreatePayload(did, payload) + err = e.DeriveFromCreatePayload(ctx, payload) assert.Error(t, err) assert.True(t, errors.IsOfType(documents.ErrCDCreate, err)) @@ -596,13 +536,13 @@ func TestEntity_unpackFromCreatePayload(t *testing.T) { payload.Attributes = map[documents.AttrKey]documents.Attribute{ attr.Key: attr, } - err = e.unpackFromCreatePayload(did, payload) + err = e.DeriveFromCreatePayload(ctx, payload) assert.NoError(t, err) } func TestInvoice_unpackFromUpdatePayload(t *testing.T) { payload := documents.UpdatePayload{} - old := createEntity(t) + old, _ := CreateEntityWithEmbedCD(t, testingconfig.CreateAccountContext(t, cfg), did, nil) e := new(Entity) // invalid data @@ -612,7 +552,7 @@ func TestInvoice_unpackFromUpdatePayload(t *testing.T) { assert.True(t, errors.IsOfType(ErrEntityInvalidData, err)) // invalid attributes - attr, err := documents.NewAttribute("test", documents.AttrString, "value") + attr, err := documents.NewStringAttribute("test", documents.AttrString, "value") assert.NoError(t, err) val := attr.Value val.Type = documents.AttributeType("some type") @@ -634,3 +574,55 @@ func TestInvoice_unpackFromUpdatePayload(t *testing.T) { err = e.unpackFromUpdatePayload(old, payload) assert.NoError(t, err) } + +func TestEntity_Patch(t *testing.T) { + payload := documents.UpdatePayload{} + doc, _ := CreateEntityWithEmbedCD(t, testingconfig.CreateAccountContext(t, cfg), did, nil) + + // invalid data + payload.Data = invalidDIDData(t) + err := doc.Patch(payload) + assert.Error(t, err) + + // coredoc patch failed + doc.CoreDocument.Status = documents.Committed + payload.Data = validDataWithIdentity(t) + err = doc.Patch(payload) + assert.Error(t, err) + assert.True(t, errors.IsOfType(documents.ErrDocumentNotInAllowedState, err)) + + // success + doc.CoreDocument.Status = documents.Pending + err = doc.Patch(payload) + assert.NoError(t, err) +} + +func TestEntity_DeriveFromUpdatePayload(t *testing.T) { + payload := documents.UpdatePayload{} + doc, _ := CreateEntityWithEmbedCD(t, testingconfig.CreateAccountContext(t, cfg), did, nil) + ctx := context.Background() + + // invalid data + payload.Data = invalidDIDData(t) + _, err := doc.DeriveFromUpdatePayload(ctx, payload) + assert.Error(t, err) + + // coredoc failed + payload.Data = validDataWithIdentity(t) + attr, err := documents.NewStringAttribute("test", documents.AttrString, "value") + assert.NoError(t, err) + val := attr.Value + val.Type = documents.AttributeType("some type") + attr.Value = val + payload.Attributes = map[documents.AttrKey]documents.Attribute{ + attr.Key: attr, + } + _, err = doc.DeriveFromUpdatePayload(ctx, payload) + assert.Error(t, err) + + // Success + payload.Attributes = nil + gdoc, err := doc.DeriveFromUpdatePayload(ctx, payload) + assert.NoError(t, err) + assert.NotNil(t, gdoc) +} diff --git a/documents/entity/service.go b/documents/entity/service.go index ecba6f9c7..05359e27e 100644 --- a/documents/entity/service.go +++ b/documents/entity/service.go @@ -11,7 +11,6 @@ import ( "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/go-centrifuge/identity" "github.com/centrifuge/go-centrifuge/jobs" - cliententitypb "github.com/centrifuge/go-centrifuge/protobufs/gen/go/entity" "github.com/centrifuge/go-centrifuge/queue" "github.com/centrifuge/go-centrifuge/utils" "github.com/ethereum/go-ethereum/common/hexutil" @@ -21,38 +20,8 @@ import ( type Service interface { documents.Service - // DeriveFromPayload derives Entity from clientPayload - DeriveFromCreatePayload(ctx context.Context, payload *cliententitypb.EntityCreatePayload) (documents.Model, error) - - // DeriveFromUpdatePayload derives entity model from update payload - DeriveFromUpdatePayload(ctx context.Context, payload *cliententitypb.EntityUpdatePayload) (documents.Model, error) - - // DeriveEntityData returns the entity data as client data - DeriveEntityData(entity documents.Model) (*cliententitypb.EntityData, error) - - // DeriveEntityResponse returns the entity model in our standard client format - DeriveEntityResponse(ctx context.Context, entity documents.Model) (*cliententitypb.EntityResponse, error) - - // ListEntityRelationships lists all the relationships associated with the passed in entity identifier - ListEntityRelationships(ctx context.Context, entityIdentifier []byte) (documents.Model, []documents.Model, error) - // GetEntityByRelationship returns the entity model from database or requests from granter GetEntityByRelationship(ctx context.Context, relationshipIdentifier []byte) (documents.Model, error) - - // DeriveFromSharePayload derives the entity relationship from the relationship payload - DeriveFromSharePayload(ctx context.Context, payload *cliententitypb.RelationshipPayload) (documents.Model, error) - - // Share takes an entity relationship, validates it, and tries to persist it to the DB - Share(ctx context.Context, entityRelationship documents.Model) (documents.Model, jobs.JobID, chan bool, error) - - // DeriveFromRevokePayload derives the revoked entity relationship from the relationship payload - DeriveFromRevokePayload(ctx context.Context, payload *cliententitypb.RelationshipPayload) (documents.Model, error) - - // Revoke takes a revoked entity relationship, validates it, and tries to persist it to the DB - Revoke(ctx context.Context, entityRelationship documents.Model) (documents.Model, jobs.JobID, chan bool, error) - - // DeriveEntityRelationshipResponse returns create response from entity relationship model - DeriveEntityRelationshipResponse(model documents.Model) (*cliententitypb.RelationshipResponse, error) } // service implements Service and handles all entity related persistence and validations @@ -66,7 +35,6 @@ type service struct { processor documents.DocumentRequestProcessor erService entityrelationship.Service anchorRepo anchors.AnchorRepository - idService identity.Service receivedEntityValidator func() documents.ValidatorGroup } @@ -78,7 +46,6 @@ func DefaultService( jobManager jobs.Manager, factory identity.Factory, erService entityrelationship.Service, - idService identity.Service, anchorRepo anchors.AnchorRepository, processor documents.DocumentRequestProcessor, receivedEntityValidator func() documents.ValidatorGroup, @@ -90,7 +57,6 @@ func DefaultService( Service: srv, factory: factory, erService: erService, - idService: idService, anchorRepo: anchorRepo, processor: processor, receivedEntityValidator: receivedEntityValidator, @@ -108,26 +74,6 @@ func (s service) DeriveFromCoreDocument(cd coredocumentpb.CoreDocument) (documen return entity, nil } -// UnpackFromCreatePayload initializes the model with parameters provided from the rest-api call -func (s service) DeriveFromCreatePayload(ctx context.Context, payload *cliententitypb.EntityCreatePayload) (documents.Model, error) { - if payload == nil || payload.Data == nil { - return nil, documents.ErrPayloadNil - } - - did, err := contextutil.AccountDID(ctx) - if err != nil { - return nil, documents.ErrDocumentConfigAccountID - } - - entity := new(Entity) - err = entity.InitEntityInput(payload, did) - if err != nil { - return nil, errors.NewTypedError(documents.ErrDocumentInvalid, err) - } - - return entity, nil -} - // validateAndPersist validates the document, calculates the data root, and persists to DB func (s service) validateAndPersist(ctx context.Context, old, new documents.Model, validator documents.Validator) (documents.Model, error) { selfDID, err := contextutil.AccountDID(ctx) @@ -155,28 +101,8 @@ func (s service) validateAndPersist(ctx context.Context, old, new documents.Mode return entity, nil } -// Create takes an entity model and does required validation checks, tries to persist to DB -func (s service) Create(ctx context.Context, entity documents.Model) (documents.Model, jobs.JobID, chan bool, error) { - selfDID, err := contextutil.AccountDID(ctx) - if err != nil { - return nil, jobs.NilJobID(), nil, errors.NewTypedError(documents.ErrDocumentConfigAccountID, err) - } - - entity, err = s.validateAndPersist(ctx, nil, entity, CreateValidator(s.factory)) - if err != nil { - return nil, jobs.NilJobID(), nil, err - } - - jobID := contextutil.Job(ctx) - jobID, done, err := documents.CreateAnchorJob(ctx, s.jobManager, s.queueSrv, selfDID, jobID, entity.CurrentVersion()) - if err != nil { - return nil, jobs.NilJobID(), nil, err - } - return entity, jobID, done, nil -} - // Update finds the old document, validates the new version and persists the updated document -func (s service) Update(ctx context.Context, new documents.Model) (documents.Model, jobs.JobID, chan bool, error) { +func (s service) Update(ctx context.Context, new documents.Model) (documents.Model, jobs.JobID, chan error, error) { selfDID, err := contextutil.AccountDID(ctx) if err != nil { return nil, jobs.NilJobID(), nil, errors.NewTypedError(documents.ErrDocumentConfigAccountID, err) @@ -200,128 +126,6 @@ func (s service) Update(ctx context.Context, new documents.Model) (documents.Mod return new, jobID, done, nil } -// DeriveEntityResponse returns create response from entity model -func (s service) DeriveEntityResponse(ctx context.Context, model documents.Model) (*cliententitypb.EntityResponse, error) { - data, err := s.DeriveEntityData(model) - if err != nil { - return nil, err - } - - // note that token registry is(must be) irrelevant here - h, err := documents.DeriveResponseHeader(nil, model) - if err != nil { - return nil, err - } - - entityID := model.ID() - var relationships []*cliententitypb.Relationship - // if this identity is not the owner of the entity, return an empty relationships array - selfDID, err := contextutil.AccountDID(ctx) - if err != nil { - return nil, errors.New("failed to get self ID") - } - - isCollaborator, err := model.IsDIDCollaborator(selfDID) - if err != nil { - return nil, err - } - if !isCollaborator { - return &cliententitypb.EntityResponse{ - Header: h, - Data: &cliententitypb.EntityDataResponse{ - Entity: data, - Relationships: relationships, - }, - }, nil - } - _, models, err := s.ListEntityRelationships(ctx, entityID) - if err != nil { - return nil, err - } - - //list the relationships associated with the entity - if models != nil { - for _, m := range models { - tokens, err := m.GetAccessTokens() - if err != nil { - return nil, err - } - - targetDID := m.(*entityrelationship.EntityRelationship).TargetIdentity.String() - r := &cliententitypb.Relationship{ - Identity: targetDID, - Active: len(tokens) != 0, - } - relationships = append(relationships, r) - } - } - - attrs, err := documents.ToClientAttributes(model.GetAttributes()) - if err != nil { - return nil, err - } - - return &cliententitypb.EntityResponse{ - Header: h, - Data: &cliententitypb.EntityDataResponse{ - Entity: data, - Relationships: relationships, - }, - Attributes: attrs, - }, nil -} - -// DeriveEntityRelationshipData returns the relationship data from an entity relationship model -func (s service) DeriveEntityRelationshipData(model documents.Model) (*cliententitypb.RelationshipData, error) { - return s.erService.DeriveEntityRelationshipData(model) -} - -// DeriveEntityData returns create response from entity model -func (s service) DeriveEntityData(doc documents.Model) (*cliententitypb.EntityData, error) { - entity, ok := doc.(*Entity) - if !ok { - return nil, documents.ErrDocumentInvalidType - } - - return entity.getClientData(), nil -} - -// DeriveFromUpdatePayload returns a new version of the old entity identified by identifier in payload -func (s service) DeriveFromUpdatePayload(ctx context.Context, payload *cliententitypb.EntityUpdatePayload) (documents.Model, error) { - if payload == nil || payload.Data == nil { - return nil, documents.ErrPayloadNil - } - - // get latest old version of the document - id, err := hexutil.Decode(payload.DocumentId) - if err != nil { - return nil, errors.NewTypedError(documents.ErrDocumentIdentifier, errors.New("failed to decode identifier: %v", err)) - } - - old, err := s.GetCurrentVersion(ctx, id) - if err != nil { - return nil, err - } - - cs, err := documents.FromClientCollaboratorAccess(payload.ReadAccess, payload.WriteAccess) - if err != nil { - return nil, err - } - - attrs, err := documents.FromClientAttributes(payload.Attributes) - if err != nil { - return nil, err - } - - entity := new(Entity) - err = entity.PrepareNewVersion(old, payload.Data, cs, attrs) - if err != nil { - return nil, errors.NewTypedError(documents.ErrDocumentPrepareCoreDocument, errors.New("failed to load entity from data: %v", err)) - } - - return entity, nil -} - // GetEntityByRelationship returns the entity model from database or requests from a granter peer func (s service) GetEntityByRelationship(ctx context.Context, relationshipIdentifier []byte) (documents.Model, error) { model, err := s.erService.GetCurrentVersion(ctx, relationshipIdentifier) @@ -353,44 +157,23 @@ func (s service) GetEntityByRelationship(ctx context.Context, relationshipIdenti return s.requestEntityWithRelationship(ctx, relationship) } -// ListEntityRelationships lists all the latest versions of the relationships associated with the passed in entity identifier -func (s service) ListEntityRelationships(ctx context.Context, entityIdentifier []byte) (documents.Model, []documents.Model, error) { - entity, err := s.GetCurrentVersion(ctx, entityIdentifier) - if err != nil { - return nil, nil, err - } - relationships, err := s.erService.GetEntityRelationships(ctx, entityIdentifier) - if err != nil { - return nil, nil, err - } - return entity, relationships, nil -} - func (s service) GetCurrentVersion(ctx context.Context, documentID []byte) (documents.Model, error) { - selfDID, err := contextutil.AccountDID(ctx) + did, err := contextutil.AccountDID(ctx) if err != nil { return nil, errors.NewTypedError(documents.ErrDocumentConfigAccountID, err) } - var entity documents.Model - if s.Service.Exists(ctx, documentID) { - entity, err = s.Service.GetCurrentVersion(ctx, documentID) - - if err != nil { - return nil, err - } + entity, err := s.Service.GetCurrentVersion(ctx, documentID) + if err != nil { + return nil, documents.ErrDocumentNotFound + } - isCollaborator, err := entity.IsDIDCollaborator(selfDID) - if err != nil { - return nil, err - } - if !isCollaborator { - return nil, documents.ErrNoCollaborator - } - return entity, nil + isCollaborator, err := entity.IsDIDCollaborator(did) + if err != nil || !isCollaborator { + return nil, documents.ErrDocumentNotFound } - return nil, documents.ErrDocumentNotFound + return entity, nil } func (s service) requestEntityWithRelationship(ctx context.Context, relationship *entityrelationship.EntityRelationship) (documents.Model, error) { @@ -405,7 +188,7 @@ func (s service) requestEntityWithRelationship(ctx context.Context, relationship } at := accessTokens[0] - if !utils.IsSameByteSlice(at.DocumentIdentifier, relationship.EntityIdentifier) { + if !utils.IsSameByteSlice(at.DocumentIdentifier, relationship.Data.EntityIdentifier) { return nil, entityrelationship.ErrERInvalidIdentifier } @@ -459,31 +242,6 @@ func (s service) store(ctx context.Context, model documents.Model) error { return nil } -// DeriveFromSharePayload derives the entity relationship from the relationship payload -func (s service) DeriveFromSharePayload(ctx context.Context, payload *cliententitypb.RelationshipPayload) (documents.Model, error) { - return s.erService.DeriveFromCreatePayload(ctx, payload) -} - -// Share takes an entity relationship, validates it, and tries to persist it to the DB -func (s service) Share(ctx context.Context, entityRelationship documents.Model) (documents.Model, jobs.JobID, chan bool, error) { - return s.erService.Create(ctx, entityRelationship) -} - -// DeriveFromRevokePayload derives the revoked entity relationship from the relationship payload -func (s service) DeriveFromRevokePayload(ctx context.Context, payload *cliententitypb.RelationshipPayload) (documents.Model, error) { - return s.erService.DeriveFromUpdatePayload(ctx, payload) -} - -// Revoke takes a revoked entity relationship, validates it, and tries to persist it to the DB -func (s service) Revoke(ctx context.Context, entityRelationship documents.Model) (documents.Model, jobs.JobID, chan bool, error) { - return s.erService.Update(ctx, entityRelationship) -} - -// DeriveEntityRelationshipResponse returns create response from entity relationship model -func (s service) DeriveEntityRelationshipResponse(model documents.Model) (*cliententitypb.RelationshipResponse, error) { - return s.erService.DeriveEntityRelationshipResponse(model) -} - // CreateModel creates entity from the payload, validates, persists, and returns the entity. func (s service) CreateModel(ctx context.Context, payload documents.CreatePayload) (documents.Model, jobs.JobID, error) { if payload.Data == nil { @@ -496,7 +254,8 @@ func (s service) CreateModel(ctx context.Context, payload documents.CreatePayloa } e := new(Entity) - if err := e.unpackFromCreatePayload(did, payload); err != nil { + payload.Collaborators.ReadWriteCollaborators = append(payload.Collaborators.ReadWriteCollaborators, did) + if err := e.DeriveFromCreatePayload(ctx, payload); err != nil { return nil, jobs.NilJobID(), errors.NewTypedError(documents.ErrDocumentInvalid, err) } @@ -558,3 +317,13 @@ func (s service) UpdateModel(ctx context.Context, payload documents.UpdatePayloa jobID, _, err = documents.CreateAnchorJob(ctx, s.jobManager, s.queueSrv, did, jobID, e.CurrentVersion()) return e, jobID, err } + +// New returns a new uninitialised Entity. +func (s service) New(_ string) (documents.Model, error) { + return new(Entity), nil +} + +// Validate takes care of entity validation +func (s service) Validate(ctx context.Context, model documents.Model, old documents.Model) error { + return fieldValidator(s.factory).Validate(old, model) +} diff --git a/documents/entity/service_test.go b/documents/entity/service_test.go index f2e893684..2f8bea1ff 100644 --- a/documents/entity/service_test.go +++ b/documents/entity/service_test.go @@ -4,11 +4,10 @@ package entity import ( "context" + "encoding/json" "testing" "time" - "github.com/centrifuge/centrifuge-protobufs/gen/go/coredocument" - "github.com/centrifuge/centrifuge-protobufs/gen/go/entity" "github.com/centrifuge/centrifuge-protobufs/gen/go/p2p" "github.com/centrifuge/go-centrifuge/anchors" "github.com/centrifuge/go-centrifuge/documents" @@ -16,8 +15,6 @@ import ( "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/go-centrifuge/identity" "github.com/centrifuge/go-centrifuge/jobs" - cliententitypb "github.com/centrifuge/go-centrifuge/protobufs/gen/go/entity" - entitypb2 "github.com/centrifuge/go-centrifuge/protobufs/gen/go/entity" "github.com/centrifuge/go-centrifuge/storage" "github.com/centrifuge/go-centrifuge/testingutils" "github.com/centrifuge/go-centrifuge/testingutils/anchors" @@ -28,86 +25,10 @@ import ( "github.com/centrifuge/go-centrifuge/testingutils/testingjobs" "github.com/centrifuge/go-centrifuge/utils" "github.com/centrifuge/gocelery" - "github.com/ethereum/go-ethereum/common/hexutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) -type MockEntityRelationService struct { - documents.Service - mock.Mock -} - -func (m *MockEntityRelationService) GetCurrentVersion(ctx context.Context, documentID []byte) (documents.Model, error) { - args := m.Called(documentID) - return args.Get(0).(documents.Model), args.Error(1) -} - -func (m *MockEntityRelationService) GetVersion(ctx context.Context, documentID []byte, version []byte) (documents.Model, error) { - args := m.Called(documentID, version) - return args.Get(0).(documents.Model), args.Error(1) -} - -func (m *MockEntityRelationService) CreateProofs(ctx context.Context, documentID []byte, fields []string) (*documents.DocumentProof, error) { - args := m.Called(documentID, fields) - return args.Get(0).(*documents.DocumentProof), args.Error(1) -} - -func (m *MockEntityRelationService) CreateProofsForVersion(ctx context.Context, documentID, version []byte, fields []string) (*documents.DocumentProof, error) { - args := m.Called(documentID, version, fields) - return args.Get(0).(*documents.DocumentProof), args.Error(1) -} - -func (m *MockEntityRelationService) DeriveFromCoreDocument(cd coredocumentpb.CoreDocument) (documents.Model, error) { - args := m.Called(cd) - return args.Get(0).(documents.Model), args.Error(1) -} - -func (m *MockEntityRelationService) RequestDocumentSignature(ctx context.Context, model documents.Model, collaborator identity.DID) (*coredocumentpb.Signature, error) { - args := m.Called() - return args.Get(0).(*coredocumentpb.Signature), args.Error(1) -} - -func (m *MockEntityRelationService) ReceiveAnchoredDocument(ctx context.Context, model documents.Model, collaborator identity.DID) error { - args := m.Called() - return args.Error(0) -} - -func (m *MockEntityRelationService) Exists(ctx context.Context, documentID []byte) bool { - args := m.Called() - return args.Get(0).(bool) -} - -// DeriveFromCreatePayload derives Entity Relationship from RelationshipPayload -func (m *MockEntityRelationService) DeriveFromCreatePayload(ctx context.Context, payload *entitypb2.RelationshipPayload) (documents.Model, error) { - args := m.Called(ctx, payload) - return args.Get(0).(documents.Model), args.Error(1) -} - -// DeriveFromUpdatePayload derives a revoked entity relationship model from RelationshipPayload -func (m *MockEntityRelationService) DeriveFromUpdatePayload(ctx context.Context, payload *entitypb2.RelationshipPayload) (documents.Model, error) { - args := m.Called(ctx, payload) - return args.Get(0).(documents.Model), args.Error(1) -} - -// DeriveEntityRelationshipData returns the entity relationship data as client data -func (m *MockEntityRelationService) DeriveEntityRelationshipData(relationship documents.Model) (*entitypb2.RelationshipData, error) { - args := m.Called(relationship) - return args.Get(0).(*entitypb2.RelationshipData), args.Error(1) -} - -// DeriveEntityRelationshipResponse returns the entity relationship model in our standard client format -func (m *MockEntityRelationService) DeriveEntityRelationshipResponse(relationship documents.Model) (*entitypb2.RelationshipResponse, error) { - args := m.Called(relationship) - return args.Get(0).(*entitypb2.RelationshipResponse), args.Error(1) -} - -// GetEntityRelationships returns a list of the latest versions of the relevant entity relationship based on an entity id -func (m *MockEntityRelationService) GetEntityRelationships(ctx context.Context, entityID []byte) ([]documents.Model, error) { - args := m.Called(ctx, entityID) - return args.Get(0).([]documents.Model), args.Error(1) -} - func getServiceWithMockedLayers() (testingcommons.MockIdentityService, *testingcommons.MockIdentityFactory, Service) { c := &testingconfig.MockConfig{} c.On("GetIdentityID").Return(dIDBytes, nil) @@ -118,14 +39,14 @@ func getServiceWithMockedLayers() (testingcommons.MockIdentityService, *testingc idFactory := new(testingcommons.MockIdentityFactory) repo := testRepo() mockAnchor := &mockAnchorRepo{} - docSrv := documents.DefaultService(cfg, repo, mockAnchor, documents.NewServiceRegistry(), &idService) + docSrv := documents.DefaultService(cfg, repo, mockAnchor, documents.NewServiceRegistry(), &idService, nil, nil) anchorRepo := &testinganchors.MockAnchorRepo{} anchorRepo.On("GetAnchorData", mock.Anything).Return(nil, errors.New("missing")) return idService, idFactory, DefaultService( docSrv, repo, queueSrv, - ctx[jobs.BootstrappedService].(jobs.Manager), idFactory, + ctx[jobs.BootstrappedService].(jobs.Manager), nil, nil, anchorRepo, nil, nil) } @@ -135,7 +56,7 @@ func TestService_Update(t *testing.T) { ctxh := testingconfig.CreateAccountContext(t, cfg) // missing last version - model, _ := createCDWithEmbeddedEntity(t) + model, _ := CreateEntityWithEmbedCD(t, ctxh, did, nil) _, _, _, err := eSrv.Update(ctxh, model) assert.Error(t, err) assert.True(t, errors.IsOfType(documents.ErrDocumentNotFound, err)) @@ -151,284 +72,72 @@ func TestService_Update(t *testing.T) { // success idFactory.On("IdentityExists", mock.Anything).Return(true, nil) - data, err := eSrv.DeriveEntityData(model) - assert.NoError(t, err) + data := model.Data data.LegalName = "test company" - data.Contacts = []*entitypb.Contact{{Name: "Mr. Test"}} - collab := testingidentity.GenerateRandomDID().String() - newInv, err := eSrv.DeriveFromUpdatePayload(ctxh, &cliententitypb.EntityUpdatePayload{ - DocumentId: hexutil.Encode(model.ID()), - WriteAccess: []string{collab}, - Data: data, + data.Contacts = []Contact{{Name: "Mr. Test"}} + collab := testingidentity.GenerateRandomDID() + newEntity := new(Entity) + d, err := json.Marshal(data) + assert.NoError(t, err) + err = newEntity.unpackFromUpdatePayload(model, documents.UpdatePayload{ + DocumentID: model.ID(), + CreatePayload: documents.CreatePayload{ + Collaborators: documents.CollaboratorsAccess{ + ReadWriteCollaborators: []identity.DID{collab}, + }, + Data: d, + }, }) assert.NoError(t, err) - newData, err := eSrv.DeriveEntityData(newInv) - assert.NoError(t, err) + newData := newEntity.Data assert.Equal(t, data, newData) - model, _, _, err = eSrv.Update(ctxh, newInv) + eSrv.factory = idFactory + doc, _, _, err := eSrv.Update(ctxh, newEntity) assert.NoError(t, err) - assert.NotNil(t, model) - assert.True(t, testRepo().Exists(accountID, model.ID())) - assert.True(t, testRepo().Exists(accountID, model.CurrentVersion())) - assert.True(t, testRepo().Exists(accountID, model.PreviousVersion())) + assert.NotNil(t, doc) + assert.True(t, testRepo().Exists(accountID, doc.ID())) + assert.True(t, testRepo().Exists(accountID, doc.CurrentVersion())) + assert.True(t, testRepo().Exists(accountID, doc.PreviousVersion())) - newData, err = eSrv.DeriveEntityData(model) + newData = doc.GetData().(Data) assert.NoError(t, err) assert.Equal(t, data, newData) idFactory.AssertExpectations(t) } -func TestService_DeriveFromUpdatePayload(t *testing.T) { - _, _, eSrv := getServiceWithMockedLayers() - // nil payload - doc, err := eSrv.DeriveFromUpdatePayload(nil, nil) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrPayloadNil, err)) - assert.Nil(t, doc) - - // nil payload data - doc, err = eSrv.DeriveFromUpdatePayload(nil, &cliententitypb.EntityUpdatePayload{}) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrPayloadNil, err)) - assert.Nil(t, doc) - - // messed up identifier - contextHeader := testingconfig.CreateAccountContext(t, cfg) - payload := &cliententitypb.EntityUpdatePayload{DocumentId: "some identifier", Data: &cliententitypb.EntityData{}} - doc, err = eSrv.DeriveFromUpdatePayload(contextHeader, payload) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrDocumentIdentifier, err)) - assert.Contains(t, err.Error(), "failed to decode identifier") - assert.Nil(t, doc) - - // missing last version - id := utils.RandomSlice(32) - payload.DocumentId = hexutil.Encode(id) - doc, err = eSrv.DeriveFromUpdatePayload(contextHeader, payload) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrDocumentNotFound, err)) - assert.Nil(t, doc) - - // Entity data does not contain an identity - old, _ := createCDWithEmbeddedEntity(t) - err = testRepo().Create(accountID, old.CurrentVersion(), old) - assert.NoError(t, err) - payload.Data = &cliententitypb.EntityData{ - LegalName: "test company", - Contacts: []*entitypb.Contact{{Name: "Mr. Test"}}, - } - - payload.DocumentId = hexutil.Encode(old.ID()) - doc, err = eSrv.DeriveFromUpdatePayload(contextHeader, payload) - - assert.Error(t, err, "should fail because Identity is missing") - assert.Nil(t, doc) - - // invalid collaborator identity - payload.Data.LegalName = "new company name" - payload.WriteAccess = []string{"some wrong ID"} - payload.Data.Identity = testingidentity.GenerateRandomDID().String() - doc, err = eSrv.DeriveFromUpdatePayload(contextHeader, payload) - assert.Error(t, err) - assert.Nil(t, doc) - - // success - wantCollab := testingidentity.GenerateRandomDID() - payload.WriteAccess = []string{wantCollab.String()} - doc, err = eSrv.DeriveFromUpdatePayload(contextHeader, payload) - assert.NoError(t, err) - assert.NotNil(t, doc) - cs, err := doc.GetCollaborators() - assert.NoError(t, err) - assert.Len(t, cs.ReadWriteCollaborators, 3) - assert.Contains(t, cs.ReadWriteCollaborators, wantCollab) - assert.Equal(t, old.ID(), doc.ID()) - assert.Equal(t, payload.DocumentId, hexutil.Encode(doc.ID())) - assert.Equal(t, old.CurrentVersion(), doc.PreviousVersion()) - assert.Equal(t, old.NextVersion(), doc.CurrentVersion()) - assert.NotNil(t, doc.NextVersion()) - data := doc.(*Entity).getClientData() - assert.Equal(t, payload.Data, data) -} - -func TestService_DeriveFromCreatePayload(t *testing.T) { - eSrv := service{} - ctxh := testingconfig.CreateAccountContext(t, cfg) - - // nil payload - m, err := eSrv.DeriveFromCreatePayload(ctxh, nil) - assert.Nil(t, m) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrPayloadNil, err)) - - // nil data payload - m, err = eSrv.DeriveFromCreatePayload(ctxh, &cliententitypb.EntityCreatePayload{}) - assert.Nil(t, m) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrPayloadNil, err)) - - // Init fails - payload := &cliententitypb.EntityCreatePayload{ - Data: &cliententitypb.EntityData{ - Identity: "i am not a did", - LegalName: "company test", - }, - } - - m, err = eSrv.DeriveFromCreatePayload(ctxh, payload) - assert.Nil(t, m) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrDocumentInvalid, err)) - - // success - payload.Data.Identity = testingidentity.GenerateRandomDID().String() - m, err = eSrv.DeriveFromCreatePayload(ctxh, payload) - assert.NoError(t, err) - assert.NotNil(t, m) - entity := m.(*Entity) - assert.Equal(t, entity.Data.LegalName, payload.Data.LegalName) -} - func TestService_DeriveFromCoreDocument(t *testing.T) { eSrv := service{repo: testRepo()} - _, cd := createCDWithEmbeddedEntity(t) + _, cd := CreateEntityWithEmbedCD(t, testingconfig.CreateAccountContext(t, cfg), did, nil) m, err := eSrv.DeriveFromCoreDocument(cd) assert.NoError(t, err, "must return model") assert.NotNil(t, m, "model must be non-nil") entity, ok := m.(*Entity) assert.True(t, ok, "must be true") - assert.Equal(t, entity.Data.LegalName, "Company Test") - assert.Equal(t, entity.Data.Contacts[0].Name, "Satoshi Nakamoto") -} - -func TestService_Create(t *testing.T) { - ctxh := testingconfig.CreateAccountContext(t, cfg) - _, idFactory, srv := getServiceWithMockedLayers() - eSrv := srv.(service) - - // calculate data root fails - m, _, _, err := eSrv.Create(ctxh, &mockModel{}) - assert.Nil(t, m) - assert.Error(t, err) - assert.Contains(t, err.Error(), "unknown document type") - - // success - idFactory.On("IdentityExists", mock.Anything).Return(true, nil) - entity, err := eSrv.DeriveFromCreatePayload(ctxh, testingdocuments.CreateEntityPayload()) - assert.NoError(t, err) - m, _, _, err = eSrv.Create(ctxh, entity) - assert.NoError(t, err) - assert.True(t, testRepo().Exists(accountID, m.ID())) - assert.True(t, testRepo().Exists(accountID, m.CurrentVersion())) - idFactory.AssertExpectations(t) -} - -func TestService_DeriveEntityData(t *testing.T) { - _, _, eSrv := getServiceWithMockedLayers() - - // some random model - _, err := eSrv.DeriveEntityData(&mockModel{}) - assert.Error(t, err, "Derive must fail") - - // success - payload := testingdocuments.CreateEntityPayload() - entity, err := eSrv.DeriveFromCreatePayload(testingconfig.CreateAccountContext(t, cfg), payload) - assert.NoError(t, err, "must be non nil") - data, err := eSrv.DeriveEntityData(entity) - assert.NoError(t, err, "Derive must succeed") - assert.NotNil(t, data, "data must be non nil") -} - -func TestService_DeriveEntityResponse(t *testing.T) { - ctxh := testingconfig.CreateAccountContext(t, cfg) - - // prepare a service with mocked layers - ctxh, entity, er, idFactory, idService, repo := setupRelationshipTesting(t) - eID := entity.ID() - erID := er.ID() - - // testcase: request from peer - mockAnchor := &mockAnchorRepo{} - docSrv := testingdocuments.MockService{} - mockedERSrv := &MockEntityRelationService{} - mockProcessor := &testingcommons.MockRequestProcessor{} - - docSrv.On("GetCurrentVersion", eID).Return(entity, nil) - docSrv.On("Exists").Return(true).Once() - mockedERSrv.On("GetCurrentVersion", er.ID()).Return(er, nil) - - fakeRoot, err := anchors.ToDocumentRoot(utils.RandomSlice(32)) - assert.NoError(t, err) - nextId, err := anchors.ToAnchorID(entity.NextVersion()) - assert.NoError(t, err) - mockAnchor.On("GetAnchorData", nextId).Return(fakeRoot, time.Now(), nil).Once() - - token, err := er.GetAccessTokens() - assert.NoError(t, err) - - cd, err := entity.PackCoreDocument() - assert.NoError(t, err) - - mockProcessor.On("RequestDocumentWithAccessToken", did, token[0].Identifier, eID, erID).Return(&p2ppb.GetDocumentResponse{Document: &cd}, nil) - docSrv.On("DeriveFromCoreDocument", mock.Anything).Return(entity, nil) - docSrv.On("Exists").Return(false).Once() - mockedERSrv.On("GetEntityRelationships", mock.Anything, entity.ID()).Return([]documents.Model{er}, nil) - //initialize service - entitySrv := DefaultService( - &docSrv, - repo, - nil, - nil, idFactory, - mockedERSrv, idService, mockAnchor, mockProcessor, func() documents.ValidatorGroup { - return documents.ValidatorGroup{} - }) - - // derive data failed - m := new(mockModel) - r, err := entitySrv.DeriveEntityResponse(ctxh, m) - m.AssertExpectations(t) - assert.Nil(t, r) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrDocumentInvalidType, err)) - - // success - r, err = entitySrv.DeriveEntityResponse(ctxh, entity) - assert.NoError(t, err) - payload := testingdocuments.CreateEntityPayload() - assert.Equal(t, payload.Data.Contacts[0].Name, r.Data.Entity.Contacts[0].Name) - assert.Equal(t, payload.Data.LegalName, r.Data.Entity.LegalName) - assert.Contains(t, r.Header.WriteAccess, did.String()) - - // entity is not collaborator on document - e := new(Entity) - err = e.InitEntityInput(testingdocuments.CreateEntityPayload(), testingidentity.GenerateRandomDID()) - assert.NoError(t, err) - _, err = e.CalculateDataRoot() - assert.NoError(t, err) - _, err = e.CalculateSigningRoot() - assert.NoError(t, err) - _, err = e.CalculateDocumentRoot() - assert.NoError(t, err) - cd, err = e.PackCoreDocument() - assert.NoError(t, err) - empty, err := entitySrv.DeriveEntityResponse(ctxh, e) - assert.NoError(t, err) - assert.Equal(t, empty.Data.Relationships, []*entitypb2.Relationship(nil)) + assert.Equal(t, entity.Data.LegalName, "Hello, world") + assert.Equal(t, entity.Data.Contacts[0].Name, "John Doe") } func TestService_GetCurrentVersion(t *testing.T) { _, _, eSrv := getServiceWithMockedLayers() - doc, _ := createCDWithEmbeddedEntity(t) + doc, _ := CreateEntityWithEmbedCD(t, testingconfig.CreateAccountContext(t, cfg), did, nil) ctxh := testingconfig.CreateAccountContext(t, cfg) err := testRepo().Create(accountID, doc.CurrentVersion(), doc) assert.NoError(t, err) - data := doc.(*Entity).getClientData() + data := doc.Data data.LegalName = "test company" doc2 := new(Entity) - assert.NoError(t, doc2.PrepareNewVersion(doc, data, documents.CollaboratorsAccess{}, doc.(*Entity).Attributes)) + d, err := json.Marshal(data) + assert.NoError(t, err) + err = doc2.unpackFromUpdatePayload(doc, documents.UpdatePayload{ + DocumentID: doc.ID(), + CreatePayload: documents.CreatePayload{ + Data: d, + }, + }) + assert.NoError(t, err) assert.NoError(t, testRepo().Create(accountID, doc2.CurrentVersion(), doc2)) doc3, err := eSrv.GetCurrentVersion(ctxh, doc.ID()) @@ -440,12 +149,12 @@ func TestService_GetCurrentVersion(t *testing.T) { } func TestService_GetVersion(t *testing.T) { + ctxh := testingconfig.CreateAccountContext(t, cfg) _, _, eSrv := getServiceWithMockedLayers() - entity, _ := createCDWithEmbeddedEntity(t) + entity, _ := CreateEntityWithEmbedCD(t, ctxh, did, nil) err := testRepo().Create(accountID, entity.CurrentVersion(), entity) assert.NoError(t, err) - ctxh := testingconfig.CreateAccountContext(t, cfg) mod, err := eSrv.GetVersion(ctxh, entity.ID(), entity.CurrentVersion()) assert.NoError(t, err) @@ -454,14 +163,13 @@ func TestService_GetVersion(t *testing.T) { } func TestService_Get_Collaborators(t *testing.T) { + ctxh := testingconfig.CreateAccountContext(t, cfg) _, _, eSrv := getServiceWithMockedLayers() - entity, _ := createCDWithEmbeddedEntity(t) + entity, _ := CreateEntityWithEmbedCD(t, ctxh, did, nil) err := testRepo().Create(accountID, entity.CurrentVersion(), entity) assert.NoError(t, err) - ctxh := testingconfig.CreateAccountContext(t, cfg) - _, err = eSrv.GetVersion(ctxh, entity.ID(), entity.CurrentVersion()) assert.NoError(t, err) @@ -484,12 +192,12 @@ func TestService_Get_Collaborators(t *testing.T) { } func TestService_Exists(t *testing.T) { + ctxh := testingconfig.CreateAccountContext(t, cfg) _, _, eSrv := getServiceWithMockedLayers() - entity, _ := createCDWithEmbeddedEntity(t) + entity, _ := CreateEntityWithEmbedCD(t, ctxh, did, nil) err := testRepo().Create(accountID, entity.CurrentVersion(), entity) assert.NoError(t, err) - ctxh := testingconfig.CreateAccountContext(t, cfg) exists := eSrv.Exists(ctxh, entity.CurrentVersion()) assert.True(t, exists, "entity should exist") @@ -508,8 +216,7 @@ func TestService_calculateDataRoot(t *testing.T) { assert.Contains(t, err.Error(), "unknown document type") // failed validator - entity, err = eSrv.DeriveFromCreatePayload(ctxh, testingdocuments.CreateEntityPayload()) - assert.NoError(t, err) + entity = InitEntity(t, did, CreateEntityPayload(t, nil)) v := documents.ValidatorFunc(func(_, _ documents.Model) error { return errors.New("validations fail") }) @@ -519,8 +226,7 @@ func TestService_calculateDataRoot(t *testing.T) { assert.Contains(t, err.Error(), "validations fail") // create failed - entity, err = eSrv.DeriveFromCreatePayload(ctxh, testingdocuments.CreateEntityPayload()) - assert.NoError(t, err) + entity, _ = CreateEntityWithEmbedCD(t, ctxh, did, nil) err = eSrv.repo.Create(accountID, entity.CurrentVersion(), entity) assert.NoError(t, err) idFactory := new(testingcommons.MockIdentityFactory) @@ -534,8 +240,7 @@ func TestService_calculateDataRoot(t *testing.T) { // success idFactory = new(testingcommons.MockIdentityFactory) idFactory.On("IdentityExists", mock.Anything).Return(true, nil).Once() - entity, err = eSrv.DeriveFromCreatePayload(ctxh, testingdocuments.CreateEntityPayload()) - assert.NoError(t, err) + entity, _ = CreateEntityWithEmbedCD(t, ctxh, did, nil) entity, err = eSrv.validateAndPersist(ctxh, nil, entity, CreateValidator(idFactory)) assert.NoError(t, err) assert.NotNil(t, entity) @@ -551,18 +256,15 @@ func setupRelationshipTesting(t *testing.T) (context.Context, documents.Model, * ctxh := testingconfig.CreateAccountContext(t, cfg) // create entity - entity, _ := createCDWithEmbeddedEntity(t) + entity, _ := CreateEntityWithEmbedCD(t, ctxh, did, nil) // create relationship - erData := &entitypb2.RelationshipData{ - EntityIdentifier: hexutil.Encode(entity.ID()), - OwnerIdentity: hexutil.Encode(dIDBytes), - TargetIdentity: hexutil.Encode(dIDBytes), + erData := entityrelationship.Data{ + EntityIdentifier: entity.ID(), + OwnerIdentity: &did, + TargetIdentity: &did, } - er := &entityrelationship.EntityRelationship{} - err := er.InitEntityRelationshipInput(ctxh, hexutil.Encode(entity.ID()), erData) - assert.NoError(t, err) - + er := entityrelationship.InitEntityRelationship(t, ctxh, erData) return ctxh, entity, er, idFactory, idService, repo } @@ -607,7 +309,7 @@ func setupRelationshipTesting(t *testing.T) (context.Context, documents.Model, * func TestService_GetEntityByRelationship_fail(t *testing.T) { // prepare a service with mocked layers - ctxh, entity, er, idFactory, idService, repo := setupRelationshipTesting(t) + ctxh, entity, er, idFactory, _, repo := setupRelationshipTesting(t) mockAnchor := &mockAnchorRepo{} docSrv := testingdocuments.MockService{} @@ -622,7 +324,7 @@ func TestService_GetEntityByRelationship_fail(t *testing.T) { repo, nil, nil, idFactory, - mockedERSrv, idService, mockAnchor, mockProcessor, nil) + mockedERSrv, mockAnchor, mockProcessor, nil) //entity relationship identifier not exists in db model, err := entitySrv.GetEntityByRelationship(ctxh, er.ID()) @@ -639,7 +341,7 @@ func TestService_GetEntityByRelationship_fail(t *testing.T) { repo, nil, nil, idFactory, - mockedERSrv, idService, mockAnchor, mockProcessor, nil) + mockedERSrv, mockAnchor, mockProcessor, nil) // pass entity id instead of er identifier model, err = entitySrv.GetEntityByRelationship(ctxh, entity.ID()) @@ -651,7 +353,7 @@ func TestService_GetEntityByRelationship_fail(t *testing.T) { func TestService_GetEntityByRelationship_requestP2P(t *testing.T) { // prepare a service with mocked layers - ctxh, entity, er, idFactory, idService, repo := setupRelationshipTesting(t) + ctxh, entity, er, idFactory, _, repo := setupRelationshipTesting(t) eID := entity.ID() erID := er.ID() @@ -688,7 +390,7 @@ func TestService_GetEntityByRelationship_requestP2P(t *testing.T) { repo, nil, nil, idFactory, - mockedERSrv, idService, mockAnchor, mockProcessor, func() documents.ValidatorGroup { + mockedERSrv, mockAnchor, mockProcessor, func() documents.ValidatorGroup { return documents.ValidatorGroup{} }) @@ -733,7 +435,7 @@ func TestService_CreateModel(t *testing.T) { payload.Data = validDataWithIdentity(t) srv.repo = testRepo() jm := testingjobs.MockJobManager{} - jm.On("ExecuteWithinJob", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(jobs.NilJobID(), make(chan bool), nil) + jm.On("ExecuteWithinJob", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(jobs.NilJobID(), make(chan error), nil) srv.jobManager = jm fact = new(testingcommons.MockIdentityFactory) fact.On("IdentityExists", mock.Anything).Return(true, nil) @@ -770,7 +472,7 @@ func TestService_UpdateModel(t *testing.T) { assert.True(t, errors.IsOfType(documents.ErrDocumentNotFound, err)) // payload invalid - e := createEntity(t) + e, _ := CreateEntityWithEmbedCD(t, ctxh, did, nil) err = testRepo().Create(did[:], e.ID(), e) assert.NoError(t, err) payload.DocumentID = e.ID() @@ -781,6 +483,7 @@ func TestService_UpdateModel(t *testing.T) { // validator failed payload.Data = validData(t) fact.On("IdentityExists", mock.Anything).Return(false, nil) + srv.factory = fact _, _, err = srv.UpdateModel(ctxh, payload) assert.Error(t, err) assert.True(t, errors.IsOfType(documents.ErrDocumentInvalid, err)) @@ -792,7 +495,7 @@ func TestService_UpdateModel(t *testing.T) { srv.factory = fact payload.Data = validDataWithIdentity(t) jm := testingjobs.MockJobManager{} - jm.On("ExecuteWithinJob", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(jobs.NilJobID(), make(chan bool), nil) + jm.On("ExecuteWithinJob", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(jobs.NilJobID(), make(chan error), nil) srv.jobManager = jm m, _, err := srv.UpdateModel(ctxh, payload) assert.NoError(t, err) @@ -801,3 +504,9 @@ func TestService_UpdateModel(t *testing.T) { jm.AssertExpectations(t) fact.AssertExpectations(t) } + +func TestService_ValidateError(t *testing.T) { + srv := service{} + err := srv.Validate(context.Background(), nil, nil) + assert.Error(t, err) +} diff --git a/documents/entity/test_entity.go b/documents/entity/test_entity.go new file mode 100644 index 000000000..7e691ba05 --- /dev/null +++ b/documents/entity/test_entity.go @@ -0,0 +1,185 @@ +// +build integration unit testworld + +package entity + +import ( + "context" + "encoding/json" + "testing" + + coredocumentpb "github.com/centrifuge/centrifuge-protobufs/gen/go/coredocument" + "github.com/centrifuge/go-centrifuge/contextutil" + "github.com/centrifuge/go-centrifuge/documents" + "github.com/centrifuge/go-centrifuge/identity" + "github.com/centrifuge/go-centrifuge/jobs" + testingidentity "github.com/centrifuge/go-centrifuge/testingutils/identity" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func (b Bootstrapper) TestBootstrap(context map[string]interface{}) error { + return b.Bootstrap(context) +} + +func (Bootstrapper) TestTearDown() error { + return nil +} + +type MockEntityRelationService struct { + documents.Service + mock.Mock +} + +func (m *MockEntityRelationService) GetCurrentVersion(ctx context.Context, documentID []byte) (documents.Model, error) { + args := m.Called(documentID) + return args.Get(0).(documents.Model), args.Error(1) +} + +func (m *MockEntityRelationService) GetVersion(ctx context.Context, documentID []byte, version []byte) (documents.Model, error) { + args := m.Called(documentID, version) + return args.Get(0).(documents.Model), args.Error(1) +} + +func (m *MockEntityRelationService) CreateProofs(ctx context.Context, documentID []byte, fields []string) (*documents.DocumentProof, error) { + args := m.Called(documentID, fields) + return args.Get(0).(*documents.DocumentProof), args.Error(1) +} + +func (m *MockEntityRelationService) CreateProofsForVersion(ctx context.Context, documentID, version []byte, fields []string) (*documents.DocumentProof, error) { + args := m.Called(documentID, version, fields) + return args.Get(0).(*documents.DocumentProof), args.Error(1) +} + +func (m *MockEntityRelationService) DeriveFromCoreDocument(cd coredocumentpb.CoreDocument) (documents.Model, error) { + args := m.Called(cd) + return args.Get(0).(documents.Model), args.Error(1) +} + +func (m *MockEntityRelationService) RequestDocumentSignature(ctx context.Context, model documents.Model, collaborator identity.DID) (*coredocumentpb.Signature, error) { + args := m.Called() + return args.Get(0).(*coredocumentpb.Signature), args.Error(1) +} + +func (m *MockEntityRelationService) ReceiveAnchoredDocument(ctx context.Context, model documents.Model, collaborator identity.DID) error { + args := m.Called() + return args.Error(0) +} + +func (m *MockEntityRelationService) Exists(ctx context.Context, documentID []byte) bool { + args := m.Called() + return args.Get(0).(bool) +} + +// GetEntityRelationships returns a list of the latest versions of the relevant entity relationship based on an entity id +func (m *MockEntityRelationService) GetEntityRelationships(ctx context.Context, entityID []byte) ([]documents.Model, error) { + args := m.Called(ctx, entityID) + rs, _ := args.Get(0).([]documents.Model) + return rs, args.Error(1) +} + +type MockService struct { + Service + mock.Mock +} + +func (m *MockService) Create(ctx context.Context, model documents.Model) (documents.Model, jobs.JobID, chan error, error) { + args := m.Called(ctx, model) + model, _ = args.Get(0).(documents.Model) + return model, contextutil.Job(ctx), nil, args.Error(2) +} + +func (m *MockService) GetCurrentVersion(ctx context.Context, documentID []byte) (documents.Model, error) { + args := m.Called(ctx, documentID) + data, _ := args.Get(0).(documents.Model) + return data, args.Error(1) +} + +func (m *MockService) GetVersion(ctx context.Context, documentID []byte, version []byte) (documents.Model, error) { + args := m.Called(ctx, documentID, version) + data, _ := args.Get(0).(documents.Model) + return data, args.Error(1) +} + +func (m *MockService) GetEntityByRelationship(ctx context.Context, rID []byte) (documents.Model, error) { + args := m.Called(ctx, rID) + doc, _ := args.Get(0).(documents.Model) + return doc, args.Error(1) +} + +func entityData(t *testing.T) []byte { + did, err := identity.NewDIDFromString("0xEA939D5C0494b072c51565b191eE59B5D34fbf79") + assert.NoError(t, err) + data := Data{ + Identity: &did, + LegalName: "Hello, world", + Addresses: []Address{ + { + Country: "Germany", + IsMain: true, + Label: "office", + }, + }, + Contacts: []Contact{ + { + Name: "John Doe", + Title: "Mr", + }, + }, + } + + d, err := json.Marshal(data) + assert.NoError(t, err) + return d +} + +func CreateEntityPayload(t *testing.T, collaborators []identity.DID) documents.CreatePayload { + if collaborators == nil { + collaborators = []identity.DID{testingidentity.GenerateRandomDID()} + } + return documents.CreatePayload{ + Scheme: Scheme, + Collaborators: documents.CollaboratorsAccess{ + ReadWriteCollaborators: collaborators, + }, + Data: entityData(t), + } +} + +func InitEntity(t *testing.T, did identity.DID, payload documents.CreatePayload) *Entity { + entity := new(Entity) + payload.Collaborators.ReadWriteCollaborators = append(payload.Collaborators.ReadWriteCollaborators, did) + assert.NoError(t, entity.DeriveFromCreatePayload(context.Background(), payload)) + return entity +} + +func CreateEntityWithEmbedCDWithPayload(t *testing.T, ctx context.Context, did identity.DID, payload documents.CreatePayload) (*Entity, coredocumentpb.CoreDocument) { + entity := new(Entity) + payload.Collaborators.ReadWriteCollaborators = append(payload.Collaborators.ReadWriteCollaborators, did) + err := entity.DeriveFromCreatePayload(ctx, payload) + assert.NoError(t, err) + entity.GetTestCoreDocWithReset() + _, err = entity.CalculateDataRoot() + assert.NoError(t, err) + sr, err := entity.CalculateSigningRoot() + assert.NoError(t, err) + // if acc errors out, just skip it + if ctx == nil { + ctx = context.Background() + } + acc, err := contextutil.Account(ctx) + if err == nil { + sig, err := acc.SignMsg(sr) + assert.NoError(t, err) + entity.AppendSignatures(sig) + } + _, err = entity.CalculateDocumentRoot() + assert.NoError(t, err) + cd, err := entity.PackCoreDocument() + assert.NoError(t, err) + return entity, cd +} + +func CreateEntityWithEmbedCD(t *testing.T, ctx context.Context, did identity.DID, collaborators []identity.DID) (*Entity, coredocumentpb.CoreDocument) { + payload := CreateEntityPayload(t, collaborators) + return CreateEntityWithEmbedCDWithPayload(t, ctx, did, payload) +} diff --git a/documents/entity/validator_test.go b/documents/entity/validator_test.go index d79a0cdc2..d01a241a2 100644 --- a/documents/entity/validator_test.go +++ b/documents/entity/validator_test.go @@ -7,6 +7,7 @@ import ( "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/go-centrifuge/testingutils/commons" + testingconfig "github.com/centrifuge/go-centrifuge/testingutils/config" "github.com/stretchr/testify/assert" ) @@ -29,7 +30,7 @@ func TestFieldValidator_Validate(t *testing.T) { // identity not created from the identity factory idFactory := new(testingcommons.MockIdentityFactory) - entity := createEntity(t) + entity, _ := CreateEntityWithEmbedCD(t, testingconfig.CreateAccountContext(t, cfg), did, nil) idFactory.On("IdentityExists", entity.Data.Identity).Return(false, nil).Once() fv = fieldValidator(idFactory) err = fv.Validate(nil, entity) diff --git a/documents/entityrelationship/bootstrapper.go b/documents/entityrelationship/bootstrapper.go index b5470f041..4496d6e28 100644 --- a/documents/entityrelationship/bootstrapper.go +++ b/documents/entityrelationship/bootstrapper.go @@ -76,6 +76,11 @@ func (Bootstrapper) Bootstrap(ctx map[string]interface{}) error { return errors.New("failed to register entity relationship service: %v", err) } + err = registry.Register(Scheme, srv) + if err != nil { + return errors.New("failed to register entity relationship service: %v", err) + } + ctx[BootstrappedEntityRelationshipService] = srv return nil diff --git a/documents/entityrelationship/model.go b/documents/entityrelationship/model.go index f684fd61f..bc8c9b541 100644 --- a/documents/entityrelationship/model.go +++ b/documents/entityrelationship/model.go @@ -2,6 +2,7 @@ package entityrelationship import ( "context" + "encoding/json" "reflect" "github.com/centrifuge/centrifuge-protobufs/documenttypes" @@ -10,118 +11,67 @@ import ( "github.com/centrifuge/go-centrifuge/documents" "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/go-centrifuge/identity" - "github.com/centrifuge/go-centrifuge/protobufs/gen/go/document" - entitypb2 "github.com/centrifuge/go-centrifuge/protobufs/gen/go/entity" + "github.com/centrifuge/go-centrifuge/utils/byteutils" "github.com/centrifuge/precise-proofs/proofs" "github.com/centrifuge/precise-proofs/proofs/proto" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" "github.com/golang/protobuf/proto" "github.com/golang/protobuf/ptypes/any" + "github.com/jinzhu/copier" ) const ( prefix string = "entity_relationship" - scheme = prefix + + // Scheme to identify entity relationship + Scheme = prefix + + // ErrEntityRelationshipUpdate is a sentinel error for update failure. + ErrEntityRelationshipUpdate = errors.Error("Entity relationship doesn't support updates.") ) // tree prefixes for specific documents use the second byte of a 4 byte slice by convention func compactPrefix() []byte { return []byte{0, 4, 0, 0} } -// EntityRelationship implements the documents.Model and keeps track of entity-relationship related fields and state. -type EntityRelationship struct { - *documents.CoreDocument - - // owner of the relationship - OwnerIdentity *identity.DID +// Data represents entity relationship data +type Data struct { + // Owner of the relationship + OwnerIdentity *identity.DID `json:"owner_identity" swaggertype:"primitive,string"` // Entity identifier - EntityIdentifier []byte + EntityIdentifier byteutils.HexBytes `json:"entity_identifier" swaggertype:"primitive,string"` // identity which will be granted access - TargetIdentity *identity.DID + TargetIdentity *identity.DID `json:"target_identity" swaggertype:"primitive,string"` } -// getRelationshipData returns the entity relationship data from the entity relationship model -func (e *EntityRelationship) getRelationshipData() *entitypb2.RelationshipData { - dids := identity.DIDsToStrings(e.OwnerIdentity, e.TargetIdentity) - eID := hexutil.Encode(e.EntityIdentifier) - return &entitypb2.RelationshipData{ - OwnerIdentity: dids[0], - TargetIdentity: dids[1], - EntityIdentifier: eID, - } +// EntityRelationship implements the documents.Model and keeps track of entity-relationship related fields and state. +type EntityRelationship struct { + *documents.CoreDocument + + Data Data `json:"data"` } // createP2PProtobuf returns Centrifuge protobuf-specific RelationshipData. func (e *EntityRelationship) createP2PProtobuf() *entitypb.EntityRelationship { - dids := identity.DIDsToBytes(e.OwnerIdentity, e.TargetIdentity) + d := e.Data + dids := identity.DIDsToBytes(d.OwnerIdentity, d.TargetIdentity) return &entitypb.EntityRelationship{ OwnerIdentity: dids[0], TargetIdentity: dids[1], - EntityIdentifier: e.EntityIdentifier, + EntityIdentifier: d.EntityIdentifier, } } -// InitEntityRelationshipInput initialize the model based on the received parameters from the rest api call -func (e *EntityRelationship) InitEntityRelationshipInput(ctx context.Context, entityID string, data *entitypb2.RelationshipData) error { - if err := e.initEntityRelationshipFromData(data); err != nil { - return err - } - - params := documentpb.AccessTokenParams{ - Grantee: data.TargetIdentity, - DocumentIdentifier: entityID, - } - - cd, err := documents.NewCoreDocumentWithAccessToken(ctx, compactPrefix(), params) - if err != nil { - return errors.New("failed to init core document: %v", err) - } - - e.CoreDocument = cd - return nil -} - -// PrepareNewVersion prepares new version from the old entity. -func (e *EntityRelationship) PrepareNewVersion(old documents.Model, data *entitypb2.RelationshipData, collaborators []string) error { - err := e.initEntityRelationshipFromData(data) - if err != nil { - return err - } - - oldCD := old.(*EntityRelationship).CoreDocument - e.CoreDocument, err = oldCD.PrepareNewVersion(compactPrefix(), documents.CollaboratorsAccess{}, nil) - if err != nil { - return err - } - - return nil -} - -// initEntityRelationshipFromData initialises an EntityRelationship from RelationshipData. -func (e *EntityRelationship) initEntityRelationshipFromData(data *entitypb2.RelationshipData) error { - dids, err := identity.StringsToDIDs(data.OwnerIdentity, data.TargetIdentity) - if err != nil { - return err - } - eID, err := hexutil.Decode(data.EntityIdentifier) - if err != nil { - return err - } - e.OwnerIdentity = dids[0] - e.TargetIdentity = dids[1] - e.EntityIdentifier = eID - return nil -} - // loadFromP2PProtobuf loads the Entity Relationship from Centrifuge protobuf. func (e *EntityRelationship) loadFromP2PProtobuf(entityRelationship *entitypb.EntityRelationship) error { dids, err := identity.BytesToDIDs(entityRelationship.OwnerIdentity, entityRelationship.TargetIdentity) if err != nil { return err } - e.OwnerIdentity = dids[0] - e.TargetIdentity = dids[1] - e.EntityIdentifier = entityRelationship.EntityIdentifier + var d Data + d.OwnerIdentity = dids[0] + d.TargetIdentity = dids[1] + d.EntityIdentifier = entityRelationship.EntityIdentifier + e.Data = d return nil } @@ -198,7 +148,11 @@ func (e *EntityRelationship) getDocumentDataTree() (tree *proofs.DocumentTree, e if e.CoreDocument == nil { return nil, errors.New("getDocumentDataTree error CoreDocument not set") } - t := e.CoreDocument.DefaultTreeWithPrefix(prefix, compactPrefix()) + t, err := e.CoreDocument.DefaultTreeWithPrefix(prefix, compactPrefix()) + if err != nil { + return nil, err + } + if err := t.AddLeavesFromDocument(eProto); err != nil { return nil, errors.New("getDocumentDataTree error %v", err) } @@ -272,7 +226,7 @@ func (e *EntityRelationship) CollaboratorCanUpdate(updated documents.Model, iden return errors.NewTypedError(documents.ErrDocumentInvalidType, errors.New("expecting an entity relationship but got %T", updated)) } - if !e.OwnerIdentity.Equal(identity) || !newEntityRelationship.OwnerIdentity.Equal(identity) { + if !e.Data.OwnerIdentity.Equal(identity) || !newEntityRelationship.Data.OwnerIdentity.Equal(identity) { return documents.ErrIdentityNotOwner } return nil @@ -302,10 +256,81 @@ func (e *EntityRelationship) DeleteAttribute(key documents.AttrKey, prepareNewVe // GetData returns entity relationship data func (e *EntityRelationship) GetData() interface{} { - return nil + return e.Data } // Scheme returns the entity relationship scheme. func (e *EntityRelationship) Scheme() string { - return scheme + return Scheme +} + +// loadData unmarshals json blob to Data. +func loadData(data []byte, d *Data) error { + err := json.Unmarshal(data, d) + if err != nil { + return err + } + + return nil +} + +// DeriveFromCreatePayload unpacks the entity relationship data from the Payload. +func (e *EntityRelationship) DeriveFromCreatePayload(ctx context.Context, payload documents.CreatePayload) error { + var d Data + if err := loadData(payload.Data, &d); err != nil { + return err + } + + params := documents.AccessTokenParams{ + Grantee: d.TargetIdentity.String(), + DocumentIdentifier: d.EntityIdentifier.String(), + } + + cd, err := documents.NewCoreDocumentWithAccessToken(ctx, compactPrefix(), params) + if err != nil { + return errors.New("failed to init core document: %v", err) + } + + e.CoreDocument = cd + e.Data = d + return nil +} + +// DeriveFromUpdatePayload is not implemented for entity relationship. +func (e *EntityRelationship) DeriveFromUpdatePayload(context.Context, documents.UpdatePayload) (documents.Model, error) { + return nil, ErrEntityRelationshipUpdate +} + +// Patch merges payload data into Document. +func (e *EntityRelationship) Patch(payload documents.UpdatePayload) error { + var d Data + err := copier.Copy(&d, &e.Data) + if err != nil { + return err + } + + if err := loadData(payload.Data, &d); err != nil { + return err + } + + ncd, err := e.CoreDocument.Patch(compactPrefix(), payload.Collaborators, payload.Attributes) + if err != nil { + return err + } + + e.Data = d + e.CoreDocument = ncd + return nil +} + +// revokeRelationship revokes a relationship by deleting the access token in the Entity +func (e *EntityRelationship) revokeRelationship(old *EntityRelationship, grantee identity.DID) error { + e.Data = old.Data + cd, err := old.DeleteAccessToken(grantee) + if err != nil { + return err + } + + e.CoreDocument = cd + return nil } diff --git a/documents/entityrelationship/model_test.go b/documents/entityrelationship/model_test.go index d6a5dbbec..b8a92192c 100644 --- a/documents/entityrelationship/model_test.go +++ b/documents/entityrelationship/model_test.go @@ -3,6 +3,8 @@ package entityrelationship import ( + "context" + "crypto/sha256" "encoding/json" "fmt" "os" @@ -15,7 +17,6 @@ import ( "github.com/centrifuge/go-centrifuge/bootstrap/bootstrappers/testlogging" "github.com/centrifuge/go-centrifuge/config" "github.com/centrifuge/go-centrifuge/config/configstore" - "github.com/centrifuge/go-centrifuge/contextutil" "github.com/centrifuge/go-centrifuge/documents" "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/go-centrifuge/ethereum" @@ -23,7 +24,6 @@ import ( "github.com/centrifuge/go-centrifuge/identity/ideth" "github.com/centrifuge/go-centrifuge/jobs" "github.com/centrifuge/go-centrifuge/p2p" - "github.com/centrifuge/go-centrifuge/protobufs/gen/go/entity" "github.com/centrifuge/go-centrifuge/queue" "github.com/centrifuge/go-centrifuge/storage/leveldb" "github.com/centrifuge/go-centrifuge/testingutils/config" @@ -31,6 +31,7 @@ import ( "github.com/centrifuge/go-centrifuge/testingutils/identity" "github.com/centrifuge/go-centrifuge/testingutils/testingjobs" "github.com/centrifuge/go-centrifuge/utils" + "github.com/centrifuge/go-centrifuge/utils/byteutils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/golang/protobuf/ptypes/any" @@ -41,8 +42,7 @@ import ( var ctx = map[string]interface{}{} var cfg config.Configuration var ( - did = testingidentity.GenerateRandomDID() - entityID = hexutil.Encode(utils.RandomSlice(32)) + did = testingidentity.GenerateRandomDID() ) func TestMain(m *testing.M) { @@ -51,7 +51,7 @@ func TestMain(m *testing.M) { ctx[ethereum.BootstrappedEthereumClient] = ethClient jobMan := &testingjobs.MockJobManager{} ctx[jobs.BootstrappedService] = jobMan - done := make(chan bool) + done := make(chan error) jobMan.On("ExecuteWithinJob", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(jobs.NilJobID(), done, nil) ctx[bootstrap.BootstrappedInvoiceUnpaid] = new(testingdocuments.MockRegistry) ibootstrappers := []bootstrap.TestBootstrapper{ @@ -75,49 +75,18 @@ func TestMain(m *testing.M) { os.Exit(result) } -func CreateRelationshipData(t *testing.T) *entitypb.RelationshipData { - ctxh := testingconfig.CreateAccountContext(t, cfg) - selfDID, err := contextutil.AccountDID(ctxh) - assert.NoError(t, err) - return &entitypb.RelationshipData{ - OwnerIdentity: selfDID.String(), - TargetIdentity: "0x5F9132e0F92952abCb154A9b34563891ffe1AAcb", - EntityIdentifier: hexutil.Encode(utils.RandomSlice(32)), - } -} - func TestEntityRelationship_PackCoreDocument(t *testing.T) { ctxh := testingconfig.CreateAccountContext(t, cfg) - er := new(EntityRelationship) - assert.NoError(t, er.InitEntityRelationshipInput(ctxh, entityID, CreateRelationshipData(t))) + er := CreateRelationship(t, ctxh) cd, err := er.PackCoreDocument() assert.NoError(t, err) assert.NotNil(t, cd.EmbeddedData) } -func TestEntityRelationship_PrepareNewVersion(t *testing.T) { - ctxh := testingconfig.CreateAccountContext(t, cfg) - selfDID, err := contextutil.AccountDID(ctxh) - assert.NoError(t, err) - - m, _ := createCDWithEmbeddedEntityRelationship(t) - old := m.(*EntityRelationship) - data := &entitypb.RelationshipData{ - OwnerIdentity: selfDID.String(), - TargetIdentity: "random string", - } - err = old.PrepareNewVersion(old, data, nil) - assert.Error(t, err) - - err = old.PrepareNewVersion(old, CreateRelationshipData(t), nil) - assert.NoError(t, err) -} - func TestEntityRelationship_JSON(t *testing.T) { - er := new(EntityRelationship) ctxh := testingconfig.CreateAccountContext(t, cfg) - assert.NoError(t, er.InitEntityRelationshipInput(ctxh, entityID, CreateRelationshipData(t))) + er, _ := CreateCDWithEmbeddedEntityRelationship(t, ctxh) cd, err := er.PackCoreDocument() assert.NoError(t, err) @@ -155,56 +124,26 @@ func TestEntityRelationship_UnpackCoreDocument(t *testing.T) { assert.Error(t, err) // successful - entityRelationship, cd := createCDWithEmbeddedEntityRelationship(t) + entityRelationship, cd := CreateCDWithEmbeddedEntityRelationship(t, testingconfig.CreateAccountContext(t, cfg)) err = model.UnpackCoreDocument(cd) assert.NoError(t, err) - assert.Equal(t, model.getRelationshipData(), model.getRelationshipData(), entityRelationship.(*EntityRelationship).getRelationshipData()) + assert.Equal(t, model.Data, entityRelationship.(*EntityRelationship).Data) assert.Equal(t, model.ID(), entityRelationship.ID()) assert.Equal(t, model.CurrentVersion(), entityRelationship.CurrentVersion()) assert.Equal(t, model.PreviousVersion(), entityRelationship.PreviousVersion()) } func TestEntityRelationship_getRelationshipData(t *testing.T) { - entityRelationship := testingdocuments.CreateRelationship() - er := new(EntityRelationship) - err := er.loadFromP2PProtobuf(entityRelationship) - assert.NoError(t, err) - - data := er.getRelationshipData() + e, _ := CreateCDWithEmbeddedEntityRelationship(t, testingconfig.CreateAccountContext(t, cfg)) + er := e.(*EntityRelationship) + data := er.GetData().(Data) assert.NotNil(t, data, "entity relationship data should not be nil") - assert.Equal(t, data.OwnerIdentity, er.OwnerIdentity.String()) - assert.Equal(t, data.TargetIdentity, er.TargetIdentity.String()) -} - -func TestEntityRelationship_InitEntityInput(t *testing.T) { - ctxh := testingconfig.CreateAccountContext(t, cfg) - selfDID, err := contextutil.AccountDID(ctxh) - assert.NoError(t, err) - // successful init - data := &entitypb.RelationshipData{ - OwnerIdentity: selfDID.String(), - TargetIdentity: testingidentity.GenerateRandomDID().String(), - EntityIdentifier: hexutil.Encode(utils.RandomSlice(32)), - } - e := new(EntityRelationship) - err = e.InitEntityRelationshipInput(ctxh, entityID, data) - assert.NoError(t, err) - - // invalid did - e = new(EntityRelationship) - data.TargetIdentity = "some random string" - err = e.InitEntityRelationshipInput(ctxh, entityID, data) - assert.Error(t, err) - assert.Contains(t, err.Error(), "malformed address provided") + assert.Equal(t, data.OwnerIdentity, er.Data.OwnerIdentity) + assert.Equal(t, data.TargetIdentity, er.Data.TargetIdentity) } func TestEntityRelationship_calculateDataRoot(t *testing.T) { - ctxh := testingconfig.CreateAccountContext(t, cfg) - m := new(EntityRelationship) - err := m.InitEntityRelationshipInput(ctxh, entityID, CreateRelationshipData(t)) - assert.NoError(t, err) - m.GetTestCoreDocWithReset() - + m, _ := CreateCDWithEmbeddedEntityRelationship(t, testingconfig.CreateAccountContext(t, cfg)) dr, err := m.CalculateDataRoot() assert.NoError(t, err) assert.False(t, utils.IsEmptyByteSlice(dr)) @@ -223,22 +162,23 @@ func TestEntityRelationship_CreateNFTProofs(t *testing.T) { } func TestEntityRelationship_CreateProofs(t *testing.T) { - e := createEntityRelationship(t) + er, _ := CreateCDWithEmbeddedEntityRelationship(t, testingconfig.CreateAccountContext(t, cfg)) + e := er.(*EntityRelationship) rk := e.Document.Roles[0].RoleKey pf := fmt.Sprintf(documents.CDTreePrefix+".roles[%s].collaborators[0]", hexutil.Encode(rk)) proof, err := e.CreateProofs([]string{"entity_relationship.owner_identity", pf, documents.CDTreePrefix + ".document_type"}) assert.NoError(t, err) assert.NotNil(t, proof) - tree, err := e.DocumentRootTree() + signingRoot, err := e.CalculateSigningRoot() assert.NoError(t, err) // Validate entity_number - valid, err := tree.ValidateProof(proof[0]) + valid, err := documents.ValidateProof(proof[0], signingRoot, sha256.New()) assert.Nil(t, err) assert.True(t, valid) // Validate roles - valid, err = tree.ValidateProof(proof[1]) + valid, err = documents.ValidateProof(proof[1], signingRoot, sha256.New()) assert.Nil(t, err) assert.True(t, valid) @@ -248,44 +188,31 @@ func TestEntityRelationship_CreateProofs(t *testing.T) { assert.True(t, e.AccountCanRead(acc)) // Validate document_type - valid, err = tree.ValidateProof(proof[2]) + valid, err = documents.ValidateProof(proof[2], signingRoot, sha256.New()) assert.Nil(t, err) assert.True(t, valid) } -func createEntityRelationship(t *testing.T) *EntityRelationship { - ctxh := testingconfig.CreateAccountContext(t, cfg) - e := new(EntityRelationship) - err := e.InitEntityRelationshipInput(ctxh, entityID, CreateRelationshipData(t)) - assert.NoError(t, err) - e.GetTestCoreDocWithReset() - _, err = e.CalculateDataRoot() - assert.NoError(t, err) - _, err = e.CalculateSigningRoot() - assert.NoError(t, err) - _, err = e.CalculateDocumentRoot() - assert.NoError(t, err) - return e -} - func TestEntityRelationship_createProofsFieldDoesNotExist(t *testing.T) { - e := createEntityRelationship(t) + e, _ := CreateCDWithEmbeddedEntityRelationship(t, testingconfig.CreateAccountContext(t, cfg)) _, err := e.CreateProofs([]string{"nonexisting"}) assert.Error(t, err) } func TestEntityRelationship_GetDocumentID(t *testing.T) { - e := createEntityRelationship(t) + er, _ := CreateCDWithEmbeddedEntityRelationship(t, testingconfig.CreateAccountContext(t, cfg)) + e := er.(*EntityRelationship) assert.Equal(t, e.CoreDocument.ID(), e.ID()) } func TestEntityRelationship_GetDocumentType(t *testing.T) { - e := createEntityRelationship(t) - assert.Equal(t, documenttypes.EntityRelationshipDataTypeUrl, e.DocumentType()) + er, _ := CreateCDWithEmbeddedEntityRelationship(t, testingconfig.CreateAccountContext(t, cfg)) + assert.Equal(t, documenttypes.EntityRelationshipDataTypeUrl, er.DocumentType()) } func TestEntityRelationship_getDocumentDataTree(t *testing.T) { - e := createEntityRelationship(t) + er, _ := CreateCDWithEmbeddedEntityRelationship(t, testingconfig.CreateAccountContext(t, cfg)) + e := er.(*EntityRelationship) tree, err := e.getDocumentDataTree() assert.Nil(t, err, "tree should be generated without error") _, leaf := tree.GetLeafByProperty("entity_relationship.owner_identity") @@ -294,31 +221,23 @@ func TestEntityRelationship_getDocumentDataTree(t *testing.T) { } func TestEntityRelationship_CollaboratorCanUpdate(t *testing.T) { - er := createEntityRelationship(t) - id1, err := identity.NewDIDFromString("0xed03Fa80291fF5DDC284DE6b51E716B130b05e20") - assert.NoError(t, err) + er, _ := CreateCDWithEmbeddedEntityRelationship(t, testingconfig.CreateAccountContext(t, cfg)) + id := testingidentity.GenerateRandomDID() // wrong type - err = er.CollaboratorCanUpdate(new(mockModel), id1) + err := er.CollaboratorCanUpdate(new(mockModel), id) assert.Error(t, err) // update doc - assert.NoError(t, testEntityRepo().Create(id1[:], er.CurrentVersion(), er)) - model, err := testEntityRepo().Get(id1[:], er.CurrentVersion()) - assert.NoError(t, err) + assert.NoError(t, testEntityRepo().Create(did[:], er.CurrentVersion(), er)) // attempted updater is not owner of the relationship - oldRelationship := model - assert.NoError(t, err) - err = er.CollaboratorCanUpdate(oldRelationship, id1) + err = er.CollaboratorCanUpdate(er, id) assert.Error(t, err) assert.Contains(t, err.Error(), "identity attempting to update the document does not own this entity relationship") // attempted updater is owner of the relationship - ctxh := testingconfig.CreateAccountContext(t, cfg) - selfDID, err := contextutil.AccountDID(ctxh) - assert.NoError(t, err) - err = er.CollaboratorCanUpdate(oldRelationship, selfDID) + err = er.CollaboratorCanUpdate(er, *er.GetData().(Data).OwnerIdentity) assert.NoError(t, err) } @@ -355,28 +274,11 @@ func testEntityRepo() repository { return testRepoGlobal } -func createCDWithEmbeddedEntityRelationship(t *testing.T) (documents.Model, coredocumentpb.CoreDocument) { - ctxh := testingconfig.CreateAccountContext(t, cfg) - e := new(EntityRelationship) - err := e.InitEntityRelationshipInput(ctxh, entityID, CreateRelationshipData(t)) - assert.NoError(t, err) - e.GetTestCoreDocWithReset() - _, err = e.CalculateDataRoot() - assert.NoError(t, err) - _, err = e.CalculateSigningRoot() - assert.NoError(t, err) - _, err = e.CalculateDocumentRoot() - assert.NoError(t, err) - cd, err := e.PackCoreDocument() - assert.NoError(t, err) - return e, cd -} - func TestEntityRelationship_AddAttributes(t *testing.T) { - e, _ := createCDWithEmbeddedEntityRelationship(t) + e, _ := CreateCDWithEmbeddedEntityRelationship(t, testingconfig.CreateAccountContext(t, cfg)) label := "some key" value := "some value" - attr, err := documents.NewAttribute(label, documents.AttrString, value) + attr, err := documents.NewStringAttribute(label, documents.AttrString, value) assert.NoError(t, err) // success @@ -395,10 +297,10 @@ func TestEntityRelationship_AddAttributes(t *testing.T) { } func TestEntityRelationship_DeleteAttribute(t *testing.T) { - e, _ := createCDWithEmbeddedEntityRelationship(t) + e, _ := CreateCDWithEmbeddedEntityRelationship(t, testingconfig.CreateAccountContext(t, cfg)) label := "some key" value := "some value" - attr, err := documents.NewAttribute(label, documents.AttrString, value) + attr, err := documents.NewStringAttribute(label, documents.AttrString, value) assert.NoError(t, err) // failed @@ -411,3 +313,122 @@ func TestEntityRelationship_DeleteAttribute(t *testing.T) { assert.NoError(t, e.DeleteAttribute(attr.Key, true)) assert.False(t, e.AttributeExists(attr.Key)) } + +func invalidData(t *testing.T) []byte { + m := map[string]string{ + "target_identity": "", + } + + d, err := json.Marshal(m) + assert.NoError(t, err) + return d +} + +func validData(t *testing.T, self identity.DID) []byte { + return validDataWithTargetDID(t, self, testingidentity.GenerateRandomDID()) +} + +func validDataWithTargetDID(t *testing.T, self, target identity.DID) []byte { + m := map[string]string{ + "target_identity": target.String(), + "owner_identity": self.String(), + "entity_identifier": byteutils.HexBytes(utils.RandomSlice(32)).String(), + } + + d, err := json.Marshal(m) + assert.NoError(t, err) + return d +} + +func TestEntityRelationship_loadData(t *testing.T) { + e := new(EntityRelationship) + + // invalid data + d := invalidData(t) + err := loadData(d, &e.Data) + assert.Error(t, err) + + d = validData(t, testingidentity.GenerateRandomDID()) + err = loadData(d, &e.Data) + assert.NoError(t, err) +} + +func TestEntityRelationship_DeriveFromCreatePayload(t *testing.T) { + e := new(EntityRelationship) + var payload documents.CreatePayload + ctx := context.Background() + + // invalid data + payload.Data = invalidData(t) + err := e.DeriveFromCreatePayload(ctx, payload) + assert.Error(t, err) + + // missing account context + payload.Data = validData(t, did) + err = e.DeriveFromCreatePayload(ctx, payload) + assert.Error(t, err) + assert.Contains(t, err.Error(), documents.ErrDocumentConfigAccountID.Error()) + + // success + ctx = testingconfig.CreateAccountContext(t, cfg) + err = e.DeriveFromCreatePayload(ctx, payload) + assert.NoError(t, err) +} + +func TestEntityRelationship_DeriveFromUpdatePayload(t *testing.T) { + e := new(EntityRelationship) + _, err := e.DeriveFromUpdatePayload(context.Background(), documents.UpdatePayload{}) + assert.Error(t, err) + assert.True(t, errors.IsOfType(ErrEntityRelationshipUpdate, err)) +} + +func TestEntityRelationship_Patch(t *testing.T) { + e := CreateRelationship(t, testingconfig.CreateAccountContext(t, cfg)) + + // invalid data + d := invalidData(t) + payload := documents.UpdatePayload{CreatePayload: documents.CreatePayload{Data: d}} + err := e.Patch(payload) + assert.Error(t, err) + + // core doc patch failed + e.CoreDocument.Status = documents.Committed + self := did + target := testingidentity.GenerateRandomDID() + assert.NotEqual(t, e.Data.TargetIdentity, &target) + payload.Data = validDataWithTargetDID(t, self, target) + err = e.Patch(payload) + assert.Error(t, err) + assert.True(t, errors.IsOfType(documents.ErrDocumentNotInAllowedState, err)) + + // success + assert.NotEqual(t, e.Data.TargetIdentity, &target) + e.CoreDocument.Status = documents.Pending + err = e.Patch(payload) + assert.NoError(t, err) + assert.Equal(t, e.Data.TargetIdentity, &target) + assert.Equal(t, e.Data.OwnerIdentity, &self) +} + +func TestEntityRelationship_revokeRelationship(t *testing.T) { + old, _ := CreateCDWithEmbeddedEntityRelationship(t, testingconfig.CreateAccountContext(t, cfg)) + e := old.(*EntityRelationship) + er := new(EntityRelationship) + + // failed to remove token + id := testingidentity.GenerateRandomDID() + docID := utils.RandomSlice(32) + payload := documents.AccessTokenParams{ + Grantee: id.String(), + DocumentIdentifier: hexutil.Encode(docID), + } + err := er.revokeRelationship(e, id) + assert.Error(t, err) + assert.True(t, errors.IsOfType(documents.ErrAccessTokenNotFound, err)) + + // success + cd, err := e.AddAccessToken(testingconfig.CreateAccountContext(t, cfg), payload) + e.CoreDocument = cd + err = er.revokeRelationship(e, id) + assert.NoError(t, err) +} diff --git a/documents/entityrelationship/repository.go b/documents/entityrelationship/repository.go index 46cde8051..651dd3c97 100644 --- a/documents/entityrelationship/repository.go +++ b/documents/entityrelationship/repository.go @@ -48,8 +48,8 @@ func (r *repo) FindEntityRelationshipIdentifier(entityIdentifier []byte, ownerDI if !ok { continue } - if bytes.Equal(e.EntityIdentifier, entityIdentifier) && targetDID.Equal(*e.TargetIdentity) { - return e.Document.DocumentIdentifier, nil + if bytes.Equal(e.Data.EntityIdentifier, entityIdentifier) && targetDID.Equal(*e.Data.TargetIdentity) { + return e.ID(), nil } } return nil, documents.ErrDocumentNotFound @@ -73,7 +73,7 @@ func (r *repo) ListAllRelationships(entityIdentifier []byte, ownerDID identity.D continue } _, found := relationships[string(e.Document.DocumentIdentifier)] - if bytes.Equal(e.EntityIdentifier, entityIdentifier) && !found { + if bytes.Equal(e.Data.EntityIdentifier, entityIdentifier) && !found { relationships[string(e.Document.DocumentIdentifier)] = e.Document.DocumentIdentifier } } diff --git a/documents/entityrelationship/repository_test.go b/documents/entityrelationship/repository_test.go index 0460a1f77..7ae574446 100644 --- a/documents/entityrelationship/repository_test.go +++ b/documents/entityrelationship/repository_test.go @@ -5,11 +5,9 @@ package entityrelationship import ( "testing" - "github.com/centrifuge/go-centrifuge/identity" "github.com/centrifuge/go-centrifuge/testingutils/config" - "github.com/centrifuge/go-centrifuge/testingutils/documents" "github.com/centrifuge/go-centrifuge/testingutils/identity" - "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/centrifuge/go-centrifuge/utils" "github.com/stretchr/testify/assert" ) @@ -20,38 +18,27 @@ func TestRepo_FindEntityRelationshipIdentifier(t *testing.T) { assert.NotNil(t, repo) // no relationships in repo - rp := testingdocuments.CreateRelationshipPayload() - id, err := hexutil.Decode(rp.DocumentId) - assert.NoError(t, err) - - tID, err := identity.StringsToDIDs(rp.TargetIdentity) - assert.NoError(t, err) - - _, err = repo.FindEntityRelationshipIdentifier(id, did, *tID[0]) + er := CreateRelationship(t, ctxh) + _, err := repo.FindEntityRelationshipIdentifier(er.Data.EntityIdentifier, did, *er.Data.TargetIdentity) assert.Error(t, err) assert.Contains(t, err.Error(), "document not found in the system database") - // create relationships - m, err := service{}.DeriveFromCreatePayload(ctxh, rp) - assert.NoError(t, err) - - err = repo.Create(did[:], m.ID(), m) - assert.NoError(t, err) - - rp.TargetIdentity = testingidentity.GenerateRandomDID().String() - m2, err := service{}.DeriveFromCreatePayload(ctxh, rp) + err = repo.Create(did[:], er.ID(), er) assert.NoError(t, err) + tid := testingidentity.GenerateRandomDID() + m2 := CreateRelationship(t, ctxh) + m2.Data.TargetIdentity = &tid err = repo.Create(did[:], m2.ID(), m2) assert.NoError(t, err) // attempt to get relationships - r, err := repo.FindEntityRelationshipIdentifier(id, did, *tID[0]) + r, err := repo.FindEntityRelationshipIdentifier(m2.Data.EntityIdentifier, did, tid) assert.NoError(t, err) - assert.Equal(t, r, m.CurrentVersion()) + assert.Equal(t, r, m2.CurrentVersion()) // throws err if relationship not found in the repo - r, err = repo.FindEntityRelationshipIdentifier(id, testingidentity.GenerateRandomDID(), *tID[0]) + r, err = repo.FindEntityRelationshipIdentifier(er.ID(), testingidentity.GenerateRandomDID(), tid) assert.Error(t, err) assert.Contains(t, err.Error(), "document not found in the system database") } @@ -62,39 +49,17 @@ func TestRepo_ListAllRelationships(t *testing.T) { repo := testEntityRepo() assert.NotNil(t, repo) - rp := testingdocuments.CreateRelationshipPayload() - id, err := hexutil.Decode(rp.DocumentId) - assert.NoError(t, err) - // no relationships in repo returns a nil map + id := utils.RandomSlice(32) r, err := repo.ListAllRelationships(id, did) assert.Equal(t, r, map[string][]byte{}) // create relationships - m, err := service{}.DeriveFromCreatePayload(ctxh, rp) - assert.NoError(t, err) - + m := CreateRelationship(t, ctxh) + m.Data.EntityIdentifier = id err = repo.Create(did[:], m.ID(), m) assert.NoError(t, err) - rp.TargetIdentity = testingidentity.GenerateRandomDID().String() - m2, err := service{}.DeriveFromCreatePayload(ctxh, rp) - assert.NoError(t, err) - - err = repo.Create(did[:], m2.ID(), m2) - assert.NoError(t, err) - - rp.TargetIdentity = testingidentity.GenerateRandomDID().String() - m3, err := service{}.DeriveFromCreatePayload(ctxh, rp) - assert.NoError(t, err) - - err = repo.Create(did[:], m3.ID(), m3) - assert.NoError(t, err) - - // attempt to get relationships - id, err = hexutil.Decode(rp.DocumentId) - assert.NoError(t, err) - r, err = repo.ListAllRelationships(id, did) - assert.Len(t, r, 3) + assert.Len(t, r, 1) } diff --git a/documents/entityrelationship/service.go b/documents/entityrelationship/service.go index 4d4d8d0d6..bee3aa9cf 100644 --- a/documents/entityrelationship/service.go +++ b/documents/entityrelationship/service.go @@ -2,6 +2,7 @@ package entityrelationship import ( "context" + "encoding/json" "github.com/centrifuge/centrifuge-protobufs/gen/go/coredocument" "github.com/centrifuge/go-centrifuge/anchors" @@ -10,28 +11,13 @@ import ( "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/go-centrifuge/identity" "github.com/centrifuge/go-centrifuge/jobs" - "github.com/centrifuge/go-centrifuge/protobufs/gen/go/document" - "github.com/centrifuge/go-centrifuge/protobufs/gen/go/entity" "github.com/centrifuge/go-centrifuge/queue" - "github.com/ethereum/go-ethereum/common/hexutil" ) // Service defines specific functions for entity type Service interface { documents.Service - // DeriveFromCreatePayload derives Entity Relationship from RelationshipPayload - DeriveFromCreatePayload(ctx context.Context, payload *entitypb.RelationshipPayload) (documents.Model, error) - - // DeriveFromUpdatePayload derives a revoked entity relationship model from RelationshipPayload - DeriveFromUpdatePayload(ctx context.Context, payload *entitypb.RelationshipPayload) (documents.Model, error) - - // DeriveEntityRelationshipData returns the entity relationship data as client data - DeriveEntityRelationshipData(relationship documents.Model) (*entitypb.RelationshipData, error) - - // DeriveEntityRelationshipResponse returns the entity relationship model in our standard client format - DeriveEntityRelationshipResponse(relationship documents.Model) (*entitypb.RelationshipResponse, error) - // GetEntityRelationships returns a list of the latest versions of the relevant entity relationship based on an entity id GetEntityRelationships(ctx context.Context, entityID []byte) ([]documents.Model, error) } @@ -77,30 +63,6 @@ func (s service) DeriveFromCoreDocument(cd coredocumentpb.CoreDocument) (documen return er, nil } -// DeriveFromCreatePayload initializes the model with parameters provided from the rest-api call -func (s service) DeriveFromCreatePayload(ctx context.Context, payload *entitypb.RelationshipPayload) (documents.Model, error) { - if payload == nil { - return nil, documents.ErrPayloadNil - } - - er := new(EntityRelationship) - selfDID, err := contextutil.AccountDID(ctx) - if err != nil { - return nil, err - } - owner := selfDID.String() - rd := &entitypb.RelationshipData{ - OwnerIdentity: owner, - TargetIdentity: payload.TargetIdentity, - EntityIdentifier: payload.DocumentId, - } - if err = er.InitEntityRelationshipInput(ctx, payload.DocumentId, rd); err != nil { - return nil, errors.NewTypedError(documents.ErrDocumentInvalid, err) - } - - return er, nil -} - // validateAndPersist validates the document, calculates the data root, and persists to DB func (s service) validateAndPersist(ctx context.Context, old, new documents.Model, validator documents.Validator) (documents.Model, error) { selfDID, err := contextutil.AccountDID(ctx) @@ -130,7 +92,7 @@ func (s service) validateAndPersist(ctx context.Context, old, new documents.Mode // Create takes an entity relationship model and does required validation checks, tries to persist to DB // For Entity Relationships, Create encompasses the Share functionality from the Entity Client API endpoint -func (s service) Create(ctx context.Context, relationship documents.Model) (documents.Model, jobs.JobID, chan bool, error) { +func (s service) Create(ctx context.Context, relationship documents.Model) (documents.Model, jobs.JobID, chan error, error) { selfDID, err := contextutil.AccountDID(ctx) if err != nil { return nil, jobs.NilJobID(), nil, errors.NewTypedError(documents.ErrDocumentConfigAccountID, err) @@ -151,7 +113,7 @@ func (s service) Create(ctx context.Context, relationship documents.Model) (docu // Update finds the old document, validates the new version and persists the updated document // For Entity Relationships, Update encompasses the Revoke functionality from the Entity Client API endpoint -func (s service) Update(ctx context.Context, updated documents.Model) (documents.Model, jobs.JobID, chan bool, error) { +func (s service) Update(ctx context.Context, updated documents.Model) (documents.Model, jobs.JobID, chan error, error) { selfDID, err := contextutil.AccountDID(ctx) if err != nil { return nil, jobs.NilJobID(), nil, errors.NewTypedError(documents.ErrDocumentConfigAccountID, err) @@ -175,103 +137,111 @@ func (s service) Update(ctx context.Context, updated documents.Model) (documents return updated, jobID, done, nil } -// DeriveEntityRelationshipResponse returns create response from entity relationship model -func (s service) DeriveEntityRelationshipResponse(model documents.Model) (*entitypb.RelationshipResponse, error) { - data, err := s.DeriveEntityRelationshipData(model) - if err != nil { - return nil, err +// GetEntityRelationships returns the latest versions of the entity relationships that involve the entityID passed in +func (s service) GetEntityRelationships(ctx context.Context, entityID []byte) ([]documents.Model, error) { + var relationships []documents.Model + if entityID == nil { + return nil, documents.ErrPayloadNil } - h := &documentpb.ResponseHeader{ - DocumentId: hexutil.Encode(model.ID()), - VersionId: hexutil.Encode(model.CurrentVersion()), + selfDID, err := contextutil.AccountDID(ctx) + if err != nil { + return nil, errors.New("failed to get self ID") } - return &entitypb.RelationshipResponse{ - Header: h, - Relationship: []*entitypb.RelationshipData{data}, - }, nil + relevant, err := s.repo.ListAllRelationships(entityID, selfDID) + if err != nil { + return nil, err + } -} + for _, v := range relevant { + r, err := s.GetCurrentVersion(ctx, v) + if err != nil { + return nil, err + } + relationships = append(relationships, r) + } -// DeriveEntityRelationshipData returns the relationship data from an entity relationship model -func (s service) DeriveEntityRelationshipData(model documents.Model) (*entitypb.RelationshipData, error) { - er, ok := model.(*EntityRelationship) - if !ok { - return nil, documents.ErrDocumentInvalidType + if relationships == nil { + return nil, nil } - return er.getRelationshipData(), nil + return relationships, nil } -// DeriveFromUpdatePayload returns a new version of the indicated Entity Relationship with a deleted access token -func (s service) DeriveFromUpdatePayload(ctx context.Context, payload *entitypb.RelationshipPayload) (documents.Model, error) { - if payload == nil { - return nil, documents.ErrPayloadNil +// CreateModel creates entity relationship from the payload, validates, persists, and returns the document. +func (s service) CreateModel(ctx context.Context, payload documents.CreatePayload) (documents.Model, jobs.JobID, error) { + e := new(EntityRelationship) + if err := e.DeriveFromCreatePayload(ctx, payload); err != nil { + return nil, jobs.NilJobID(), errors.NewTypedError(documents.ErrDocumentInvalid, err) } - eID, err := hexutil.Decode(payload.DocumentId) + // validate invoice + err := CreateValidator(s.factory).Validate(nil, e) if err != nil { - return nil, err + return nil, jobs.NilJobID(), errors.NewTypedError(documents.ErrDocumentInvalid, err) } - did, err := identity.StringsToDIDs(payload.TargetIdentity) + // we use CurrentVersion as the id since that will be unique across multiple versions of the same document + did := *e.Data.OwnerIdentity + err = s.repo.Create(did[:], e.CurrentVersion(), e) if err != nil { - return nil, err + return nil, jobs.NilJobID(), errors.NewTypedError(documents.ErrDocumentPersistence, err) } - selfDID, err := contextutil.AccountDID(ctx) + jobID := contextutil.Job(ctx) + jobID, _, err = documents.CreateAnchorJob(ctx, s.jobManager, s.queueSrv, did, jobID, e.CurrentVersion()) + return e, jobID, err +} + +// UpdateModel revokes the entity relationship of a target identity. +func (s service) UpdateModel(ctx context.Context, payload documents.UpdatePayload) (documents.Model, jobs.JobID, error) { + var data Data + err := json.Unmarshal(payload.Data, &data) if err != nil { - return nil, errors.New("failed to get self ID") + return nil, jobs.NilJobID(), errors.NewTypedError(documents.ErrDocumentInvalid, err) } - id, err := s.repo.FindEntityRelationshipIdentifier(eID, selfDID, *did[0]) + id, err := s.repo.FindEntityRelationshipIdentifier(data.EntityIdentifier, *data.OwnerIdentity, *data.TargetIdentity) if err != nil { - return nil, err + return nil, jobs.NilJobID(), errors.NewTypedError(documents.ErrDocumentNotFound, err) } r, err := s.GetCurrentVersion(ctx, id) if err != nil { - return nil, err + return nil, jobs.NilJobID(), errors.NewTypedError(documents.ErrDocumentNotFound, err) } - cd, err := r.(*EntityRelationship).DeleteAccessToken(ctx, hexutil.Encode(did[0][:])) + er := new(EntityRelationship) + err = er.revokeRelationship(r.(*EntityRelationship), *data.TargetIdentity) if err != nil { - return nil, err + return nil, jobs.NilJobID(), err } - r.(*EntityRelationship).CoreDocument = cd - return r, nil -} - -// GetEntityRelationships returns the latest versions of the entity relationships that involve the entityID passed in -func (s service) GetEntityRelationships(ctx context.Context, entityID []byte) ([]documents.Model, error) { - var relationships []documents.Model - if entityID == nil { - return nil, documents.ErrPayloadNil - } - - selfDID, err := contextutil.AccountDID(ctx) + // validate invoice + err = UpdateValidator(s.factory, s.anchorRepo).Validate(r, er) if err != nil { - return nil, errors.New("failed to get self ID") + return nil, jobs.NilJobID(), errors.NewTypedError(documents.ErrDocumentInvalid, err) } - relevant, err := s.repo.ListAllRelationships(entityID, selfDID) + // we use CurrentVersion as the id since that will be unique across multiple versions of the same document + did := *er.Data.OwnerIdentity + err = s.repo.Create(did[:], er.CurrentVersion(), er) if err != nil { - return nil, err + return nil, jobs.NilJobID(), errors.NewTypedError(documents.ErrDocumentPersistence, err) } - for _, v := range relevant { - r, err := s.GetCurrentVersion(ctx, v) - if err != nil { - return nil, err - } - relationships = append(relationships, r) - } + jobID := contextutil.Job(ctx) + jobID, _, err = documents.CreateAnchorJob(ctx, s.jobManager, s.queueSrv, did, jobID, er.CurrentVersion()) + return er, jobID, err +} - if relationships == nil { - return nil, nil - } +// New returns a new uninitialised EntityRelationship. +func (s service) New(_ string) (documents.Model, error) { + return new(EntityRelationship), nil +} - return relationships, nil +// Validate takes care of document validation +func (s service) Validate(ctx context.Context, model documents.Model, old documents.Model) error { + return fieldValidator(s.factory).Validate(old, model) } diff --git a/documents/entityrelationship/service_test.go b/documents/entityrelationship/service_test.go index 6e465a4f9..c33fe9693 100644 --- a/documents/entityrelationship/service_test.go +++ b/documents/entityrelationship/service_test.go @@ -3,20 +3,20 @@ package entityrelationship import ( + "context" "testing" "github.com/centrifuge/centrifuge-protobufs/gen/go/coredocument" - "github.com/centrifuge/go-centrifuge/contextutil" "github.com/centrifuge/go-centrifuge/documents" "github.com/centrifuge/go-centrifuge/errors" + "github.com/centrifuge/go-centrifuge/identity" "github.com/centrifuge/go-centrifuge/jobs" - "github.com/centrifuge/go-centrifuge/protobufs/gen/go/entity" "github.com/centrifuge/go-centrifuge/testingutils" "github.com/centrifuge/go-centrifuge/testingutils/anchors" "github.com/centrifuge/go-centrifuge/testingutils/commons" "github.com/centrifuge/go-centrifuge/testingutils/config" - "github.com/centrifuge/go-centrifuge/testingutils/documents" "github.com/centrifuge/go-centrifuge/testingutils/identity" + "github.com/centrifuge/go-centrifuge/testingutils/testingjobs" "github.com/centrifuge/go-centrifuge/utils" "github.com/centrifuge/gocelery" "github.com/ethereum/go-ethereum/common/hexutil" @@ -35,7 +35,7 @@ func getServiceWithMockedLayers() (testingcommons.MockIdentityService, *testingc entityRepo := testEntityRepo() anchorRepo := &testinganchors.MockAnchorRepo{} anchorRepo.On("GetAnchorData", mock.Anything).Return(nil, errors.New("missing")) - docSrv := documents.DefaultService(cfg, entityRepo, anchorRepo, documents.NewServiceRegistry(), &idService) + docSrv := documents.DefaultService(cfg, entityRepo, anchorRepo, documents.NewServiceRegistry(), &idService, nil, nil) return idService, idFactory, DefaultService( docSrv, entityRepo, @@ -49,7 +49,7 @@ func TestService_Update(t *testing.T) { eSrv := srv.(service) // missing last version - model, _ := createCDWithEmbeddedEntityRelationship(t) + model, _ := CreateCDWithEmbeddedEntityRelationship(t, ctxh) _, _, _, err := eSrv.Update(ctxh, model) assert.Error(t, err) assert.True(t, errors.IsOfType(documents.ErrDocumentNotFound, err)) @@ -65,9 +65,7 @@ func TestService_Update(t *testing.T) { // create idFactory.On("IdentityExists", mock.Anything).Return(true, nil) - rp := testingdocuments.CreateRelationshipPayload() - relationship, err := eSrv.DeriveFromCreatePayload(ctxh, rp) - assert.NoError(t, err) + relationship := CreateRelationship(t, ctxh) old, _, _, err := eSrv.Create(ctxh, relationship) assert.NoError(t, err) @@ -75,7 +73,8 @@ func TestService_Update(t *testing.T) { assert.True(t, testEntityRepo().Exists(did[:], old.CurrentVersion())) // derive update payload - m, err := eSrv.DeriveFromUpdatePayload(ctxh, rp) + m := new(EntityRelationship) + err = m.revokeRelationship(old.(*EntityRelationship), *relationship.Data.TargetIdentity) assert.NoError(t, err) updated, _, _, err := eSrv.Update(ctxh, m) @@ -86,57 +85,6 @@ func TestService_Update(t *testing.T) { assert.True(t, testEntityRepo().Exists(did[:], updated.PreviousVersion())) } -func TestService_DeriveFromUpdatePayload(t *testing.T) { - ctxh := testingconfig.CreateAccountContext(t, cfg) - _, idFactory, srv := getServiceWithMockedLayers() - eSrv := srv.(service) - - // success - idFactory.On("IdentityExists", mock.Anything).Return(true, nil) - rp := testingdocuments.CreateRelationshipPayload() - relationship, err := eSrv.DeriveFromCreatePayload(ctxh, rp) - assert.NoError(t, err) - - old, _, _, err := eSrv.Create(ctxh, relationship) - assert.NoError(t, err) - assert.True(t, testEntityRepo().Exists(did[:], old.ID())) - assert.True(t, testEntityRepo().Exists(did[:], old.CurrentVersion())) - - // nil payload - m, err := eSrv.DeriveFromUpdatePayload(ctxh, nil) - assert.Nil(t, m) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrPayloadNil, err)) - - // invalid identity - payload := &entitypb.RelationshipPayload{ - TargetIdentity: "some random string", - DocumentId: rp.DocumentId, - } - - m, err = eSrv.DeriveFromUpdatePayload(ctxh, payload) - assert.Nil(t, m) - assert.Error(t, err) - assert.Contains(t, err.Error(), "malformed address provided") - - // invalid identifier - payload.DocumentId = "random string" - m, err = eSrv.DeriveFromUpdatePayload(ctxh, payload) - assert.Nil(t, m) - assert.Error(t, err) - - // other DID in payload - payload.DocumentId = rp.DocumentId - payload.TargetIdentity = testingidentity.GenerateRandomDID().String() - m, err = eSrv.DeriveFromUpdatePayload(ctxh, payload) - assert.Nil(t, m) - assert.Error(t, err) - - // valid payload - m, err = eSrv.DeriveFromUpdatePayload(ctxh, rp) - assert.NoError(t, err) -} - func TestService_GetEntityRelationships(t *testing.T) { ctxh := testingconfig.CreateAccountContext(t, cfg) _, idFactory, srv := getServiceWithMockedLayers() @@ -144,27 +92,15 @@ func TestService_GetEntityRelationships(t *testing.T) { // create idFactory.On("IdentityExists", mock.Anything).Return(true, nil) - rp := testingdocuments.CreateRelationshipPayload() - relationship, err := eSrv.DeriveFromCreatePayload(ctxh, rp) - assert.NoError(t, err) + relationship := CreateRelationship(t, ctxh) old, _, _, err := eSrv.Create(ctxh, relationship) assert.NoError(t, err) assert.True(t, testEntityRepo().Exists(did[:], old.ID())) assert.True(t, testEntityRepo().Exists(did[:], old.CurrentVersion())) - // derive update payload - m, err := eSrv.DeriveFromUpdatePayload(ctxh, rp) - assert.NoError(t, err) - - updated, _, _, err := eSrv.Update(ctxh, m) - assert.NoError(t, err) - assert.True(t, testEntityRepo().Exists(did[:], updated.CurrentVersion())) - // get all relationships - entityID, err := hexutil.Decode(rp.DocumentId) - assert.NoError(t, err) - r, err := eSrv.GetEntityRelationships(ctxh, entityID) + r, err := eSrv.GetEntityRelationships(ctxh, relationship.Data.EntityIdentifier) assert.NoError(t, err) assert.Len(t, r, 1) r, err = eSrv.GetEntityRelationships(ctxh, utils.RandomSlice(32)) @@ -185,8 +121,7 @@ func TestService_Create(t *testing.T) { // success idFactory.On("IdentityExists", mock.Anything).Return(true, nil) - relationship, err := eSrv.DeriveFromCreatePayload(ctxh, testingdocuments.CreateRelationshipPayload()) - assert.NoError(t, err) + relationship := CreateRelationship(t, ctxh) m, _, _, err = eSrv.Create(ctxh, relationship) assert.NoError(t, err) assert.True(t, testEntityRepo().Exists(did[:], m.ID())) @@ -194,96 +129,156 @@ func TestService_Create(t *testing.T) { idFactory.AssertExpectations(t) } -func TestService_DeriveFromCreatePayload(t *testing.T) { - eSrv := service{} - ctxh := testingconfig.CreateAccountContext(t, cfg) - - // nil payload - m, err := eSrv.DeriveFromCreatePayload(ctxh, nil) - assert.Nil(t, m) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrPayloadNil, err)) - - // invalid identity - docID := hexutil.Encode(utils.RandomSlice(32)) - payload := &entitypb.RelationshipPayload{ - TargetIdentity: "some random string", - DocumentId: docID, - } - - m, err = eSrv.DeriveFromCreatePayload(ctxh, payload) - assert.Nil(t, m) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrDocumentInvalid, err)) - - // success - payload.TargetIdentity = testingidentity.GenerateRandomDID().String() - m, err = eSrv.DeriveFromCreatePayload(ctxh, payload) - assert.NoError(t, err) - assert.NotNil(t, m) - er := m.(*EntityRelationship) - assert.Equal(t, er.TargetIdentity.String(), payload.TargetIdentity) -} - func TestService_DeriveFromCoreDocument(t *testing.T) { ctxh := testingconfig.CreateAccountContext(t, cfg) - selfDID, err := contextutil.AccountDID(ctxh) - assert.NoError(t, err) eSrv := service{repo: testEntityRepo()} empty := coredocumentpb.CoreDocument{} m, err := eSrv.DeriveFromCoreDocument(empty) assert.Error(t, err) - _, cd := createCDWithEmbeddedEntityRelationship(t) + _, cd := CreateCDWithEmbeddedEntityRelationship(t, ctxh) m, err = eSrv.DeriveFromCoreDocument(cd) assert.NoError(t, err, "must return model") assert.NotNil(t, m, "model must be non-nil") relationship, ok := m.(*EntityRelationship) assert.True(t, ok, "must be true") - assert.Equal(t, relationship.TargetIdentity.String(), "0x5F9132e0F92952abCb154A9b34563891ffe1AAcb") - assert.Equal(t, relationship.OwnerIdentity.String(), selfDID.String()) + assert.Equal(t, relationship.Data.TargetIdentity.String(), "0x5F9132e0F92952abCb154A9b34563891ffe1AAcb") + assert.Equal(t, relationship.Data.OwnerIdentity.String(), did.String()) } -func TestService_DeriveEntityRelationshipData(t *testing.T) { - _, _, eSrv := getServiceWithMockedLayers() +type mockRepo struct { + repository + mock.Mock +} - // some random model - _, err := eSrv.DeriveEntityRelationshipData(&mockModel{}) - assert.Error(t, err, "Derive must fail") +func (m *mockRepo) Create(acc, id []byte, model documents.Model) error { + args := m.Called(acc, id, model) + return args.Error(0) +} - // success - payload := testingdocuments.CreateRelationshipPayload() - relationship, err := eSrv.DeriveFromCreatePayload(testingconfig.CreateAccountContext(t, cfg), payload) - assert.NoError(t, err, "must be non nil") - data, err := eSrv.DeriveEntityRelationshipData(relationship) - assert.NoError(t, err, "Derive must succeed") - assert.NotNil(t, data, "data must be non nil") +func (m *mockRepo) FindEntityRelationshipIdentifier(entityIdentifier []byte, ownerDID, targetDID identity.DID) ([]byte, error) { + args := m.Called(entityIdentifier, ownerDID, targetDID) + d, _ := args.Get(0).([]byte) + return d, args.Error(1) } +func TestService_CreateModel(t *testing.T) { + payload := documents.CreatePayload{} + srv := service{} -func TestService_DeriveEntityResponse(t *testing.T) { - // success - eSrv := service{repo: testEntityRepo()} + // invalid data + ctxh := testingconfig.CreateAccountContext(t, cfg) + _, _, err := srv.CreateModel(ctxh, payload) + assert.Error(t, err) + assert.True(t, errors.IsOfType(documents.ErrDocumentInvalid, err)) + + // validator failed + idFactory := new(testingcommons.MockIdentityFactory) + idFactory.On("IdentityExists", mock.Anything).Return(false, nil).Once() + payload.Data = validData(t, did) + srv.factory = idFactory + _, _, err = srv.CreateModel(ctxh, payload) + assert.Error(t, err) + assert.True(t, errors.IsOfType(documents.ErrDocumentInvalid, err)) - // derive data failed - m := new(mockModel) - r, err := eSrv.DeriveEntityRelationshipResponse(m) - m.AssertExpectations(t) - assert.Nil(t, r) + // failed to create + idFactory.On("IdentityExists", mock.Anything).Return(true, nil) + repo := new(mockRepo) + repo.On("Create", mock.Anything, mock.Anything, mock.Anything).Return(errors.New("failed to save")).Once() + srv.repo = repo + _, _, err = srv.CreateModel(ctxh, payload) assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrDocumentInvalidType, err)) + assert.True(t, errors.IsOfType(documents.ErrDocumentPersistence, err)) // success - relationship, _ := createCDWithEmbeddedEntityRelationship(t) - r, err = eSrv.DeriveEntityRelationshipResponse(relationship) + srv.repo = testEntityRepo() + jm := testingjobs.MockJobManager{} + jm.On("ExecuteWithinJob", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(jobs.NilJobID(), make(chan error), nil) + srv.jobManager = jm + m, _, err := srv.CreateModel(ctxh, payload) assert.NoError(t, err) + assert.NotNil(t, m) + jm.AssertExpectations(t) + idFactory.AssertExpectations(t) + repo.AssertExpectations(t) +} - ctxh := testingconfig.CreateAccountContext(t, cfg) - selfDID, err := contextutil.AccountDID(ctxh) +func TestService_UpdateModel(t *testing.T) { + payload := documents.UpdatePayload{} + _, _, gsrv := getServiceWithMockedLayers() + srv := gsrv.(service) + ctx := context.Background() + + // invalid payload + _, _, err := srv.UpdateModel(ctx, payload) + assert.Error(t, err) + assert.True(t, errors.IsOfType(documents.ErrDocumentInvalid, err)) + + // missing relationship + payload.Data = validData(t, did) + r := new(mockRepo) + r.On("FindEntityRelationshipIdentifier", mock.Anything, did, mock.Anything).Return( + nil, errors.New("failed to find relationship")).Once() + srv.repo = r + _, _, err = srv.UpdateModel(ctx, payload) + assert.Error(t, err) + assert.True(t, errors.IsOfType(documents.ErrDocumentNotFound, err)) + + // missing version + erid := utils.RandomSlice(32) + r.On("FindEntityRelationshipIdentifier", mock.Anything, did, mock.Anything).Return(erid, nil).Once() + _, _, err = srv.UpdateModel(ctx, payload) + assert.Error(t, err) + assert.True(t, errors.IsOfType(documents.ErrDocumentNotFound, err)) + + // missing token + ctx = testingconfig.CreateAccountContext(t, cfg) + old, _ := CreateCDWithEmbeddedEntityRelationship(t, ctx) + err = testEntityRepo().Create(did[:], old.ID(), old) assert.NoError(t, err) - payload := &entitypb.RelationshipData{ - OwnerIdentity: selfDID.String(), - TargetIdentity: "0x5F9132e0F92952abCb154A9b34563891ffe1AAcb", + r.On("FindEntityRelationshipIdentifier", mock.Anything, did, mock.Anything).Return(old.ID(), nil) + _, _, err = srv.UpdateModel(ctx, payload) + assert.Error(t, err) + assert.Contains(t, err.Error(), documents.ErrAccessTokenNotFound.Error()) + + // validation failed + id := testingidentity.GenerateRandomDID() + p := documents.AccessTokenParams{ + Grantee: id.String(), + DocumentIdentifier: hexutil.Encode(old.ID()), } - assert.Equal(t, payload.TargetIdentity, r.Relationship[0].TargetIdentity) - assert.Equal(t, payload.OwnerIdentity, r.Relationship[0].OwnerIdentity) + cd, err := old.(*EntityRelationship).AddAccessToken(ctx, p) + assert.NoError(t, err) + old.(*EntityRelationship).CoreDocument.Document.AccessTokens = cd.Document.AccessTokens + assert.NoError(t, testEntityRepo().Update(did[:], old.ID(), old)) + idFactory := new(testingcommons.MockIdentityFactory) + idFactory.On("IdentityExists", mock.Anything).Return(false, nil).Once() + srv.factory = idFactory + payload.Data = validDataWithTargetDID(t, did, id) + _, _, err = srv.UpdateModel(ctx, payload) + assert.Error(t, err) + assert.True(t, errors.IsOfType(documents.ErrDocumentInvalid, err)) + + // create failed + idFactory.On("IdentityExists", mock.Anything).Return(true, nil) + r.On("Create", did[:], old.NextVersion(), mock.Anything).Return(errors.New("failed to create")).Once() + _, _, err = srv.UpdateModel(ctx, payload) + assert.Error(t, err) + assert.Contains(t, err.Error(), "failed to create") + + // success + r.On("Create", did[:], old.NextVersion(), mock.Anything).Return(nil) + jm := testingjobs.MockJobManager{} + jm.On("ExecuteWithinJob", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(jobs.NilJobID(), make(chan error), nil) + srv.jobManager = jm + _, _, err = srv.UpdateModel(ctx, payload) + assert.NoError(t, err) + r.AssertExpectations(t) + idFactory.AssertExpectations(t) + jm.AssertExpectations(t) +} + +func TestService_ValidateError(t *testing.T) { + srv := service{} + err := srv.Validate(context.Background(), nil, nil) + assert.Error(t, err) } diff --git a/documents/entityrelationship/test_bootstrapper.go b/documents/entityrelationship/test_bootstrapper.go deleted file mode 100644 index cd38fb4d4..000000000 --- a/documents/entityrelationship/test_bootstrapper.go +++ /dev/null @@ -1,11 +0,0 @@ -// +build integration unit - -package entityrelationship - -func (b Bootstrapper) TestBootstrap(context map[string]interface{}) error { - return b.Bootstrap(context) -} - -func (Bootstrapper) TestTearDown() error { - return nil -} diff --git a/documents/entityrelationship/test_entityrelationship.go b/documents/entityrelationship/test_entityrelationship.go new file mode 100644 index 000000000..df8eb3c03 --- /dev/null +++ b/documents/entityrelationship/test_entityrelationship.go @@ -0,0 +1,58 @@ +// +build integration unit testworld + +package entityrelationship + +import ( + "context" + "encoding/json" + "testing" + + coredocumentpb "github.com/centrifuge/centrifuge-protobufs/gen/go/coredocument" + "github.com/centrifuge/go-centrifuge/contextutil" + "github.com/centrifuge/go-centrifuge/documents" + "github.com/centrifuge/go-centrifuge/identity" + "github.com/centrifuge/go-centrifuge/utils" + "github.com/stretchr/testify/assert" +) + +func (b Bootstrapper) TestBootstrap(context map[string]interface{}) error { + return b.Bootstrap(context) +} + +func (Bootstrapper) TestTearDown() error { + return nil +} + +func InitEntityRelationship(t *testing.T, ctx context.Context, data Data) *EntityRelationship { + er := new(EntityRelationship) + d, err := json.Marshal(data) + assert.NoError(t, err) + err = er.DeriveFromCreatePayload(ctx, documents.CreatePayload{Data: d}) + assert.NoError(t, err) + return er +} + +func CreateRelationship(t *testing.T, ctx context.Context) *EntityRelationship { + did, err := contextutil.AccountDID(ctx) + assert.NoError(t, err) + target, _ := identity.StringsToDIDs("0x5F9132e0F92952abCb154A9b34563891ffe1AAcb") + d := Data{ + EntityIdentifier: utils.RandomSlice(32), + OwnerIdentity: &did, + TargetIdentity: target[0], + } + return InitEntityRelationship(t, ctx, d) +} + +func CreateCDWithEmbeddedEntityRelationship(t *testing.T, ctx context.Context) (documents.Model, coredocumentpb.CoreDocument) { + e := CreateRelationship(t, ctx) + _, err := e.CalculateDataRoot() + assert.NoError(t, err) + _, err = e.CalculateSigningRoot() + assert.NoError(t, err) + _, err = e.CalculateDocumentRoot() + assert.NoError(t, err) + cd, err := e.PackCoreDocument() + assert.NoError(t, err) + return e, cd +} diff --git a/documents/entityrelationship/validator.go b/documents/entityrelationship/validator.go index 91149b12e..d3fac8b34 100644 --- a/documents/entityrelationship/validator.go +++ b/documents/entityrelationship/validator.go @@ -19,7 +19,7 @@ func fieldValidator(factory identity.Factory) documents.Validator { return documents.ErrDocumentInvalidType } - identities := []*identity.DID{relationship.OwnerIdentity, relationship.TargetIdentity} + identities := []*identity.DID{relationship.Data.OwnerIdentity, relationship.Data.TargetIdentity} for _, i := range identities { valid, err := factory.IdentityExists(i) if err != nil || !valid { diff --git a/documents/entityrelationship/validator_test.go b/documents/entityrelationship/validator_test.go index ece4fc309..85fab7f22 100644 --- a/documents/entityrelationship/validator_test.go +++ b/documents/entityrelationship/validator_test.go @@ -7,6 +7,7 @@ import ( "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/go-centrifuge/testingutils/commons" + testingconfig "github.com/centrifuge/go-centrifuge/testingutils/config" "github.com/stretchr/testify/assert" ) @@ -39,8 +40,8 @@ func TestFieldValidator_Validate(t *testing.T) { // identity not created from the identity factory idFactory := new(testingcommons.MockIdentityFactory) - relationship := createEntityRelationship(t) - idFactory.On("IdentityExists", relationship.OwnerIdentity).Return(false, nil).Once() + relationship := CreateRelationship(t, testingconfig.CreateAccountContext(t, cfg)) + idFactory.On("IdentityExists", relationship.Data.OwnerIdentity).Return(false, nil).Once() fv = fieldValidator(idFactory) err = fv.Validate(nil, relationship) assert.Error(t, err) @@ -48,8 +49,8 @@ func TestFieldValidator_Validate(t *testing.T) { // identity created from identity factory idFactory = new(testingcommons.MockIdentityFactory) - idFactory.On("IdentityExists", relationship.TargetIdentity).Return(true, nil).Once() - idFactory.On("IdentityExists", relationship.OwnerIdentity).Return(true, nil).Once() + idFactory.On("IdentityExists", relationship.Data.TargetIdentity).Return(true, nil).Once() + idFactory.On("IdentityExists", relationship.Data.OwnerIdentity).Return(true, nil).Once() fv = fieldValidator(idFactory) err = fv.Validate(nil, relationship) assert.NoError(t, err) diff --git a/documents/error.go b/documents/error.go index 1cfa49ed9..d71b93af2 100644 --- a/documents/error.go +++ b/documents/error.go @@ -17,9 +17,6 @@ const ( // ErrDocumentIdentifier must be used for errors caused by document identifier problems ErrDocumentIdentifier = errors.Error("document identifier error") - // ErrDocumentVersion must be used for errors caused by document version problems - ErrDocumentVersion = errors.Error("document version error") - // ErrDocumentInvalidType must be used when a provided document type is not valid to be processed by the service ErrDocumentInvalidType = errors.Error("document is of invalid type") @@ -32,18 +29,12 @@ const ( // ErrDocumentSchemeUnknown is a sentinel error when the scheme provided is missing in the registry. ErrDocumentSchemeUnknown = errors.Error("unknown document scheme provided") - // ErrDocumentNotification must be used when a notification about a document could not be delivered - ErrDocumentNotification = errors.Error("could not notify of the document") - // ErrDocumentInvalid must only be used when the reason for invalidity is impossible to determine or the invalidity is caused by validation errors ErrDocumentInvalid = errors.Error("document is invalid") // ErrDocumentNotFound must be used to indicate that the document for provided id is not found in the system ErrDocumentNotFound = errors.Error("document not found in the system database") - // ErrNoCollaborator must be used to indicate that the document for provided id the user is not a collaborator - ErrNoCollaborator = errors.Error("no collaborator on the document") - // ErrDocumentVersionNotFound must be used to indicate that the specified version of the document for provided id is not found in the system ErrDocumentVersionNotFound = errors.Error("specified version of the document not found in the system database") @@ -53,15 +44,15 @@ const ( // ErrDocumentUnPackingCoreDocument must be used when unpacking of core document for the given document failed ErrDocumentUnPackingCoreDocument = errors.Error("core document unpacking failed") - // ErrDocumentPrepareCoreDocument must be used when preparing a new core document fails for the given document - ErrDocumentPrepareCoreDocument = errors.Error("core document preparation failed") - // ErrDocumentAnchoring must be used when document anchoring fails ErrDocumentAnchoring = errors.Error("document anchoring failed") // ErrDocumentProof must be used when document proof creation fails ErrDocumentProof = errors.Error("document proof error") + // ErrNotPatcher must be used if an expected patcher model does not support patching + ErrNotPatcher = errors.Error("document doesn't support patching") + // Coredoc errors // ErrCDCreate must be used for coredoc creation/generation errors @@ -70,15 +61,18 @@ const ( // ErrCDNewVersion must be used for coredoc creation/generation errors ErrCDNewVersion = errors.Error("error creating new version of core document") - // ErrCollaborators must be used when collaborators are not valid - ErrCollaborators = errors.Error("invalid collaborators") - // ErrCDTree must be used when there are errors during precise-proof tree and root generation ErrCDTree = errors.Error("error when generating trees/roots") // ErrCDAttribute must be used when there are errors caused by custom model attributes ErrCDAttribute = errors.Error("model attribute error") + // ErrCDStatus is a sentinel error used when status is being chnaged from Committed to anything else. + ErrCDStatus = errors.Error("cannot change the status of a committed document") + + // ErrDocumentNotInAllowedState is a sentinel error used when a document is not in allowed state for certain op + ErrDocumentNotInAllowedState = errors.Error("document is not in allowed state") + // Read ACL errors // ErrNftNotFound must be used when the NFT is not found in the document @@ -128,20 +122,26 @@ const ( // ErrNotImplemented must be used when an method has not been implemented ErrNotImplemented = errors.Error("Method not implemented") - // ErrDocumentConfigNotInitialised is a sentinal error when document config is missing + // ErrDocumentConfigNotInitialised is a sentinel error when document config is missing ErrDocumentConfigNotInitialised = errors.Error("document config not initialised") - // ErrDifferentAnchoredAddress is a sentinal error when anchor address is different from the configured one. + // ErrDifferentAnchoredAddress is a sentinel error when anchor address is different from the configured one. ErrDifferentAnchoredAddress = errors.Error("anchor address is not the node configured address") - // ErrDocumentIDReused is a sentinal error when identifier is re-used + // ErrDocumentIDReused is a sentinel error when identifier is re-used ErrDocumentIDReused = errors.Error("document identifier is already used") - // ErrNotValidAttrType is a sentinal error when an unknown attribute type is given + // ErrNotValidAttrType is a sentinel error when an unknown attribute type is given ErrNotValidAttrType = errors.Error("not a valid attribute type") - // ErrEmptyAttrLabel is a sentinal error when the attribute label is empty + // ErrEmptyAttrLabel is a sentinel error when the attribute label is empty ErrEmptyAttrLabel = errors.Error("empty attribute label") + + // ErrWrongAttrFormat is a sentinel error when the attribute format is wrong + ErrWrongAttrFormat = errors.Error("wrong attribute format") + + // ErrDocumentValidation must be used when document validation fails + ErrDocumentValidation = errors.Error("document validation failure") ) // Error wraps an error with specific key diff --git a/documents/generic/bootstrapper.go b/documents/generic/bootstrapper.go index 19b98d281..f839e4d1a 100644 --- a/documents/generic/bootstrapper.go +++ b/documents/generic/bootstrapper.go @@ -53,7 +53,7 @@ func (Bootstrapper) Bootstrap(ctx map[string]interface{}) error { return errors.New("failed to register generic doc service: %v", err) } - err = registry.Register(scheme, srv) + err = registry.Register(Scheme, srv) if err != nil { return errors.New("failed to register generic doc service: %v", err) } diff --git a/documents/generic/model.go b/documents/generic/model.go index 1050b63c4..d2810e3bf 100644 --- a/documents/generic/model.go +++ b/documents/generic/model.go @@ -1,7 +1,7 @@ package generic import ( - "encoding/json" + "context" "reflect" "github.com/centrifuge/centrifuge-protobufs/documenttypes" @@ -20,7 +20,8 @@ import ( const ( prefix string = "generic" - scheme = prefix + // Scheme to identify generic document + Scheme = prefix ) // tree prefixes for specific to documents use the second byte of a 4 byte slice by convention @@ -37,7 +38,7 @@ type Generic struct { func getProtoGenericData() *genericpb.GenericData { return &genericpb.GenericData{ - Scheme: []byte(scheme), + Scheme: []byte(Scheme), } } @@ -102,7 +103,11 @@ func (g *Generic) getDocumentDataTree() (tree *proofs.DocumentTree, err error) { return nil, errors.New("getDocumentDataTree error CoreDocument not set") } - t := g.CoreDocument.DefaultTreeWithPrefix(prefix, compactPrefix()) + t, err := g.CoreDocument.DefaultTreeWithPrefix(prefix, compactPrefix()) + if err != nil { + return nil, err + } + err = t.AddLeavesFromDocument(getProtoGenericData()) if err != nil { return nil, errors.New("getDocumentDataTree error %v", err) @@ -254,25 +259,8 @@ func (g *Generic) GetData() interface{} { return g.Data } -// loadData unmarshals json blob to Data. -func (g *Generic) loadData(data []byte) error { - var d Data - err := json.Unmarshal(data, &d) - if err != nil { - return err - } - - g.Data = d - return nil -} - -// unpackFromCreatePayload unpacks the invoice data from the Payload. -func (g *Generic) unpackFromCreatePayload(did identity.DID, payload documents.CreatePayload) error { - if err := g.loadData(payload.Data); err != nil { - return err - } - - payload.Collaborators.ReadWriteCollaborators = append(payload.Collaborators.ReadWriteCollaborators, did) +// DeriveFromCreatePayload unpacks the invoice data from the Payload. +func (g *Generic) DeriveFromCreatePayload(_ context.Context, payload documents.CreatePayload) error { cd, err := documents.NewCoreDocument(compactPrefix(), payload.Collaborators, payload.Attributes) if err != nil { return errors.NewTypedError(documents.ErrCDCreate, err) @@ -282,13 +270,20 @@ func (g *Generic) unpackFromCreatePayload(did identity.DID, payload documents.Cr return nil } -// unpackFromUpdatePayload unpacks the update payload and prepares a new version. -func (g *Generic) unpackFromUpdatePayload(old *Generic, payload documents.UpdatePayload) error { - if err := g.loadData(payload.Data); err != nil { +// unpackFromUpdatePayloadOld unpacks the update payload and prepares a new version. +func (g *Generic) unpackFromUpdatePayloadOld(old *Generic, payload documents.UpdatePayload) error { + ncd, err := old.CoreDocument.PrepareNewVersion(compactPrefix(), payload.Collaborators, payload.Attributes) + if err != nil { return err } - ncd, err := old.CoreDocument.PrepareNewVersion(compactPrefix(), payload.Collaborators, payload.Attributes) + g.CoreDocument = ncd + return nil +} + +// Patch merges payload data into model +func (g *Generic) Patch(payload documents.UpdatePayload) error { + ncd, err := g.CoreDocument.Patch(compactPrefix(), payload.Collaborators, payload.Attributes) if err != nil { return err } @@ -297,7 +292,19 @@ func (g *Generic) unpackFromUpdatePayload(old *Generic, payload documents.Update return nil } -// Scheme returns the invoice scheme. +// DeriveFromUpdatePayload unpacks the update payload and prepares a new version. +func (g *Generic) DeriveFromUpdatePayload(_ context.Context, payload documents.UpdatePayload) (documents.Model, error) { + ncd, err := g.CoreDocument.PrepareNewVersion(compactPrefix(), payload.Collaborators, payload.Attributes) + if err != nil { + return nil, err + } + + return &Generic{ + CoreDocument: ncd, + }, nil +} + +// Scheme returns the invoice Scheme. func (g *Generic) Scheme() string { - return scheme + return Scheme } diff --git a/documents/generic/model_test.go b/documents/generic/model_test.go index 6bd810642..cf055ebb1 100644 --- a/documents/generic/model_test.go +++ b/documents/generic/model_test.go @@ -3,6 +3,8 @@ package generic import ( + "context" + "crypto/sha256" "encoding/json" "fmt" "os" @@ -44,7 +46,7 @@ func TestMain(m *testing.M) { ctx[ethereum.BootstrappedEthereumClient] = ethClient jobMan := &testingjobs.MockJobManager{} ctx[jobs.BootstrappedService] = jobMan - done := make(chan bool) + done := make(chan error) jobMan.On("ExecuteWithinJob", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(jobs.NilJobID(), done, nil) ctx[bootstrap.BootstrappedInvoiceUnpaid] = new(testingdocuments.MockRegistry) ibootstrappers := []bootstrap.TestBootstrapper{ @@ -152,11 +154,11 @@ func TestGeneric_CreateProofs(t *testing.T) { return } - tree, err := g.DocumentRootTree() + signingRoot, err := g.CalculateSigningRoot() assert.NoError(t, err) // Validate roles - valid, err := tree.ValidateProof(proof[0]) + valid, err := documents.ValidateProof(proof[0], signingRoot, sha256.New()) assert.Nil(t, err) assert.True(t, valid) @@ -166,9 +168,74 @@ func TestGeneric_CreateProofs(t *testing.T) { assert.True(t, g.AccountCanRead(acc)) // Validate document_type - valid, err = tree.ValidateProof(proof[1]) - assert.Nil(t, err) + valid, err = documents.ValidateProof(proof[1], signingRoot, sha256.New()) + assert.NoError(t, err) + assert.True(t, valid) +} + +func TestAttributeProof(t *testing.T) { + tc, err := configstore.NewAccount("main", cfg) + acc := tc.(*configstore.Account) + acc.IdentityID = did[:] + assert.NoError(t, err) + g, _ := createCDWithEmbeddedGeneric(t) + + var attrs []documents.Attribute + loanAmount := "loanAmount" + loanAmountValue := "100" + attr0, err := documents.NewStringAttribute(loanAmount, documents.AttrInt256, loanAmountValue) + assert.NoError(t, err) + attrs = append(attrs, attr0) + asIsValue := "asIsValue" + asIsValueValue := "1000" + attr1, err := documents.NewStringAttribute(asIsValue, documents.AttrInt256, asIsValueValue) + assert.NoError(t, err) + attrs = append(attrs, attr1) + afterRehabValue := "afterRehabValue" + afterRehabValueValue := "2000" + attr2, err := documents.NewStringAttribute(afterRehabValue, documents.AttrInt256, afterRehabValueValue) + assert.NoError(t, err) + attrs = append(attrs, attr2) + + err = g.AddAttributes(documents.CollaboratorsAccess{}, false, attrs...) + assert.NoError(t, err) + + sig, err := acc.SignMsg([]byte{0, 1, 2, 3}) + assert.NoError(t, err) + g.AppendSignatures(sig) + _, err = g.CalculateDataRoot() + assert.NoError(t, err) + signingRoot, err := g.CalculateSigningRoot() + assert.NoError(t, err) + + keys, err := tc.GetKeys() + assert.NoError(t, err) + signerId := hexutil.Encode(append(did[:], keys[identity.KeyPurposeSigning.Name].PublicKey...)) + signatureSender := fmt.Sprintf("%s.signatures[%s].signature", documents.SignaturesTreePrefix, signerId) + attributeLoanAmount := fmt.Sprintf("%s.attributes[%s].byte_val", documents.CDTreePrefix, attr0.Key.String()) + attributeAsIsVal := fmt.Sprintf("%s.attributes[%s].byte_val", documents.CDTreePrefix, attr1.Key.String()) + attributeAfterRehabVal := fmt.Sprintf("%s.attributes[%s].byte_val", documents.CDTreePrefix, attr2.Key.String()) + proofFields := []string{attributeLoanAmount, attributeAsIsVal, attributeAfterRehabVal, signatureSender} + proof, err := g.CreateProofs(proofFields) + assert.NoError(t, err) + assert.NotNil(t, proof) + assert.Len(t, proofFields, 4) + + // Validate loanAmount + valid, err := documents.ValidateProof(proof[0], signingRoot, sha256.New()) + assert.NoError(t, err) assert.True(t, valid) + + // Validate asIsValue + valid, err = documents.ValidateProof(proof[1], signingRoot, sha256.New()) + assert.NoError(t, err) + assert.True(t, valid) + + // Validate afterRehabValue + valid, err = documents.ValidateProof(proof[2], signingRoot, sha256.New()) + assert.NoError(t, err) + assert.True(t, valid) + } func TestGeneric_CreateNFTProofs(t *testing.T) { @@ -182,7 +249,7 @@ func TestGeneric_CreateNFTProofs(t *testing.T) { g.AppendSignatures(sig) _, err = g.CalculateDataRoot() assert.NoError(t, err) - _, err = g.CalculateSigningRoot() + signingRoot, err := g.CalculateSigningRoot() assert.NoError(t, err) _, err = g.CalculateDocumentRoot() assert.NoError(t, err) @@ -190,9 +257,9 @@ func TestGeneric_CreateNFTProofs(t *testing.T) { keys, err := tc.GetKeys() assert.NoError(t, err) signerId := hexutil.Encode(append(did[:], keys[identity.KeyPurposeSigning.Name].PublicKey...)) - signingRoot := fmt.Sprintf("%s.%s", documents.DRTreePrefix, documents.SigningRootField) + signingRootField := fmt.Sprintf("%s.%s", documents.DRTreePrefix, documents.SigningRootField) signatureSender := fmt.Sprintf("%s.signatures[%s].signature", documents.SignaturesTreePrefix, signerId) - proofFields := []string{signingRoot, signatureSender, documents.CDTreePrefix + ".next_version"} + proofFields := []string{signingRootField, signatureSender, documents.CDTreePrefix + ".next_version"} proof, err := g.CreateProofs(proofFields) assert.Nil(t, err) assert.NotNil(t, proof) @@ -211,7 +278,7 @@ func TestGeneric_CreateNFTProofs(t *testing.T) { assert.True(t, valid) // Validate next_version - valid, err = tree.ValidateProof(proof[2]) + valid, err = documents.ValidateProof(proof[2], signingRoot, sha256.New()) assert.Nil(t, err) assert.True(t, valid) } @@ -223,7 +290,7 @@ func TestGeneric_getDocumentDataTree(t *testing.T) { _, leaf := tree.GetLeafByProperty("generic.scheme") assert.NotNil(t, leaf) assert.Equal(t, "generic.scheme", leaf.Property.ReadableName()) - assert.Equal(t, []byte(scheme), leaf.Value) + assert.Equal(t, []byte(Scheme), leaf.Value) } type mockModel struct { @@ -306,7 +373,7 @@ func TestGeneric_AddAttributes(t *testing.T) { g, _ := createCDWithEmbeddedGeneric(t) label := "some key" value := "some value" - attr, err := documents.NewAttribute(label, documents.AttrString, value) + attr, err := documents.NewStringAttribute(label, documents.AttrString, value) assert.NoError(t, err) // success @@ -328,7 +395,7 @@ func TestGeneric_DeleteAttribute(t *testing.T) { g, _ := createCDWithEmbeddedGeneric(t) label := "some key" value := "some value" - attr, err := documents.NewAttribute(label, documents.AttrString, value) + attr, err := documents.NewStringAttribute(label, documents.AttrString, value) assert.NoError(t, err) // failed @@ -359,29 +426,15 @@ func validData(t *testing.T) []byte { return marshallData(t, d) } -func TestGeneric_loadData(t *testing.T) { +func TestGeneric_DeriveFromCreatePayload(t *testing.T) { + payload := documents.CreatePayload{Collaborators: documents.CollaboratorsAccess{ + ReadWriteCollaborators: []identity.DID{did}, + }} g := new(Generic) - payload := documents.CreatePayload{} - - // valid data - payload.Data = validData(t) - err := g.loadData(payload.Data) - assert.NoError(t, err) - data := g.GetData().(Data) - assert.Empty(t, data) -} - -func TestGeneric_unpackFromCreatePayload(t *testing.T) { - payload := documents.CreatePayload{} - g := new(Generic) - - // invalid data - err := g.unpackFromCreatePayload(did, payload) - assert.Error(t, err) - assert.Contains(t, err.Error(), "unexpected end of JSON input") + ctx := context.Background() // invalid attributes - attr, err := documents.NewAttribute("test", documents.AttrString, "value") + attr, err := documents.NewStringAttribute("test", documents.AttrString, "value") assert.NoError(t, err) val := attr.Value val.Type = documents.AttributeType("some type") @@ -390,7 +443,7 @@ func TestGeneric_unpackFromCreatePayload(t *testing.T) { attr.Key: attr, } payload.Data = validData(t) - err = g.unpackFromCreatePayload(did, payload) + err = g.DeriveFromCreatePayload(ctx, payload) assert.Error(t, err) assert.True(t, errors.IsOfType(documents.ErrCDCreate, err)) @@ -400,7 +453,7 @@ func TestGeneric_unpackFromCreatePayload(t *testing.T) { payload.Attributes = map[documents.AttrKey]documents.Attribute{ attr.Key: attr, } - err = g.unpackFromCreatePayload(did, payload) + err = g.DeriveFromCreatePayload(ctx, payload) assert.NoError(t, err) } @@ -409,13 +462,8 @@ func TestGeneric_unpackFromUpdatePayload(t *testing.T) { old, _ := createCDWithEmbeddedGeneric(t) g := new(Generic) - // invalid data - err := g.unpackFromUpdatePayload(old.(*Generic), payload) - assert.Error(t, err) - assert.Contains(t, err.Error(), "unexpected end of JSON input") - // invalid attributes - attr, err := documents.NewAttribute("test", documents.AttrString, "value") + attr, err := documents.NewStringAttribute("test", documents.AttrString, "value") assert.NoError(t, err) val := attr.Value val.Type = documents.AttributeType("some type") @@ -424,7 +472,7 @@ func TestGeneric_unpackFromUpdatePayload(t *testing.T) { attr.Key: attr, } payload.Data = validData(t) - err = g.unpackFromUpdatePayload(old.(*Generic), payload) + err = g.unpackFromUpdatePayloadOld(old.(*Generic), payload) assert.Error(t, err) assert.True(t, errors.IsOfType(documents.ErrCDNewVersion, err)) @@ -434,6 +482,6 @@ func TestGeneric_unpackFromUpdatePayload(t *testing.T) { payload.Attributes = map[documents.AttrKey]documents.Attribute{ attr.Key: attr, } - err = g.unpackFromUpdatePayload(old.(*Generic), payload) + err = g.unpackFromUpdatePayloadOld(old.(*Generic), payload) assert.NoError(t, err) } diff --git a/documents/generic/service.go b/documents/generic/service.go index 35645caff..b0ae4fcfc 100644 --- a/documents/generic/service.go +++ b/documents/generic/service.go @@ -52,7 +52,7 @@ func (s service) DeriveFromCoreDocument(cd coredocumentpb.CoreDocument) (documen } // Update finds the old document, validates the new version and persists the updated document -func (s service) Update(ctx context.Context, new documents.Model) (documents.Model, jobs.JobID, chan bool, error) { +func (s service) Update(ctx context.Context, new documents.Model) (documents.Model, jobs.JobID, chan error, error) { selfDID, err := contextutil.AccountDID(ctx) if err != nil { return nil, jobs.NilJobID(), nil, errors.NewTypedError(documents.ErrDocumentConfigAccountID, err) @@ -91,7 +91,8 @@ func (s service) CreateModel(ctx context.Context, payload documents.CreatePayloa } g := new(Generic) - if err := g.unpackFromCreatePayload(did, payload); err != nil { + payload.Collaborators.ReadWriteCollaborators = append(payload.Collaborators.ReadWriteCollaborators, did) + if err := g.DeriveFromCreatePayload(ctx, payload); err != nil { return nil, jobs.NilJobID(), errors.NewTypedError(documents.ErrDocumentInvalid, err) } @@ -124,7 +125,7 @@ func (s service) UpdateModel(ctx context.Context, payload documents.UpdatePayloa } g := new(Generic) - err = g.unpackFromUpdatePayload(oldGeneric, payload) + err = g.unpackFromUpdatePayloadOld(oldGeneric, payload) if err != nil { return nil, jobs.NilJobID(), errors.NewTypedError(documents.ErrDocumentInvalid, err) } @@ -144,3 +145,13 @@ func (s service) UpdateModel(ctx context.Context, payload documents.UpdatePayloa jobID, _, err = documents.CreateAnchorJob(ctx, s.jobManager, s.queueSrv, did, jobID, g.CurrentVersion()) return g, jobID, err } + +// New returns a new uninitialised Generic document. +func (s service) New(_ string) (documents.Model, error) { + return new(Generic), nil +} + +// Validate takes care of document validation +func (s service) Validate(ctx context.Context, model documents.Model, old documents.Model) error { + return nil +} diff --git a/documents/generic/service_test.go b/documents/generic/service_test.go index 341d84c99..5849f7134 100644 --- a/documents/generic/service_test.go +++ b/documents/generic/service_test.go @@ -30,17 +30,12 @@ func TestService_CreateModel(t *testing.T) { assert.Error(t, err) assert.True(t, errors.IsOfType(documents.ErrDocumentConfigAccountID, err)) - // invalid data - ctxh := testingconfig.CreateAccountContext(t, cfg) - _, _, err = srv.CreateModel(ctxh, payload) - assert.Error(t, err) - assert.Contains(t, err.Error(), "unexpected end of JSON input") - // success + ctxh := testingconfig.CreateAccountContext(t, cfg) payload.Data = validData(t) srv.repo = testRepo() jm := testingjobs.MockJobManager{} - jm.On("ExecuteWithinJob", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(jobs.NilJobID(), make(chan bool), nil) + jm.On("ExecuteWithinJob", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(jobs.NilJobID(), make(chan error), nil) srv.jobManager = jm m, _, err := srv.CreateModel(ctxh, payload) assert.NoError(t, err) @@ -59,7 +54,7 @@ func getServiceWithMockedLayers() (testingcommons.MockIdentityService, documents repo := testRepo() anchorRepo := &testinganchors.MockAnchorRepo{} anchorRepo.On("GetAnchorData", mock.Anything).Return(nil, errors.New("missing")) - docSrv := documents.DefaultService(cfg, repo, anchorRepo, documents.NewServiceRegistry(), &idService) + docSrv := documents.DefaultService(cfg, repo, anchorRepo, documents.NewServiceRegistry(), &idService, nil, nil) return idService, DefaultService( docSrv, repo, @@ -90,9 +85,6 @@ func TestService_UpdateModel(t *testing.T) { err = testRepo().Create(did[:], g.ID(), g) assert.NoError(t, err) payload.DocumentID = g.ID() - _, _, err = srv.UpdateModel(ctxh, payload) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrDocumentInvalid, err)) // failed validations payload.Data = validData(t) @@ -108,7 +100,7 @@ func TestService_UpdateModel(t *testing.T) { // Success jm := testingjobs.MockJobManager{} - jm.On("ExecuteWithinJob", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(jobs.NilJobID(), make(chan bool), nil) + jm.On("ExecuteWithinJob", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(jobs.NilJobID(), make(chan error), nil) srv.jobManager = jm srv.anchorRepo = oldRepo m, _, err := srv.UpdateModel(ctxh, payload) @@ -150,7 +142,7 @@ func TestService_Update(t *testing.T) { // success jm := testingjobs.MockJobManager{} - jm.On("ExecuteWithinJob", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(jobs.NilJobID(), make(chan bool), nil) + jm.On("ExecuteWithinJob", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(jobs.NilJobID(), make(chan error), nil) gsrv.jobManager = jm m, _, _, err := gsrv.Update(ctxh, g) assert.NoError(t, err) @@ -160,3 +152,9 @@ func TestService_Update(t *testing.T) { assert.Equal(t, m.CurrentVersion(), g.NextVersion()) jm.AssertExpectations(t) } + +func TestService_Validate(t *testing.T) { + srv := service{} + err := srv.Validate(context.Background(), nil, nil) + assert.NoError(t, err) +} diff --git a/documents/generic/test_generic.go b/documents/generic/test_generic.go new file mode 100644 index 000000000..08729d9bc --- /dev/null +++ b/documents/generic/test_generic.go @@ -0,0 +1,27 @@ +// +build integration unit testworld + +package generic + +import ( + "context" + "testing" + + "github.com/centrifuge/go-centrifuge/documents" + "github.com/centrifuge/go-centrifuge/identity" + "github.com/stretchr/testify/assert" +) + +func InitGeneric(t *testing.T, did identity.DID, payload documents.CreatePayload) *Generic { + gen := new(Generic) + payload.Collaborators.ReadWriteCollaborators = append(payload.Collaborators.ReadWriteCollaborators, did) + assert.NoError(t, gen.DeriveFromCreatePayload(context.Background(), payload)) + return gen +} + +func (b Bootstrapper) TestBootstrap(context map[string]interface{}) error { + return b.Bootstrap(context) +} + +func (Bootstrapper) TestTearDown() error { + return nil +} diff --git a/documents/invoice/bootstrapper.go b/documents/invoice/bootstrapper.go index d7fa6e8e7..b5ebe1942 100644 --- a/documents/invoice/bootstrapper.go +++ b/documents/invoice/bootstrapper.go @@ -4,18 +4,12 @@ import ( "github.com/centrifuge/centrifuge-protobufs/documenttypes" "github.com/centrifuge/go-centrifuge/anchors" "github.com/centrifuge/go-centrifuge/bootstrap" - "github.com/centrifuge/go-centrifuge/config" "github.com/centrifuge/go-centrifuge/documents" "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/go-centrifuge/jobs" "github.com/centrifuge/go-centrifuge/queue" ) -const ( - // BootstrappedInvoiceHandler maps to grpc handler for invoices - BootstrappedInvoiceHandler string = "BootstrappedInvoiceHandler" -) - // Bootstrapper implements bootstrap.Bootstrapper. type Bootstrapper struct{} @@ -47,11 +41,6 @@ func (Bootstrapper) Bootstrap(ctx map[string]interface{}) error { return errors.New("transaction service not initialised") } - cfgSrv, ok := ctx[config.BootstrappedConfigStorage].(config.Service) - if !ok { - return errors.New("config service not initialised") - } - anchorRepo, ok := ctx[anchors.BootstrappedAnchorRepo].(anchors.AnchorRepository) if !ok { return anchors.ErrAnchorRepoNotInitialised @@ -74,11 +63,10 @@ func (Bootstrapper) Bootstrap(ctx map[string]interface{}) error { return errors.New("failed to register invoice service: %v", err) } - err = registry.Register(scheme, srv) + err = registry.Register(Scheme, srv) if err != nil { return errors.New("failed to register invoice service: %v", err) } - ctx[BootstrappedInvoiceHandler] = GRPCHandler(cfgSrv, srv) return nil } diff --git a/documents/invoice/bootstrapper_test.go b/documents/invoice/bootstrapper_test.go index dd251e6a5..c6f61992a 100644 --- a/documents/invoice/bootstrapper_test.go +++ b/documents/invoice/bootstrapper_test.go @@ -4,27 +4,11 @@ package invoice import ( "testing" + + "github.com/stretchr/testify/assert" ) func TestBootstrapper_Bootstrap(t *testing.T) { - //err := (&Bootstrapper{}).Bootstrap(map[string]interface{}{}) - //assert.Error(t, err, "Should throw an error because of empty context") -} - -func TestBootstrapper_registerInvoiceService(t *testing.T) { - //context := map[string]interface{}{} - //context[bootstrap.BootstrappedDb] = true - //err := (&Bootstrapper{}).Bootstrap(context) - //assert.Nil(t, err, "Should throw because context is passed") - // - ////coreDocument embeds a invoice - //coreDocument := testingutils.GenerateCoreDocument() - //registry := document.GetRegistryInstance() - // - //documentType, err := cd.GetTypeUrl(coreDocument) - //assert.Nil(t, err, "should not throw an error because document contains a type") - // - //service, err := registry.LocateService(documentType) - //assert.Nil(t, err, "service should be successful registered and able to locate") - //assert.NotNil(t, service, "service should be returned") + err := (&Bootstrapper{}).Bootstrap(map[string]interface{}{}) + assert.Error(t, err, "Should throw an error because of empty context") } diff --git a/documents/invoice/converters.go b/documents/invoice/converters.go index 55fd56bca..c72719935 100644 --- a/documents/invoice/converters.go +++ b/documents/invoice/converters.go @@ -3,36 +3,8 @@ package invoice import ( "github.com/centrifuge/centrifuge-protobufs/gen/go/invoice" "github.com/centrifuge/go-centrifuge/documents" - clientpb "github.com/centrifuge/go-centrifuge/protobufs/gen/go/invoice" ) -func toClientLineItems(items []*LineItem) []*clientpb.LineItem { - var citems []*clientpb.LineItem - for _, item := range items { - decs := documents.DecimalsToStrings( - item.PricePerUnit, item.Quantity, item.NetWeight, item.TaxAmount, item.TaxRate, item.TaxCode, item.TotalAmount) - - citems = append(citems, &clientpb.LineItem{ - ItemNumber: item.ItemNumber, - Description: item.Description, - SenderPartNo: item.SenderPartNo, - PricePerUnit: decs[0], - Quantity: decs[1], - UnitOfMeasure: item.UnitOfMeasure, - NetWeight: decs[2], - TaxAmount: decs[3], - TaxRate: decs[4], - TaxCode: decs[5], - TotalAmount: decs[6], - PurchaseOrderNumber: item.PurchaseOrderNumber, - PurchaseOrderItemNumber: item.PurchaseOrderItemNumber, - DeliveryNoteNumber: item.DeliveryNoteNumber, - }) - } - - return citems -} - func toP2PLineItems(items []*LineItem) ([]*invoicepb.LineItem, error) { var pitems []*invoicepb.LineItem for _, item := range items { @@ -63,36 +35,6 @@ func toP2PLineItems(items []*LineItem) ([]*invoicepb.LineItem, error) { return pitems, nil } -func fromClientLineItems(citems []*clientpb.LineItem) ([]*LineItem, error) { - var items []*LineItem - for _, item := range citems { - decs, err := documents.StringsToDecimals( - item.PricePerUnit, item.Quantity, item.NetWeight, item.TaxAmount, item.TaxRate, item.TaxCode, item.TotalAmount) - if err != nil { - return nil, err - } - - items = append(items, &LineItem{ - ItemNumber: item.ItemNumber, - Description: item.Description, - SenderPartNo: item.SenderPartNo, - PricePerUnit: decs[0], - Quantity: decs[1], - UnitOfMeasure: item.UnitOfMeasure, - NetWeight: decs[2], - TaxAmount: decs[3], - TaxRate: decs[4], - TaxCode: decs[5], - TotalAmount: decs[6], - PurchaseOrderNumber: item.PurchaseOrderNumber, - PurchaseOrderItemNumber: item.PurchaseOrderItemNumber, - DeliveryNoteNumber: item.DeliveryNoteNumber, - }) - } - - return items, nil -} - func fromP2PLineItems(pitems []*invoicepb.LineItem) ([]*LineItem, error) { var items []*LineItem for _, item := range pitems { @@ -123,23 +65,6 @@ func fromP2PLineItems(pitems []*invoicepb.LineItem) ([]*LineItem, error) { return items, nil } -func toClientTaxItems(items []*TaxItem) []*clientpb.TaxItem { - var citems []*clientpb.TaxItem - for _, item := range items { - decs := documents.DecimalsToStrings(item.TaxAmount, item.TaxRate, item.TaxCode, item.TaxBaseAmount) - citems = append(citems, &clientpb.TaxItem{ - ItemNumber: item.ItemNumber, - InvoiceItemNumber: item.InvoiceItemNumber, - TaxAmount: decs[0], - TaxRate: decs[1], - TaxCode: decs[2], - TaxBaseAmount: decs[3], - }) - } - - return citems -} - func toP2PTaxItems(items []*TaxItem) ([]*invoicepb.TaxItem, error) { var pitems []*invoicepb.TaxItem for _, item := range items { @@ -161,27 +86,6 @@ func toP2PTaxItems(items []*TaxItem) ([]*invoicepb.TaxItem, error) { return pitems, nil } -func fromClientTaxItems(citems []*clientpb.TaxItem) ([]*TaxItem, error) { - var items []*TaxItem - for _, item := range citems { - decs, err := documents.StringsToDecimals(item.TaxAmount, item.TaxRate, item.TaxCode, item.TaxBaseAmount) - if err != nil { - return nil, err - } - - items = append(items, &TaxItem{ - ItemNumber: item.ItemNumber, - InvoiceItemNumber: item.InvoiceItemNumber, - TaxAmount: decs[0], - TaxRate: decs[1], - TaxCode: decs[2], - TaxBaseAmount: decs[3], - }) - } - - return items, nil -} - func fromP2PTaxItems(pitems []*invoicepb.TaxItem) ([]*TaxItem, error) { var items []*TaxItem for _, item := range pitems { diff --git a/documents/invoice/converters_test.go b/documents/invoice/converters_test.go index d809ce81f..0dfc30904 100644 --- a/documents/invoice/converters_test.go +++ b/documents/invoice/converters_test.go @@ -21,22 +21,13 @@ func TestLineItems(t *testing.T) { }, } - citems := toClientLineItems(items) pitems, err := toP2PLineItems(items) assert.NoError(t, err) - fcitems, err := fromClientLineItems(citems) - assert.NoError(t, err) - assert.Equal(t, items, fcitems) - fpitems, err := fromP2PLineItems(pitems) assert.NoError(t, err) assert.Equal(t, items, fpitems) - citems[0].TaxAmount = "100.1234567891234567891" - _, err = fromClientLineItems(citems) - assert.Error(t, err) - pitems[0].TaxAmount = utils.RandomSlice(40) _, err = fromP2PLineItems(pitems) assert.Error(t, err) @@ -53,22 +44,13 @@ func TestTaxItems(t *testing.T) { }, } - citems := toClientTaxItems(items) pitems, err := toP2PTaxItems(items) assert.NoError(t, err) - fcitems, err := fromClientTaxItems(citems) - assert.NoError(t, err) fpitems, err := fromP2PTaxItems(pitems) assert.NoError(t, err) - - assert.Equal(t, items, fcitems) assert.Equal(t, items, fpitems) - citems[0].TaxAmount = "0.1.1" - _, err = fromClientTaxItems(citems) - assert.Error(t, err) - pitems[0].TaxAmount = utils.RandomSlice(40) _, err = fromP2PTaxItems(pitems) assert.Error(t, err) diff --git a/documents/invoice/handler.go b/documents/invoice/handler.go deleted file mode 100644 index 9697e452e..000000000 --- a/documents/invoice/handler.go +++ /dev/null @@ -1,158 +0,0 @@ -package invoice - -import ( - "github.com/centrifuge/go-centrifuge/config" - "github.com/centrifuge/go-centrifuge/contextutil" - - "github.com/centrifuge/go-centrifuge/centerrors" - clientinvoicepb "github.com/centrifuge/go-centrifuge/protobufs/gen/go/invoice" - "github.com/ethereum/go-ethereum/common/hexutil" - logging "github.com/ipfs/go-log" - "golang.org/x/net/context" -) - -var apiLog = logging.Logger("invoice-api") - -// grpcHandler handles all the invoice document related actions -// anchoring, sending, finding stored invoice document -type grpcHandler struct { - service Service - config config.Service -} - -// GRPCHandler returns an implementation of invoice.DocumentServiceServer -func GRPCHandler(config config.Service, srv Service) clientinvoicepb.InvoiceServiceServer { - return &grpcHandler{ - service: srv, - config: config, - } -} - -// Create handles the creation of the invoices and anchoring the documents on chain -func (h *grpcHandler) Create(ctx context.Context, req *clientinvoicepb.InvoiceCreatePayload) (*clientinvoicepb.InvoiceResponse, error) { - apiLog.Debugf("Create request %v", req) - cctx, err := contextutil.Context(ctx, h.config) - if err != nil { - apiLog.Error(err) - return nil, err - } - - doc, err := h.service.DeriveFromCreatePayload(cctx, req) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "could not derive create payload") - } - - // validate and persist - doc, jobID, _, err := h.service.Create(cctx, doc) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "could not create document") - } - - resp, err := h.service.DeriveInvoiceResponse(doc) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "could not derive response") - } - - resp.Header.JobId = jobID.String() - return resp, nil -} - -// Update handles the document update and anchoring -func (h *grpcHandler) Update(ctx context.Context, payload *clientinvoicepb.InvoiceUpdatePayload) (*clientinvoicepb.InvoiceResponse, error) { - apiLog.Debugf("Update request %v", payload) - ctxHeader, err := contextutil.Context(ctx, h.config) - if err != nil { - apiLog.Error(err) - return nil, err - } - - doc, err := h.service.DeriveFromUpdatePayload(ctxHeader, payload) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "could not derive update payload") - } - - doc, jobID, _, err := h.service.Update(ctxHeader, doc) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "could not update document") - } - - resp, err := h.service.DeriveInvoiceResponse(doc) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "could not derive response") - } - - resp.Header.JobId = jobID.String() - return resp, nil -} - -// GetVersion returns the requested version of the document -func (h *grpcHandler) GetVersion(ctx context.Context, getVersionRequest *clientinvoicepb.GetVersionRequest) (*clientinvoicepb.InvoiceResponse, error) { - apiLog.Debugf("Get version request %v", getVersionRequest) - ctxHeader, err := contextutil.Context(ctx, h.config) - if err != nil { - apiLog.Error(err) - return nil, err - } - - identifier, err := hexutil.Decode(getVersionRequest.DocumentId) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "identifier is invalid") - } - - version, err := hexutil.Decode(getVersionRequest.VersionId) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "version is invalid") - } - - model, err := h.service.GetVersion(ctxHeader, identifier, version) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "document not found") - } - - resp, err := h.service.DeriveInvoiceResponse(model) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "could not derive response") - } - - return resp, nil -} - -// Get returns the invoice the latest version of the document with given identifier -func (h *grpcHandler) Get(ctx context.Context, getRequest *clientinvoicepb.GetRequest) (*clientinvoicepb.InvoiceResponse, error) { - apiLog.Debugf("Get request %v", getRequest) - ctxHeader, err := contextutil.Context(ctx, h.config) - if err != nil { - apiLog.Error(err) - return nil, err - } - - identifier, err := hexutil.Decode(getRequest.DocumentId) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "identifier is an invalid hex string") - } - - model, err := h.service.GetCurrentVersion(ctxHeader, identifier) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "document not found") - } - - resp, err := h.service.DeriveInvoiceResponse(model) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "could not derive response") - } - - return resp, nil -} diff --git a/documents/invoice/handler_test.go b/documents/invoice/handler_test.go deleted file mode 100644 index 81d2dee94..000000000 --- a/documents/invoice/handler_test.go +++ /dev/null @@ -1,283 +0,0 @@ -// +build unit - -package invoice - -import ( - "context" - "testing" - - "github.com/centrifuge/centrifuge-protobufs/gen/go/coredocument" - "github.com/centrifuge/go-centrifuge/contextutil" - "github.com/centrifuge/go-centrifuge/documents" - "github.com/centrifuge/go-centrifuge/errors" - "github.com/centrifuge/go-centrifuge/jobs" - "github.com/centrifuge/go-centrifuge/protobufs/gen/go/document" - clientinvoicepb "github.com/centrifuge/go-centrifuge/protobufs/gen/go/invoice" - "github.com/centrifuge/go-centrifuge/testingutils/config" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - -type mockService struct { - Service - mock.Mock -} - -func (m *mockService) DeriveFromCreatePayload(ctx context.Context, payload *clientinvoicepb.InvoiceCreatePayload) (documents.Model, error) { - args := m.Called(ctx, payload) - model, _ := args.Get(0).(documents.Model) - return model, args.Error(1) -} - -func (m *mockService) Create(ctx context.Context, model documents.Model) (documents.Model, jobs.JobID, chan bool, error) { - args := m.Called(ctx, model) - model, _ = args.Get(0).(documents.Model) - return model, contextutil.Job(ctx), nil, args.Error(2) -} - -func (m *mockService) GetCurrentVersion(ctx context.Context, documentID []byte) (documents.Model, error) { - args := m.Called(ctx, documentID) - data, _ := args.Get(0).(documents.Model) - return data, args.Error(1) -} - -func (m *mockService) GetVersion(ctx context.Context, documentID []byte, version []byte) (documents.Model, error) { - args := m.Called(ctx, documentID, version) - data, _ := args.Get(0).(documents.Model) - return data, args.Error(1) -} - -func (m *mockService) DeriveInvoiceData(doc documents.Model) (*clientinvoicepb.InvoiceData, error) { - args := m.Called(doc) - data, _ := args.Get(0).(*clientinvoicepb.InvoiceData) - return data, args.Error(1) -} - -func (m *mockService) DeriveInvoiceResponse(doc documents.Model) (*clientinvoicepb.InvoiceResponse, error) { - args := m.Called(doc) - data, _ := args.Get(0).(*clientinvoicepb.InvoiceResponse) - return data, args.Error(1) -} - -func (m *mockService) Update(ctx context.Context, model documents.Model) (documents.Model, jobs.JobID, chan bool, error) { - args := m.Called(ctx, model) - doc1, _ := args.Get(0).(documents.Model) - return doc1, contextutil.Job(ctx), nil, args.Error(2) -} - -func (m *mockService) DeriveFromUpdatePayload(ctx context.Context, payload *clientinvoicepb.InvoiceUpdatePayload) (documents.Model, error) { - args := m.Called(ctx, payload) - doc, _ := args.Get(0).(documents.Model) - return doc, args.Error(1) -} - -func getHandler() *grpcHandler { - return &grpcHandler{service: &mockService{}, config: configService} -} - -func TestGRPCHandler_Create_derive_fail(t *testing.T) { - // DeriveFrom payload fails - h := getHandler() - srv := h.service.(*mockService) - srv.On("DeriveFromCreatePayload", mock.Anything, mock.Anything).Return(nil, errors.New("derive failed")).Once() - _, err := h.Create(testingconfig.HandlerContext(configService), nil) - srv.AssertExpectations(t) - assert.Error(t, err, "must be non nil") - assert.Contains(t, err.Error(), "derive failed") -} - -func TestGRPCHandler_Create_create_fail(t *testing.T) { - h := getHandler() - srv := h.service.(*mockService) - srv.On("DeriveFromCreatePayload", mock.Anything, mock.Anything).Return(new(Invoice), nil).Once() - srv.On("Create", mock.Anything, mock.Anything).Return(nil, jobs.NilJobID().String(), errors.New("create failed")).Once() - payload := &clientinvoicepb.InvoiceCreatePayload{Data: &clientinvoicepb.InvoiceData{GrossAmount: "300"}} - _, err := h.Create(testingconfig.HandlerContext(configService), payload) - srv.AssertExpectations(t) - assert.Error(t, err, "must be non nil") - assert.Contains(t, err.Error(), "create failed") -} - -type mockModel struct { - documents.Model - mock.Mock - CoreDocument *coredocumentpb.CoreDocument -} - -func (m *mockModel) ID() []byte { - args := m.Called() - id, _ := args.Get(0).([]byte) - return id -} - -func TestGRPCHandler_Create_DeriveInvoiceResponse_fail(t *testing.T) { - h := getHandler() - srv := h.service.(*mockService) - model := new(Invoice) - srv.On("DeriveFromCreatePayload", mock.Anything, mock.Anything).Return(model, nil).Once() - srv.On("Create", mock.Anything, mock.Anything).Return(model, jobs.NilJobID().String(), nil).Once() - srv.On("DeriveInvoiceResponse", mock.Anything).Return(nil, errors.New("derive response failed")) - payload := &clientinvoicepb.InvoiceCreatePayload{Data: &clientinvoicepb.InvoiceData{Currency: "EUR"}} - _, err := h.Create(testingconfig.HandlerContext(configService), payload) - srv.AssertExpectations(t) - assert.Error(t, err, "must be non nil") - assert.Contains(t, err.Error(), "derive response failed") -} - -func TestGrpcHandler_Create(t *testing.T) { - h := getHandler() - srv := h.service.(*mockService) - model := new(Invoice) - txID := jobs.NewJobID() - payload := &clientinvoicepb.InvoiceCreatePayload{ - Data: &clientinvoicepb.InvoiceData{GrossAmount: "300"}, - WriteAccess: []string{"0x010203040506"}} - response := &clientinvoicepb.InvoiceResponse{Header: new(documentpb.ResponseHeader)} - srv.On("DeriveFromCreatePayload", mock.Anything, mock.Anything).Return(model, nil).Once() - srv.On("Create", mock.Anything, mock.Anything).Return(model, txID.String(), nil).Once() - srv.On("DeriveInvoiceResponse", model).Return(response, nil) - res, err := h.Create(testingconfig.HandlerContext(configService), payload) - srv.AssertExpectations(t) - assert.Nil(t, err, "must be nil") - assert.NotNil(t, res, "must be non nil") - assert.Equal(t, res, response) -} - -func TestGrpcHandler_Get_invalid_input(t *testing.T) { - identifier := "0x01010101" - identifierBytes, _ := hexutil.Decode(identifier) - h := getHandler() - srv := h.service.(*mockService) - payload := &clientinvoicepb.GetRequest{DocumentId: "invalid"} - - res, err := h.Get(testingconfig.HandlerContext(configService), payload) - assert.Nil(t, res) - assert.EqualError(t, err, "identifier is an invalid hex string: hex string without 0x prefix") - - payload.DocumentId = identifier - srv.On("GetCurrentVersion", mock.Anything, identifierBytes).Return(nil, errors.New("not found")) - res, err = h.Get(testingconfig.HandlerContext(configService), payload) - srv.AssertExpectations(t) - assert.Nil(t, res) - assert.EqualError(t, err, "document not found: not found") -} - -func TestGrpcHandler_Get(t *testing.T) { - identifier := "0x01010101" - identifierBytes, _ := hexutil.Decode(identifier) - h := getHandler() - srv := h.service.(*mockService) - model := new(mockModel) - payload := &clientinvoicepb.GetRequest{DocumentId: identifier} - response := &clientinvoicepb.InvoiceResponse{} - srv.On("GetCurrentVersion", mock.Anything, identifierBytes).Return(model, nil) - srv.On("DeriveInvoiceResponse", model).Return(response, nil) - res, err := h.Get(testingconfig.HandlerContext(configService), payload) - model.AssertExpectations(t) - srv.AssertExpectations(t) - assert.Nil(t, err, "must be nil") - assert.NotNil(t, res, "must be non nil") - assert.Equal(t, res, response) -} - -func TestGrpcHandler_GetVersion_invalid_input(t *testing.T) { - h := getHandler() - srv := h.service.(*mockService) - payload := &clientinvoicepb.GetVersionRequest{DocumentId: "0x0x", VersionId: "0x00"} - res, err := h.GetVersion(testingconfig.HandlerContext(configService), payload) - assert.Error(t, err) - assert.EqualError(t, err, "identifier is invalid: invalid hex string") - payload.VersionId = "0x0x" - payload.DocumentId = "0x01" - - res, err = h.GetVersion(testingconfig.HandlerContext(configService), payload) - assert.Error(t, err) - assert.EqualError(t, err, "version is invalid: invalid hex string") - payload.VersionId = "0x00" - payload.DocumentId = "0x01" - - mockErr := errors.New("not found") - srv.On("GetVersion", mock.Anything, []byte{0x01}, []byte{0x00}).Return(nil, mockErr) - res, err = h.GetVersion(testingconfig.HandlerContext(configService), payload) - srv.AssertExpectations(t) - assert.EqualError(t, err, "document not found: not found") - assert.Nil(t, res) -} - -func TestGrpcHandler_GetVersion(t *testing.T) { - h := getHandler() - srv := h.service.(*mockService) - model := new(mockModel) - payload := &clientinvoicepb.GetVersionRequest{DocumentId: "0x01", VersionId: "0x00"} - - response := &clientinvoicepb.InvoiceResponse{} - srv.On("GetVersion", mock.Anything, []byte{0x01}, []byte{0x00}).Return(model, nil) - srv.On("DeriveInvoiceResponse", model).Return(response, nil) - res, err := h.GetVersion(testingconfig.HandlerContext(configService), payload) - model.AssertExpectations(t) - srv.AssertExpectations(t) - assert.Nil(t, err) - assert.NotNil(t, res) - assert.Equal(t, res, response) -} - -func TestGrpcHandler_Update_derive_fail(t *testing.T) { - h := getHandler() - srv := h.service.(*mockService) - payload := &clientinvoicepb.InvoiceUpdatePayload{DocumentId: "0x010201"} - srv.On("DeriveFromUpdatePayload", mock.Anything, payload).Return(nil, errors.New("derive error")).Once() - res, err := h.Update(testingconfig.HandlerContext(configService), payload) - srv.AssertExpectations(t) - assert.Error(t, err) - assert.Contains(t, err.Error(), "derive error") - assert.Nil(t, res) -} - -func TestGrpcHandler_Update_update_fail(t *testing.T) { - h := getHandler() - srv := h.service.(*mockService) - model := &mockModel{} - ctx := testingconfig.HandlerContext(configService) - payload := &clientinvoicepb.InvoiceUpdatePayload{DocumentId: "0x010201"} - srv.On("DeriveFromUpdatePayload", mock.Anything, payload).Return(model, nil).Once() - srv.On("Update", mock.Anything, model).Return(nil, jobs.NilJobID().String(), errors.New("update error")).Once() - res, err := h.Update(ctx, payload) - srv.AssertExpectations(t) - assert.Error(t, err) - assert.Contains(t, err.Error(), "update error") - assert.Nil(t, res) -} - -func TestGrpcHandler_Update_derive_response_fail(t *testing.T) { - h := getHandler() - srv := h.service.(*mockService) - model := &mockModel{} - ctx := testingconfig.HandlerContext(configService) - payload := &clientinvoicepb.InvoiceUpdatePayload{DocumentId: "0x010201"} - srv.On("DeriveFromUpdatePayload", mock.Anything, payload).Return(model, nil).Once() - srv.On("Update", mock.Anything, model).Return(model, jobs.NilJobID().String(), nil).Once() - srv.On("DeriveInvoiceResponse", model).Return(nil, errors.New("derive response error")).Once() - res, err := h.Update(ctx, payload) - srv.AssertExpectations(t) - assert.Error(t, err) - assert.Contains(t, err.Error(), "derive response error") - assert.Nil(t, res) -} - -func TestGrpcHandler_Update(t *testing.T) { - h := getHandler() - srv := h.service.(*mockService) - model := &mockModel{} - ctx := testingconfig.HandlerContext(configService) - jobID := jobs.NewJobID() - payload := &clientinvoicepb.InvoiceUpdatePayload{DocumentId: "0x010201"} - resp := &clientinvoicepb.InvoiceResponse{Header: new(documentpb.ResponseHeader)} - srv.On("DeriveFromUpdatePayload", mock.Anything, payload).Return(model, nil).Once() - srv.On("Update", mock.Anything, model).Return(model, jobID.String(), nil).Once() - srv.On("DeriveInvoiceResponse", model).Return(resp, nil).Once() - res, err := h.Update(ctx, payload) - srv.AssertExpectations(t) - assert.Nil(t, err) - assert.Equal(t, resp, res) -} diff --git a/documents/invoice/model.go b/documents/invoice/model.go index 7b7d35bfa..95276738f 100644 --- a/documents/invoice/model.go +++ b/documents/invoice/model.go @@ -1,6 +1,7 @@ package invoice import ( + "context" "encoding/json" "reflect" "time" @@ -11,19 +12,20 @@ import ( "github.com/centrifuge/go-centrifuge/documents" "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/go-centrifuge/identity" - clientinvoicepb "github.com/centrifuge/go-centrifuge/protobufs/gen/go/invoice" "github.com/centrifuge/go-centrifuge/utils/timeutils" "github.com/centrifuge/precise-proofs/proofs" "github.com/centrifuge/precise-proofs/proofs/proto" "github.com/ethereum/go-ethereum/common" "github.com/golang/protobuf/proto" "github.com/golang/protobuf/ptypes/any" + "github.com/jinzhu/copier" ) const ( prefix string = "invoice" - scheme = prefix + // Scheme is the scheme used by invoice documents + Scheme = prefix // ErrInvoiceInvalidData sentinel error when data unmarshal is failed. ErrInvoiceInvalidData = errors.Error("invalid invoice data") @@ -75,15 +77,15 @@ type Data struct { ShipToZipcode string `json:"ship_to_zipcode"` ShipToState string `json:"ship_to_state"` ShipToCountry string `json:"ship_to_country"` - Currency string `json:"currency"` // ISO currency code - GrossAmount *documents.Decimal `json:"gross_amount"` // invoice amount including tax - NetAmount *documents.Decimal `json:"net_amount"` // invoice amount excluding tax - TaxAmount *documents.Decimal `json:"tax_amount"` - TaxRate *documents.Decimal `json:"tax_rate"` + Currency string `json:"currency"` // ISO currency code + GrossAmount *documents.Decimal `json:"gross_amount" swaggertype:"primitive,string"` // invoice amount including tax + NetAmount *documents.Decimal `json:"net_amount" swaggertype:"primitive,string"` // invoice amount excluding tax + TaxAmount *documents.Decimal `json:"tax_amount" swaggertype:"primitive,string"` + TaxRate *documents.Decimal `json:"tax_rate" swaggertype:"primitive,string"` TaxOnLineLevel bool `json:"tax_on_line_level"` - Recipient *identity.DID `json:"recipient,string"` // centrifuge ID of the recipient - Sender *identity.DID `json:"sender,string"` // centrifuge ID of the sender - Payee *identity.DID `json:"payee,string"` // centrifuge ID of the payee + Recipient *identity.DID `json:"recipient,string" swaggertype:"primitive,string"` // centrifuge ID of the recipient + Sender *identity.DID `json:"sender,string" swaggertype:"primitive,string"` // centrifuge ID of the sender + Payee *identity.DID `json:"payee,string" swaggertype:"primitive,string"` // centrifuge ID of the payee Comment string `json:"comment"` ShippingTerms string `json:"shipping_terms"` RequesterEmail string `json:"requester_email"` @@ -92,10 +94,10 @@ type Data struct { IsCreditNote bool `json:"is_credit_note"` CreditNoteInvoiceNumber string `json:"credit_note_invoice_number"` CreditForInvoiceDate *time.Time `json:"credit_for_invoice_date"` - DateDue *time.Time `json:"date_due"` - DatePaid *time.Time `json:"date_paid"` - DateUpdated *time.Time `json:"date_updated"` - DateCreated *time.Time `json:"date_created"` + DateDue *time.Time `json:"date_due" swaggertype:"primitive,string"` + DatePaid *time.Time `json:"date_paid" swaggertype:"primitive,string"` + DateUpdated *time.Time `json:"date_updated" swaggertype:"primitive,string"` + DateCreated *time.Time `json:"date_created" swaggertype:"primitive,string"` Attachments []*documents.BinaryAttachment `json:"attachments"` LineItems []*LineItem `json:"line_items"` PaymentDetails []*documents.PaymentDetails `json:"payment_details"` @@ -113,14 +115,14 @@ type LineItem struct { ItemNumber string `json:"item_number"` Description string `json:"description"` SenderPartNo string `json:"sender_part_no"` - PricePerUnit *documents.Decimal `json:"price_per_unit"` - Quantity *documents.Decimal `json:"quantity"` + PricePerUnit *documents.Decimal `json:"price_per_unit" swaggertype:"primitive,string"` + Quantity *documents.Decimal `json:"quantity" swaggertype:"primitive,string"` UnitOfMeasure string `json:"unit_of_measure"` - NetWeight *documents.Decimal `json:"net_weight"` - TaxAmount *documents.Decimal `json:"tax_amount"` - TaxRate *documents.Decimal `json:"tax_rate"` - TaxCode *documents.Decimal `json:"tax_code"` - TotalAmount *documents.Decimal `json:"total_amount"` // the total amount of the line item + NetWeight *documents.Decimal `json:"net_weight" swaggertype:"primitive,string"` + TaxAmount *documents.Decimal `json:"tax_amount" swaggertype:"primitive,string"` + TaxRate *documents.Decimal `json:"tax_rate" swaggertype:"primitive,string"` + TaxCode *documents.Decimal `json:"tax_code" swaggertype:"primitive,string"` + TotalAmount *documents.Decimal `json:"total_amount" swaggertype:"primitive,string"` // the total amount of the line item PurchaseOrderNumber string `json:"purchase_order_number"` PurchaseOrderItemNumber string `json:"purchase_order_item_number"` DeliveryNoteNumber string `json:"delivery_note_number"` @@ -130,96 +132,10 @@ type LineItem struct { type TaxItem struct { ItemNumber string `json:"item_number"` InvoiceItemNumber string `json:"invoice_item_number"` - TaxAmount *documents.Decimal `json:"tax_amount"` - TaxRate *documents.Decimal `json:"tax_rate"` - TaxCode *documents.Decimal `json:"tax_code"` - TaxBaseAmount *documents.Decimal `json:"tax_base_amount"` -} - -// getClientData returns the client data from the invoice model -func (i *Invoice) getClientData() (*clientinvoicepb.InvoiceData, error) { - d := i.Data - decs := documents.DecimalsToStrings(d.GrossAmount, d.NetAmount, d.TaxAmount, d.TaxRate) - dids := identity.DIDsToStrings(d.Recipient, d.Sender, d.Payee) - - pd, err := documents.ToClientPaymentDetails(d.PaymentDetails) - if err != nil { - return nil, err - } - - pts, err := timeutils.ToProtoTimestamps(d.CreditForInvoiceDate, d.DateDue, d.DatePaid, d.DateCreated, d.DateUpdated) - if err != nil { - return nil, err - } - - return &clientinvoicepb.InvoiceData{ - Number: d.Number, - Status: d.Status, - SenderInvoiceId: d.SenderInvoiceID, - RecipientInvoiceId: d.RecipientInvoiceID, - SenderCompanyName: d.SenderCompanyName, - SenderContactPersonName: d.SenderContactPersonName, - SenderStreet1: d.SenderStreet1, - SenderStreet2: d.SenderStreet2, - SenderCity: d.SenderCity, - SenderZipcode: d.SenderZipcode, - SenderState: d.SenderState, - SenderCountry: d.SenderCountry, - BillToCompanyName: d.BillToCompanyName, - BillToContactPersonName: d.BillToContactPersonName, - BillToStreet1: d.BillToStreet1, - BillToStreet2: d.BillToStreet2, - BillToCity: d.BillToCity, - BillToZipcode: d.BillToZipcode, - BillToState: d.BillToState, - BillToCountry: d.BillToCountry, - BillToLocalTaxId: d.BillToLocalTaxID, - BillToVatNumber: d.BillToVatNumber, - RemitToCompanyName: d.RemitToCompanyName, - RemitToContactPersonName: d.RemitToContactPersonName, - RemitToStreet1: d.RemitToStreet1, - RemitToStreet2: d.RemitToStreet2, - RemitToCity: d.RemitToCity, - RemitToCountry: d.RemitToCountry, - RemitToState: d.RemitToState, - RemitToZipcode: d.RemitToZipcode, - RemitToLocalTaxId: d.RemitToLocalTaxID, - RemitToTaxCountry: d.RemitToTaxCountry, - RemitToVatNumber: d.RemitToVatNumber, - ShipToCompanyName: d.ShipToCompanyName, - ShipToContactPersonName: d.ShipToContactPersonName, - ShipToStreet1: d.ShipToStreet1, - ShipToStreet2: d.ShipToStreet2, - ShipToCity: d.ShipToCity, - ShipToState: d.ShipToState, - ShipToCountry: d.ShipToCountry, - ShipToZipcode: d.ShipToZipcode, - Currency: d.Currency, - GrossAmount: decs[0], - NetAmount: decs[1], - TaxAmount: decs[2], - TaxRate: decs[3], - TaxOnLineLevel: d.TaxOnLineLevel, - Recipient: dids[0], - Sender: dids[1], - Payee: dids[2], - Comment: d.Comment, - ShippingTerms: d.ShippingTerms, - RequesterEmail: d.RequesterEmail, - RequesterName: d.RequesterName, - DeliveryNumber: d.DeliveryNumber, - IsCreditNote: d.IsCreditNote, - CreditNoteInvoiceNumber: d.CreditNoteInvoiceNumber, - CreditForInvoiceDate: pts[0], - DateDue: pts[1], - DatePaid: pts[2], - DateCreated: pts[3], - DateUpdated: pts[4], - Attachments: documents.ToClientAttachments(d.Attachments), - LineItems: toClientLineItems(d.LineItems), - PaymentDetails: pd, - TaxItems: toClientTaxItems(d.TaxItems), - }, nil + TaxAmount *documents.Decimal `json:"tax_amount" swaggertype:"primitive,string"` + TaxRate *documents.Decimal `json:"tax_rate" swaggertype:"primitive,string"` + TaxCode *documents.Decimal `json:"tax_code" swaggertype:"primitive,string"` + TaxBaseAmount *documents.Decimal `json:"tax_base_amount" swaggertype:"primitive,string"` } // createP2PProtobuf returns centrifuge protobuf specific invoiceData @@ -322,140 +238,6 @@ func (i *Invoice) createP2PProtobuf() (data *invoicepb.InvoiceData, err error) { } -// InitInvoiceInput initialize the model based on the received parameters from the rest api call -func (i *Invoice) InitInvoiceInput(payload *clientinvoicepb.InvoiceCreatePayload, self identity.DID) error { - err := i.initInvoiceFromData(payload.Data) - if err != nil { - return err - } - - cs, err := documents.FromClientCollaboratorAccess(payload.ReadAccess, payload.WriteAccess) - if err != nil { - return err - } - cs.ReadWriteCollaborators = append(cs.ReadWriteCollaborators, self) - - attrs, err := documents.FromClientAttributes(payload.Attributes) - if err != nil { - return err - } - cd, err := documents.NewCoreDocument(compactPrefix(), cs, attrs) - if err != nil { - return errors.New("failed to init core document: %v", err) - } - - i.CoreDocument = cd - return nil -} - -// initInvoiceFromData initialises invoice from invoiceData -func (i *Invoice) initInvoiceFromData(data *clientinvoicepb.InvoiceData) error { - decs, err := documents.StringsToDecimals(data.GrossAmount, data.NetAmount, data.TaxAmount, data.TaxRate) - if err != nil { - return err - } - - dids, err := identity.StringsToDIDs(data.Recipient, data.Sender, data.Payee) - if err != nil { - return err - } - - atts, err := documents.FromClientAttachments(data.Attachments) - if err != nil { - return err - } - - li, err := fromClientLineItems(data.LineItems) - if err != nil { - return err - } - - pd, err := documents.FromClientPaymentDetails(data.PaymentDetails) - if err != nil { - return err - } - - ti, err := fromClientTaxItems(data.TaxItems) - if err != nil { - return err - } - - tms, err := timeutils.FromProtoTimestamps(data.CreditForInvoiceDate, data.DateDue, data.DatePaid, data.DateCreated, data.DateUpdated) - if err != nil { - return err - } - - var d Data - d.Number = data.Number - d.Status = data.Status - d.SenderInvoiceID = data.SenderInvoiceId - d.RecipientInvoiceID = data.RecipientInvoiceId - d.SenderCompanyName = data.SenderCompanyName - d.SenderContactPersonName = data.SenderContactPersonName - d.SenderStreet1 = data.SenderStreet1 - d.SenderStreet2 = data.SenderStreet2 - d.SenderCity = data.SenderCity - d.SenderZipcode = data.SenderZipcode - d.SenderState = data.SenderState - d.SenderCountry = data.SenderCountry - d.BillToCompanyName = data.BillToCompanyName - d.BillToContactPersonName = data.BillToContactPersonName - d.BillToStreet1 = data.BillToStreet1 - d.BillToStreet2 = data.BillToStreet2 - d.BillToCity = data.BillToCity - d.BillToZipcode = data.BillToZipcode - d.BillToState = data.BillToState - d.BillToCountry = data.BillToCountry - d.BillToVatNumber = data.BillToVatNumber - d.BillToLocalTaxID = data.BillToLocalTaxId - d.RemitToCompanyName = data.RemitToCompanyName - d.RemitToContactPersonName = data.RemitToContactPersonName - d.RemitToStreet1 = data.RemitToStreet1 - d.RemitToStreet2 = data.RemitToStreet2 - d.RemitToCity = data.RemitToCity - d.RemitToZipcode = data.RemitToZipcode - d.RemitToState = data.RemitToState - d.RemitToCountry = data.RemitToCountry - d.RemitToVatNumber = data.RemitToVatNumber - d.RemitToLocalTaxID = data.RemitToLocalTaxId - d.RemitToTaxCountry = data.RemitToTaxCountry - d.ShipToCompanyName = data.ShipToCompanyName - d.ShipToContactPersonName = data.ShipToContactPersonName - d.ShipToStreet1 = data.ShipToStreet1 - d.ShipToStreet2 = data.ShipToStreet2 - d.ShipToCity = data.ShipToCity - d.ShipToZipcode = data.ShipToZipcode - d.ShipToState = data.ShipToState - d.ShipToCountry = data.ShipToCountry - d.Currency = data.Currency - d.GrossAmount = decs[0] - d.NetAmount = decs[1] - d.TaxAmount = decs[2] - d.TaxRate = decs[3] - d.TaxOnLineLevel = data.TaxOnLineLevel - d.Recipient = dids[0] - d.Sender = dids[1] - d.Payee = dids[2] - d.Comment = data.Comment - d.ShippingTerms = data.ShippingTerms - d.RequesterEmail = data.RequesterEmail - d.RequesterName = data.RequesterName - d.DeliveryNumber = data.DeliveryNumber - d.IsCreditNote = data.IsCreditNote - d.CreditNoteInvoiceNumber = data.CreditNoteInvoiceNumber - d.CreditForInvoiceDate = tms[0] - d.DateDue = tms[1] - d.DatePaid = tms[2] - d.DateCreated = tms[3] - d.DateUpdated = tms[4] - d.Attachments = atts - d.LineItems = li - d.PaymentDetails = pd - d.TaxItems = ti - i.Data = d - return nil -} - // loadFromP2PProtobuf loads the invoice from centrifuge protobuf invoice data func (i *Invoice) loadFromP2PProtobuf(data *invoicepb.InvoiceData) error { decs, err := documents.BytesToDecimals(data.GrossAmount, data.NetAmount, data.TaxAmount, data.TaxRate) @@ -638,7 +420,11 @@ func (i *Invoice) getDocumentDataTree() (tree *proofs.DocumentTree, err error) { if i.CoreDocument == nil { return nil, errors.New("getDocumentDataTree error CoreDocument not set") } - t := i.CoreDocument.DefaultTreeWithPrefix(prefix, compactPrefix()) + t, err := i.CoreDocument.DefaultTreeWithPrefix(prefix, compactPrefix()) + if err != nil { + return nil, err + } + err = t.AddLeavesFromDocument(invProto) if err != nil { return nil, errors.New("getDocumentDataTree error %v", err) @@ -666,22 +452,6 @@ func (*Invoice) DocumentType() string { return documenttypes.InvoiceDataTypeUrl } -// PrepareNewVersion prepares new version from the old invoice. -func (i *Invoice) PrepareNewVersion(old documents.Model, data *clientinvoicepb.InvoiceData, collaborators documents.CollaboratorsAccess, attrs map[documents.AttrKey]documents.Attribute) error { - err := i.initInvoiceFromData(data) - if err != nil { - return err - } - - oldCD := old.(*Invoice).CoreDocument - i.CoreDocument, err = oldCD.PrepareNewVersion(compactPrefix(), collaborators, attrs) - if err != nil { - return err - } - - return nil -} - // AddNFT adds NFT to the Invoice. func (i *Invoice) AddNFT(grantReadAccess bool, registry common.Address, tokenID []byte) error { cd, err := i.CoreDocument.AddNFT(grantReadAccess, registry, tokenID) @@ -795,24 +565,21 @@ func (i *Invoice) GetData() interface{} { } // loadData unmarshals json blob to Data. -func (i *Invoice) loadData(data []byte) error { - var d Data - err := json.Unmarshal(data, &d) +func loadData(data []byte, v *Data) error { + err := json.Unmarshal(data, v) if err != nil { return err } - i.Data = d return nil } -// unpackFromCreatePayload unpacks the invoice data from the Payload. -func (i *Invoice) unpackFromCreatePayload(did identity.DID, payload documents.CreatePayload) error { - if err := i.loadData(payload.Data); err != nil { +// DeriveFromCreatePayload unpacks the invoice data from the Payload. +func (i *Invoice) DeriveFromCreatePayload(_ context.Context, payload documents.CreatePayload) error { + if err := loadData(payload.Data, &i.Data); err != nil { return errors.NewTypedError(ErrInvoiceInvalidData, err) } - payload.Collaborators.ReadWriteCollaborators = append(payload.Collaborators.ReadWriteCollaborators, did) cd, err := documents.NewCoreDocument(compactPrefix(), payload.Collaborators, payload.Attributes) if err != nil { return errors.NewTypedError(documents.ErrCDCreate, err) @@ -822,9 +589,10 @@ func (i *Invoice) unpackFromCreatePayload(did identity.DID, payload documents.Cr return nil } -// unpackFromUpdatePayload unpacks the update payload and prepares a new version. -func (i *Invoice) unpackFromUpdatePayload(old *Invoice, payload documents.UpdatePayload) error { - if err := i.loadData(payload.Data); err != nil { +// unpackFromUpdatePayloadOld unpacks the update payload and prepares a new version. +// deprecated +func (i *Invoice) unpackFromUpdatePayloadOld(old *Invoice, payload documents.UpdatePayload) error { + if err := loadData(payload.Data, &i.Data); err != nil { return errors.NewTypedError(ErrInvoiceInvalidData, err) } @@ -837,7 +605,57 @@ func (i *Invoice) unpackFromUpdatePayload(old *Invoice, payload documents.Update return nil } -// Scheme returns the invoice scheme. +// DeriveFromUpdatePayload unpacks the update payload and prepares a new version. +func (i *Invoice) DeriveFromUpdatePayload(_ context.Context, payload documents.UpdatePayload) (documents.Model, error) { + d, err := i.patch(payload) + if err != nil { + return nil, err + } + + ncd, err := i.CoreDocument.PrepareNewVersion(compactPrefix(), payload.Collaborators, payload.Attributes) + if err != nil { + return nil, err + } + + return &Invoice{ + Data: d, + CoreDocument: ncd, + }, nil +} + +// patch merges payload into model data +func (i *Invoice) patch(payload documents.UpdatePayload) (Data, error) { + var d Data + err := copier.Copy(&d, &i.Data) + if err != nil { + return d, err + } + + if err := loadData(payload.Data, &d); err != nil { + return Data{}, errors.NewTypedError(ErrInvoiceInvalidData, err) + } + + return d, nil +} + +// Patch merges payload data into model +func (i *Invoice) Patch(payload documents.UpdatePayload) error { + d, err := i.patch(payload) + if err != nil { + return err + } + + ncd, err := i.CoreDocument.Patch(compactPrefix(), payload.Collaborators, payload.Attributes) + if err != nil { + return err + } + + i.Data = d + i.CoreDocument = ncd + return nil +} + +// Scheme returns the invoice Scheme. func (i *Invoice) Scheme() string { - return scheme + return Scheme } diff --git a/documents/invoice/model_test.go b/documents/invoice/model_test.go index 8b3c83a40..ad67d627a 100644 --- a/documents/invoice/model_test.go +++ b/documents/invoice/model_test.go @@ -3,6 +3,8 @@ package invoice import ( + "context" + "crypto/sha256" "encoding/json" "fmt" "os" @@ -24,7 +26,6 @@ import ( "github.com/centrifuge/go-centrifuge/identity/ideth" "github.com/centrifuge/go-centrifuge/jobs" "github.com/centrifuge/go-centrifuge/p2p" - clientinvoicepb "github.com/centrifuge/go-centrifuge/protobufs/gen/go/invoice" "github.com/centrifuge/go-centrifuge/queue" "github.com/centrifuge/go-centrifuge/storage/leveldb" "github.com/centrifuge/go-centrifuge/testingutils/config" @@ -34,23 +35,27 @@ import ( "github.com/centrifuge/go-centrifuge/utils" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/golang/protobuf/ptypes/any" - "github.com/golang/protobuf/ptypes/timestamp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) var ctx = map[string]interface{}{} var cfg config.Configuration -var configService config.Service var defaultDID = testingidentity.GenerateRandomDID() +type mockModel struct { + documents.Model + mock.Mock + CoreDocument *coredocumentpb.CoreDocument +} + func TestMain(m *testing.M) { ethClient := ðereum.MockEthClient{} ethClient.On("GetEthClient").Return(nil) ctx[ethereum.BootstrappedEthereumClient] = ethClient jobMan := &testingjobs.MockJobManager{} ctx[jobs.BootstrappedService] = jobMan - done := make(chan bool) + done := make(chan error) jobMan.On("ExecuteWithinJob", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(jobs.NilJobID(), done, nil) ctx[bootstrap.BootstrappedInvoiceUnpaid] = new(testingdocuments.MockRegistry) ibootstrappers := []bootstrap.TestBootstrapper{ @@ -70,7 +75,6 @@ func TestMain(m *testing.M) { bootstrap.RunTestBootstrappers(ibootstrappers, ctx) cfg = ctx[bootstrap.BootstrappedConfig].(config.Configuration) cfg.Set("identityId", did.String()) - configService = ctx[config.BootstrappedConfigStorage].(config.Service) result := m.Run() bootstrap.RunTestTeardown(ibootstrappers) os.Exit(result) @@ -82,8 +86,7 @@ func TestInvoice_PackCoreDocument(t *testing.T) { assert.NoError(t, err) inv := new(Invoice) - assert.NoError(t, inv.InitInvoiceInput(testingdocuments.CreateInvoicePayload(), did)) - + assert.NoError(t, inv.DeriveFromCreatePayload(ctx, CreateInvoicePayload(t, []identity.DID{did}))) cd, err := inv.PackCoreDocument() assert.NoError(t, err) assert.NotNil(t, cd.EmbeddedData) @@ -94,20 +97,20 @@ func TestInvoice_JSON(t *testing.T) { ctx := testingconfig.CreateAccountContext(t, cfg) did, err := contextutil.AccountDID(ctx) assert.NoError(t, err) - assert.NoError(t, inv.InitInvoiceInput(testingdocuments.CreateInvoicePayload(), did)) + assert.NoError(t, inv.DeriveFromCreatePayload(ctx, CreateInvoicePayload(t, []identity.DID{did}))) cd, err := inv.PackCoreDocument() assert.NoError(t, err) jsonBytes, err := inv.JSON() - assert.Nil(t, err, "marshal to json didn't work correctly") + assert.NoError(t, err, "marshal to json didn't work correctly") assert.True(t, json.Valid(jsonBytes), "json format not correct") inv = new(Invoice) err = inv.FromJSON(jsonBytes) - assert.Nil(t, err, "unmarshal JSON didn't work correctly") + assert.NoError(t, err, "unmarshal JSON didn't work correctly") ncd, err := inv.PackCoreDocument() - assert.Nil(t, err, "JSON unmarshal damaged invoice variables") + assert.NoError(t, err, "JSON unmarshal damaged invoice variables") assert.Equal(t, cd, ncd) } @@ -127,105 +130,28 @@ func TestInvoiceModel_UnpackCoreDocument(t *testing.T) { err = model.UnpackCoreDocument(coredocumentpb.CoreDocument{ EmbeddedData: &any.Any{ Value: utils.RandomSlice(32), - TypeUrl: documenttypes.InvoiceDataTypeUrl, + TypeUrl: documenttypes.EntityDataTypeUrl, }, }) assert.Error(t, err) // successful - inv, cd := createCDWithEmbeddedInvoice(t) - err = model.UnpackCoreDocument(cd) - assert.NoError(t, err) - d, err := model.getClientData() - assert.NoError(t, err) - d1, err := inv.(*Invoice).getClientData() - assert.NoError(t, err) - assert.Equal(t, d, d1) + inv, cd := CreateInvoiceWithEmbedCD(t, nil, did, nil) + assert.NoError(t, model.UnpackCoreDocument(cd)) + data := model.GetData() + data1 := inv.GetData() + assert.Equal(t, data, data1) assert.Equal(t, model.ID(), inv.ID()) assert.Equal(t, model.CurrentVersion(), inv.CurrentVersion()) assert.Equal(t, model.PreviousVersion(), inv.PreviousVersion()) } -func TestInvoiceModel_getClientData(t *testing.T) { - invData := testingdocuments.CreateInvoiceData() - inv := new(Invoice) - inv.CoreDocument = new(documents.CoreDocument) - err := inv.loadFromP2PProtobuf(&invData) - assert.NoError(t, err) - - data, err := inv.getClientData() - assert.NoError(t, err) - assert.NotNil(t, data, "invoice data should not be nil") - assert.Equal(t, data.GrossAmount, data.GrossAmount, "gross amount must match") - assert.Equal(t, data.Recipient, inv.Data.Recipient.String(), "recipient should match") - assert.Equal(t, data.Sender, inv.Data.Sender.String(), "sender should match") - assert.Equal(t, data.Payee, inv.Data.Payee.String(), "payee should match") -} - -func TestInvoiceModel_InitInvoiceInput(t *testing.T) { - ctx := testingconfig.CreateAccountContext(t, cfg) - did, err := contextutil.AccountDID(ctx) - assert.NoError(t, err) - - // fail recipient - data := &clientinvoicepb.InvoiceData{ - Recipient: "some recipient", - } - inv := new(Invoice) - err = inv.InitInvoiceInput(&clientinvoicepb.InvoiceCreatePayload{Data: data}, did) - assert.Error(t, err, "must return err") - assert.Contains(t, err.Error(), "malformed address provided") - assert.Nil(t, inv.Data.Recipient) - assert.Nil(t, inv.Data.Sender) - assert.Nil(t, inv.Data.Payee) - - recipientDID := testingidentity.GenerateRandomDID() - data.Recipient = recipientDID.String() - err = inv.InitInvoiceInput(&clientinvoicepb.InvoiceCreatePayload{Data: data}, did) - assert.Nil(t, err) - assert.NotNil(t, inv.Data.Recipient) - assert.Nil(t, inv.Data.Sender) - assert.Nil(t, inv.Data.Payee) - - senderDID := testingidentity.GenerateRandomDID() - data.Sender = senderDID.String() - err = inv.InitInvoiceInput(&clientinvoicepb.InvoiceCreatePayload{Data: data}, did) - assert.Nil(t, err) - assert.NotNil(t, inv.Data.Recipient) - assert.NotNil(t, inv.Data.Sender) - assert.Nil(t, inv.Data.Payee) - - payeeDID := testingidentity.GenerateRandomDID() - data.Payee = payeeDID.String() - err = inv.InitInvoiceInput(&clientinvoicepb.InvoiceCreatePayload{Data: data}, did) - assert.Nil(t, err) - assert.NotNil(t, inv.Data.Recipient) - assert.NotNil(t, inv.Data.Sender) - assert.NotNil(t, inv.Data.Payee) - - collabs := []string{"0x010102040506", "some id"} - err = inv.InitInvoiceInput(&clientinvoicepb.InvoiceCreatePayload{Data: data, WriteAccess: collabs}, did) - assert.Error(t, err) - assert.Contains(t, err.Error(), "malformed address provided") - - collab1, err := identity.NewDIDFromString("0xBAEb33a61f05e6F269f1c4b4CFF91A901B54DaF7") - assert.NoError(t, err) - collab2, err := identity.NewDIDFromString("0xBAEb33a61f05e6F269f1c4b4CFF91A901B54DaF3") - assert.NoError(t, err) - collabs = []string{collab1.String(), collab2.String()} - err = inv.InitInvoiceInput(&clientinvoicepb.InvoiceCreatePayload{Data: data, WriteAccess: collabs}, did) - assert.Nil(t, err, "must be nil") - assert.Equal(t, inv.Data.Sender[:], senderDID[:]) - assert.Equal(t, inv.Data.Payee[:], payeeDID[:]) - assert.Equal(t, inv.Data.Recipient[:], recipientDID[:]) -} - func TestInvoiceModel_calculateDataRoot(t *testing.T) { ctx := testingconfig.CreateAccountContext(t, cfg) did, err := contextutil.AccountDID(ctx) assert.NoError(t, err) m := new(Invoice) - err = m.InitInvoiceInput(testingdocuments.CreateInvoicePayload(), did) + assert.NoError(t, m.DeriveFromCreatePayload(ctx, CreateInvoicePayload(t, []identity.DID{did}))) assert.Nil(t, err, "Init must pass") dr, err := m.CalculateDataRoot() @@ -234,7 +160,7 @@ func TestInvoiceModel_calculateDataRoot(t *testing.T) { } func TestInvoice_CreateProofs(t *testing.T) { - i := createInvoice(t) + i, _ := CreateInvoiceWithEmbedCD(t, nil, did, nil) rk := i.GetTestCoreDocWithReset().Roles[0].RoleKey pf := fmt.Sprintf(documents.CDTreePrefix+".roles[%s].collaborators[0]", hexutil.Encode(rk)) proof, err := i.CreateProofs([]string{"invoice.number", pf, documents.CDTreePrefix + ".document_type", "invoice.line_items[0].item_number", "invoice.line_items[0].description"}) @@ -244,17 +170,17 @@ func TestInvoice_CreateProofs(t *testing.T) { return } - tree, err := i.DocumentRootTree() + signingRoot, err := i.CalculateSigningRoot() assert.NoError(t, err) // Validate invoice_number - valid, err := tree.ValidateProof(proof[0]) - assert.Nil(t, err) + valid, err := documents.ValidateProof(proof[0], signingRoot, sha256.New()) + assert.NoError(t, err) assert.True(t, valid) // Validate roles - valid, err = tree.ValidateProof(proof[1]) - assert.Nil(t, err) + valid, err = documents.ValidateProof(proof[1], signingRoot, sha256.New()) + assert.NoError(t, err) assert.True(t, valid) // Validate []byte value @@ -263,17 +189,17 @@ func TestInvoice_CreateProofs(t *testing.T) { assert.True(t, i.AccountCanRead(acc)) // Validate document_type - valid, err = tree.ValidateProof(proof[2]) - assert.Nil(t, err) + valid, err = documents.ValidateProof(proof[2], signingRoot, sha256.New()) + assert.NoError(t, err) assert.True(t, valid) // validate line item - valid, err = tree.ValidateProof(proof[3]) - assert.Nil(t, err) + valid, err = documents.ValidateProof(proof[3], signingRoot, sha256.New()) + assert.NoError(t, err) assert.True(t, valid) - valid, err = tree.ValidateProof(proof[4]) - assert.Nil(t, err) + valid, err = documents.ValidateProof(proof[4], signingRoot, sha256.New()) + assert.NoError(t, err) assert.True(t, valid) } @@ -282,19 +208,18 @@ func TestInvoice_CreateNFTProofs(t *testing.T) { acc := tc.(*configstore.Account) acc.IdentityID = defaultDID[:] assert.NoError(t, err) - i := new(Invoice) - invPayload := testingdocuments.CreateInvoicePayload() - invPayload.Data.DateDue = ×tamp.Timestamp{Seconds: time.Now().Unix()} - invPayload.Data.Status = "unpaid" - invPayload.WriteAccess = []string{defaultDID.String()} - err = i.InitInvoiceInput(invPayload, defaultDID) + + i, _ := CreateInvoiceWithEmbedCD(t, nil, did, []identity.DID{defaultDID}) + tt := time.Now() + i.Data.DateDue = &tt + i.Data.Status = "unpaid" assert.NoError(t, err) sig, err := acc.SignMsg([]byte{0, 1, 2, 3}) assert.NoError(t, err) i.AppendSignatures(sig) _, err = i.CalculateDataRoot() assert.NoError(t, err) - _, err = i.CalculateSigningRoot() + signingRoot, err := i.CalculateSigningRoot() assert.NoError(t, err) _, err = i.CalculateDocumentRoot() assert.NoError(t, err) @@ -302,9 +227,9 @@ func TestInvoice_CreateNFTProofs(t *testing.T) { keys, err := tc.GetKeys() assert.NoError(t, err) signerId := hexutil.Encode(append(defaultDID[:], keys[identity.KeyPurposeSigning.Name].PublicKey...)) - signingRoot := fmt.Sprintf("%s.%s", documents.DRTreePrefix, documents.SigningRootField) + signingRootField := fmt.Sprintf("%s.%s", documents.DRTreePrefix, documents.SigningRootField) signatureSender := fmt.Sprintf("%s.signatures[%s].signature", documents.SignaturesTreePrefix, signerId) - proofFields := []string{"invoice.gross_amount", "invoice.currency", "invoice.date_due", "invoice.sender", "invoice.status", signingRoot, signatureSender, documents.CDTreePrefix + ".next_version"} + proofFields := []string{"invoice.gross_amount", "invoice.currency", "invoice.date_due", "invoice.sender", "invoice.status", signingRootField, signatureSender, documents.CDTreePrefix + ".next_version"} proof, err := i.CreateProofs(proofFields) assert.Nil(t, err) assert.NotNil(t, proof) @@ -313,8 +238,8 @@ func TestInvoice_CreateNFTProofs(t *testing.T) { assert.Len(t, proofFields, 8) // Validate invoice_gross_amount - valid, err := tree.ValidateProof(proof[0]) - assert.Nil(t, err) + valid, err := documents.ValidateProof(proof[0], signingRoot, sha256.New()) + assert.NoError(t, err) assert.True(t, valid) // Validate signing_root @@ -328,19 +253,19 @@ func TestInvoice_CreateNFTProofs(t *testing.T) { assert.True(t, valid) // Validate next_version - valid, err = tree.ValidateProof(proof[7]) + valid, err = documents.ValidateProof(proof[7], signingRoot, sha256.New()) assert.Nil(t, err) assert.True(t, valid) } func TestInvoiceModel_createProofsFieldDoesNotExist(t *testing.T) { - i := createInvoice(t) + i, _ := CreateInvoiceWithEmbedCD(t, nil, did, nil) _, err := i.CreateProofs([]string{"nonexisting"}) assert.NotNil(t, err) } func TestInvoiceModel_GetDocumentID(t *testing.T) { - i := createInvoice(t) + i, _ := CreateInvoiceWithEmbedCD(t, nil, did, nil) assert.Equal(t, i.CoreDocument.ID(), i.ID()) } @@ -349,7 +274,7 @@ func TestInvoiceModel_getDocumentDataTree(t *testing.T) { assert.NoError(t, na.SetString("2")) ga := new(documents.Decimal) assert.NoError(t, ga.SetString("2")) - i := createInvoice(t) + i, _ := CreateInvoiceWithEmbedCD(t, nil, did, nil) i.Data.Number = "321321" i.Data.NetAmount = na i.Data.GrossAmount = ga @@ -361,33 +286,9 @@ func TestInvoiceModel_getDocumentDataTree(t *testing.T) { assert.Equal(t, []byte(i.Data.Number), leaf.Value) } -func createInvoice(t *testing.T) *Invoice { - i := new(Invoice) - payload := testingdocuments.CreateInvoicePayload() - payload.Data.LineItems = []*clientinvoicepb.LineItem{ - { - ItemNumber: "123456", - TaxAmount: "1.99", - TotalAmount: "99", - Description: "Some description", - }, - } - - err := i.InitInvoiceInput(payload, defaultDID) - assert.NoError(t, err) - i.GetTestCoreDocWithReset() - _, err = i.CalculateDataRoot() - assert.NoError(t, err) - _, err = i.CalculateSigningRoot() - assert.NoError(t, err) - _, err = i.CalculateDocumentRoot() - assert.NoError(t, err) - return i -} - func TestInvoice_CollaboratorCanUpdate(t *testing.T) { - inv := createInvoice(t) - id1 := defaultDID + inv, _ := CreateInvoiceWithEmbedCD(t, nil, did, nil) + id1 := did id2 := testingidentity.GenerateRandomDID() id3 := testingidentity.GenerateRandomDID() @@ -401,21 +302,21 @@ func TestInvoice_CollaboratorCanUpdate(t *testing.T) { model, err := testRepo().Get(id1[:], inv.CurrentVersion()) assert.NoError(t, err) oldInv := model.(*Invoice) - data, err := oldInv.getClientData() - assert.NoError(t, err) - data.GrossAmount = "50" - err = inv.PrepareNewVersion(inv, data, documents.CollaboratorsAccess{ - ReadWriteCollaborators: []identity.DID{id3}, - }, oldInv.Attributes) - assert.NoError(t, err) - - _, err = inv.CalculateDataRoot() - assert.NoError(t, err) - - _, err = inv.CalculateSigningRoot() - assert.NoError(t, err) - - _, err = inv.CalculateDocumentRoot() + data := oldInv.Data + dec, err := documents.NewDecimal("55") + assert.NoError(t, err) + data.GrossAmount = dec + d, err := json.Marshal(data) + assert.NoError(t, err) + err = inv.unpackFromUpdatePayloadOld(inv, documents.UpdatePayload{ + DocumentID: inv.ID(), + CreatePayload: documents.CreatePayload{ + Data: d, + Collaborators: documents.CollaboratorsAccess{ + ReadWriteCollaborators: []identity.DID{id3}, + }, + }, + }) assert.NoError(t, err) // id1 should have permission @@ -424,20 +325,26 @@ func TestInvoice_CollaboratorCanUpdate(t *testing.T) { // id2 should fail since it doesn't have the permission to update assert.Error(t, oldInv.CollaboratorCanUpdate(inv, id2)) - // update the id3 rules to update only gross amount - inv.CoreDocument.GetTestCoreDocWithReset().TransitionRules[3].MatchType = coredocumentpb.FieldMatchType_FIELD_MATCH_TYPE_EXACT - inv.CoreDocument.GetTestCoreDocWithReset().TransitionRules[3].Field = append(compactPrefix(), 0, 0, 0, 14) + // update the id3 rules to update only total amount + inv.CoreDocument.Document.TransitionRules[3].MatchType = coredocumentpb.FieldMatchType_FIELD_MATCH_TYPE_EXACT + inv.CoreDocument.Document.TransitionRules[3].Field = append(compactPrefix(), 0, 0, 0, 18) assert.NoError(t, testRepo().Create(id1[:], inv.CurrentVersion(), inv)) // fetch the document model, err = testRepo().Get(id1[:], inv.CurrentVersion()) assert.NoError(t, err) oldInv = model.(*Invoice) - data, err = oldInv.getClientData() + data = oldInv.Data + dec, err = documents.NewDecimal("55") assert.NoError(t, err) - data.GrossAmount = "55" + data.GrossAmount = dec data.Currency = "INR" - err = inv.PrepareNewVersion(inv, data, documents.CollaboratorsAccess{}, oldInv.Attributes) + d, err = json.Marshal(data) + assert.NoError(t, err) + err = inv.unpackFromUpdatePayloadOld(inv, documents.UpdatePayload{ + DocumentID: inv.ID(), + CreatePayload: documents.CreatePayload{Data: d}, + }) assert.NoError(t, err) // id1 should have permission @@ -457,7 +364,7 @@ func TestInvoice_AddAttributes(t *testing.T) { inv, _ := createCDWithEmbeddedInvoice(t) label := "some key" value := "some value" - attr, err := documents.NewAttribute(label, documents.AttrString, value) + attr, err := documents.NewStringAttribute(label, documents.AttrString, value) assert.NoError(t, err) // success @@ -479,7 +386,7 @@ func TestInvoice_DeleteAttribute(t *testing.T) { inv, _ := createCDWithEmbeddedInvoice(t) label := "some key" value := "some value" - attr, err := documents.NewAttribute(label, documents.AttrString, value) + attr, err := documents.NewStringAttribute(label, documents.AttrString, value) assert.NoError(t, err) // failed @@ -494,7 +401,7 @@ func TestInvoice_DeleteAttribute(t *testing.T) { } func TestInvoice_GetData(t *testing.T) { - inv := createInvoice(t) + inv, _ := CreateInvoiceWithEmbedCD(t, nil, did, nil) data := inv.GetData() assert.Equal(t, inv.Data, data) } @@ -599,7 +506,7 @@ func validDataWithCurrency(t *testing.T) []byte { } func checkInvoicePayloadDataError(t *testing.T, inv *Invoice, payload documents.CreatePayload) { - err := inv.loadData(payload.Data) + err := loadData(payload.Data, &inv.Data) assert.Error(t, err) } @@ -633,7 +540,7 @@ func TestInvoice_loadData(t *testing.T) { // valid data payload.Data = validData(t) - err := inv.loadData(payload.Data) + err := loadData(payload.Data, &inv.Data) assert.NoError(t, err) data := inv.GetData().(Data) assert.Equal(t, data.Number, "12345") @@ -653,15 +560,17 @@ func TestInvoice_loadData(t *testing.T) { func TestInvoice_unpackFromCreatePayload(t *testing.T) { payload := documents.CreatePayload{} inv := new(Invoice) + ctx := context.Background() // invalid data + payload.Collaborators.ReadWriteCollaborators = append(payload.Collaborators.ReadWriteCollaborators, did) payload.Data = invalidDecimalData(t) - err := inv.unpackFromCreatePayload(did, payload) + err := inv.DeriveFromCreatePayload(ctx, payload) assert.Error(t, err) assert.True(t, errors.IsOfType(ErrInvoiceInvalidData, err)) // invalid attributes - attr, err := documents.NewAttribute("test", documents.AttrString, "value") + attr, err := documents.NewStringAttribute("test", documents.AttrString, "value") assert.NoError(t, err) val := attr.Value val.Type = documents.AttributeType("some type") @@ -670,7 +579,7 @@ func TestInvoice_unpackFromCreatePayload(t *testing.T) { attr.Key: attr, } payload.Data = validData(t) - err = inv.unpackFromCreatePayload(did, payload) + err = inv.DeriveFromCreatePayload(ctx, payload) assert.Error(t, err) assert.True(t, errors.IsOfType(documents.ErrCDCreate, err)) @@ -680,23 +589,23 @@ func TestInvoice_unpackFromCreatePayload(t *testing.T) { payload.Attributes = map[documents.AttrKey]documents.Attribute{ attr.Key: attr, } - err = inv.unpackFromCreatePayload(did, payload) + err = inv.DeriveFromCreatePayload(ctx, payload) assert.NoError(t, err) } -func TestInvoice_unpackFromUpdatePayload(t *testing.T) { +func TestInvoice_unpackFromUpdatePayloadOld(t *testing.T) { payload := documents.UpdatePayload{} - old := createInvoice(t) + old, _ := CreateInvoiceWithEmbedCD(t, nil, did, nil) inv := new(Invoice) // invalid data payload.Data = invalidDecimalData(t) - err := inv.unpackFromUpdatePayload(old, payload) + err := inv.unpackFromUpdatePayloadOld(old, payload) assert.Error(t, err) assert.True(t, errors.IsOfType(ErrInvoiceInvalidData, err)) // invalid attributes - attr, err := documents.NewAttribute("test", documents.AttrString, "value") + attr, err := documents.NewStringAttribute("test", documents.AttrString, "value") assert.NoError(t, err) val := attr.Value val.Type = documents.AttributeType("some type") @@ -705,7 +614,7 @@ func TestInvoice_unpackFromUpdatePayload(t *testing.T) { attr.Key: attr, } payload.Data = validData(t) - err = inv.unpackFromUpdatePayload(old, payload) + err = inv.unpackFromUpdatePayloadOld(old, payload) assert.Error(t, err) assert.True(t, errors.IsOfType(documents.ErrCDNewVersion, err)) @@ -715,6 +624,78 @@ func TestInvoice_unpackFromUpdatePayload(t *testing.T) { payload.Attributes = map[documents.AttrKey]documents.Attribute{ attr.Key: attr, } - err = inv.unpackFromUpdatePayload(old, payload) + err = inv.unpackFromUpdatePayloadOld(old, payload) + assert.NoError(t, err) +} + +func TestInvoice_unpackFromUpdatePayload(t *testing.T) { + payload := documents.UpdatePayload{} + old, _ := CreateInvoiceWithEmbedCD(t, nil, did, nil) + + // invalid data + ctx := context.Background() + payload.Data = invalidDecimalData(t) + inv, err := old.DeriveFromUpdatePayload(ctx, payload) + assert.Error(t, err) + assert.True(t, errors.IsOfType(ErrInvoiceInvalidData, err)) + + // invalid attributes + attr, err := documents.NewStringAttribute("test", documents.AttrString, "value") + assert.NoError(t, err) + val := attr.Value + val.Type = documents.AttributeType("some type") + attr.Value = val + payload.Attributes = map[documents.AttrKey]documents.Attribute{ + attr.Key: attr, + } + payload.Data = validData(t) + _, err = old.DeriveFromUpdatePayload(ctx, payload) + assert.Error(t, err) + assert.True(t, errors.IsOfType(documents.ErrCDNewVersion, err)) + + // valid + val.Type = documents.AttrString + attr.Value = val + payload.Attributes = map[documents.AttrKey]documents.Attribute{ + attr.Key: attr, + } + inv, err = old.DeriveFromUpdatePayload(ctx, payload) + assert.NoError(t, err) + // check if patch worked + assert.NotEqual(t, inv.GetData(), old.Data) + assert.Equal(t, inv.GetData().(Data).Recipient.String(), "0xBAEb33a61f05e6F269f1c4b4CFF91A901B54DaF7") + assert.Equal(t, old.Data.Recipient.String(), "0xEA939D5C0494b072c51565b191eE59B5D34fbf79") + assert.Len(t, inv.GetData().(Data).LineItems, 1) + + // new data + assert.Len(t, old.Data.Attachments, 0) + assert.Len(t, inv.GetData().(Data).Attachments, 1) +} + +func TestInvoice_Patch(t *testing.T) { + payload := documents.UpdatePayload{} + inv, _ := CreateInvoiceWithEmbedCD(t, nil, did, nil) + + // invalid data + payload.Data = invalidDecimalData(t) + err := inv.Patch(payload) + assert.Error(t, err) + assert.True(t, errors.IsOfType(ErrInvoiceInvalidData, err)) + + // valid + payload.Data = validData(t) + attr, err := documents.NewStringAttribute("test", documents.AttrString, "value") + assert.NoError(t, err) + val := attr.Value + val.Type = documents.AttrString + attr.Value = val + payload.Attributes = map[documents.AttrKey]documents.Attribute{ + attr.Key: attr, + } + err = inv.Patch(payload) + assert.NoError(t, err) + assert.Equal(t, inv.Data.Recipient.String(), "0xBAEb33a61f05e6F269f1c4b4CFF91A901B54DaF7") + collabs, err := inv.GetCollaborators() assert.NoError(t, err) + assert.Len(t, collabs.ReadWriteCollaborators, 0) } diff --git a/documents/invoice/service.go b/documents/invoice/service.go index 3e6141553..ffc8dfc9c 100644 --- a/documents/invoice/service.go +++ b/documents/invoice/service.go @@ -9,28 +9,10 @@ import ( "github.com/centrifuge/go-centrifuge/documents" "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/go-centrifuge/jobs" - clientinvoicepb "github.com/centrifuge/go-centrifuge/protobufs/gen/go/invoice" "github.com/centrifuge/go-centrifuge/queue" "github.com/ethereum/go-ethereum/common/hexutil" ) -// Service defines specific functions for invoice -type Service interface { - documents.Service - - // DeriverFromPayload derives Invoice from clientPayload - DeriveFromCreatePayload(ctx context.Context, payload *clientinvoicepb.InvoiceCreatePayload) (documents.Model, error) - - // DeriveFromUpdatePayload derives invoice model from update payload - DeriveFromUpdatePayload(ctx context.Context, payload *clientinvoicepb.InvoiceUpdatePayload) (documents.Model, error) - - // DeriveInvoiceData returns the invoice data as client data - DeriveInvoiceData(inv documents.Model) (*clientinvoicepb.InvoiceData, error) - - // DeriveInvoiceResponse returns the invoice model in our standard client format - DeriveInvoiceResponse(inv documents.Model) (*clientinvoicepb.InvoiceResponse, error) -} - // service implements Service and handles all invoice related persistence and validations // service always returns errors of type `errors.Error` or `errors.TypedError` type service struct { @@ -50,7 +32,7 @@ func DefaultService( jobManager jobs.Manager, tokenRegFinder func() documents.TokenRegistry, anchorRepo anchors.AnchorRepository, -) Service { +) documents.Service { return service{ repo: repo, queueSrv: queueSrv, @@ -72,26 +54,6 @@ func (s service) DeriveFromCoreDocument(cd coredocumentpb.CoreDocument) (documen return inv, nil } -// UnpackFromCreatePayload initializes the model with parameters provided from the rest-api call -func (s service) DeriveFromCreatePayload(ctx context.Context, payload *clientinvoicepb.InvoiceCreatePayload) (documents.Model, error) { - if payload == nil || payload.Data == nil { - return nil, documents.ErrDocumentNil - } - - did, err := contextutil.AccountDID(ctx) - if err != nil { - return nil, documents.ErrDocumentConfigAccountID - } - - invoiceModel := new(Invoice) - err = invoiceModel.InitInvoiceInput(payload, did) - if err != nil { - return nil, errors.NewTypedError(documents.ErrDocumentInvalid, err) - } - - return invoiceModel, nil -} - // validateAndPersist validates the document, calculates the data root, and persists to DB func (s service) validateAndPersist(ctx context.Context, old, new documents.Model, validator documents.Validator) (documents.Model, error) { selfDID, err := contextutil.AccountDID(ctx) @@ -120,7 +82,8 @@ func (s service) validateAndPersist(ctx context.Context, old, new documents.Mode } // Create takes and invoice model and does required validation checks, tries to persist to DB -func (s service) Create(ctx context.Context, inv documents.Model) (documents.Model, jobs.JobID, chan bool, error) { +// Deprecated +func (s service) Create(ctx context.Context, inv documents.Model) (documents.Model, jobs.JobID, chan error, error) { selfDID, err := contextutil.AccountDID(ctx) if err != nil { return nil, jobs.NilJobID(), nil, errors.NewTypedError(documents.ErrDocumentConfigAccountID, err) @@ -140,7 +103,8 @@ func (s service) Create(ctx context.Context, inv documents.Model) (documents.Mod } // Update finds the old document, validates the new version and persists the updated document -func (s service) Update(ctx context.Context, new documents.Model) (documents.Model, jobs.JobID, chan bool, error) { +// Deprecated +func (s service) Update(ctx context.Context, new documents.Model) (documents.Model, jobs.JobID, chan error, error) { selfDID, err := contextutil.AccountDID(ctx) if err != nil { return nil, jobs.NilJobID(), nil, errors.NewTypedError(documents.ErrDocumentConfigAccountID, err) @@ -164,77 +128,8 @@ func (s service) Update(ctx context.Context, new documents.Model) (documents.Mod return new, jobID, done, nil } -// DeriveInvoiceResponse returns create response from invoice model -func (s service) DeriveInvoiceResponse(model documents.Model) (*clientinvoicepb.InvoiceResponse, error) { - data, err := s.DeriveInvoiceData(model) - if err != nil { - return nil, err - } - - h, err := documents.DeriveResponseHeader(s.tokenRegFinder(), model) - if err != nil { - return nil, errors.New("failed to derive response: %v", err) - } - - attrs, err := documents.ToClientAttributes(model.GetAttributes()) - if err != nil { - return nil, err - } - - return &clientinvoicepb.InvoiceResponse{ - Header: h, - Data: data, - Attributes: attrs, - }, nil - -} - -// DeriveInvoiceData returns create response from invoice model -func (s service) DeriveInvoiceData(doc documents.Model) (*clientinvoicepb.InvoiceData, error) { - inv, ok := doc.(*Invoice) - if !ok { - return nil, documents.ErrDocumentInvalidType - } - - return inv.getClientData() -} - -// DeriveFromUpdatePayload returns a new version of the old invoice identified by identifier in payload -func (s service) DeriveFromUpdatePayload(ctx context.Context, payload *clientinvoicepb.InvoiceUpdatePayload) (documents.Model, error) { - if payload == nil || payload.Data == nil { - return nil, documents.ErrDocumentNil - } - - // get latest old version of the document - id, err := hexutil.Decode(payload.DocumentId) - if err != nil { - return nil, errors.NewTypedError(documents.ErrDocumentIdentifier, errors.New("failed to decode identifier: %v", err)) - } - - old, err := s.GetCurrentVersion(ctx, id) - if err != nil { - return nil, err - } - - cs, err := documents.FromClientCollaboratorAccess(payload.ReadAccess, payload.WriteAccess) - if err != nil { - return nil, err - } - - attrs, err := documents.FromClientAttributes(payload.Attributes) - if err != nil { - return nil, err - } - - inv := new(Invoice) - if err := inv.PrepareNewVersion(old, payload.Data, cs, attrs); err != nil { - return nil, errors.NewTypedError(documents.ErrDocumentPrepareCoreDocument, errors.New("failed to load invoice from data: %v", err)) - } - - return inv, nil -} - // CreateModel creates invoice from the payload, validates, persists, and returns the invoice. +// Deprecated func (s service) CreateModel(ctx context.Context, payload documents.CreatePayload) (documents.Model, jobs.JobID, error) { if payload.Data == nil { return nil, jobs.NilJobID(), documents.ErrDocumentNil @@ -246,7 +141,8 @@ func (s service) CreateModel(ctx context.Context, payload documents.CreatePayloa } inv := new(Invoice) - if err := inv.unpackFromCreatePayload(did, payload); err != nil { + payload.Collaborators.ReadWriteCollaborators = append(payload.Collaborators.ReadWriteCollaborators, did) + if err := inv.DeriveFromCreatePayload(ctx, payload); err != nil { return nil, jobs.NilJobID(), errors.NewTypedError(documents.ErrDocumentInvalid, err) } @@ -268,6 +164,7 @@ func (s service) CreateModel(ctx context.Context, payload documents.CreatePayloa } // UpdateModel updates the migrates the current invoice to next version with data from the update payload +// Deprecated func (s service) UpdateModel(ctx context.Context, payload documents.UpdatePayload) (documents.Model, jobs.JobID, error) { if payload.Data == nil { return nil, jobs.NilJobID(), documents.ErrDocumentNil @@ -289,7 +186,7 @@ func (s service) UpdateModel(ctx context.Context, payload documents.UpdatePayloa } inv := new(Invoice) - err = inv.unpackFromUpdatePayload(oldInv, payload) + err = inv.unpackFromUpdatePayloadOld(oldInv, payload) if err != nil { return nil, jobs.NilJobID(), errors.NewTypedError(documents.ErrDocumentInvalid, err) } @@ -308,3 +205,13 @@ func (s service) UpdateModel(ctx context.Context, payload documents.UpdatePayloa jobID, _, err = documents.CreateAnchorJob(ctx, s.jobManager, s.queueSrv, did, jobID, inv.CurrentVersion()) return inv, jobID, err } + +// Validate takes care of document validation +func (s service) Validate(ctx context.Context, model documents.Model, old documents.Model) error { + return nil +} + +// New returns a new uninitialised invoice. +func (s service) New(_ string) (documents.Model, error) { + return new(Invoice), nil +} diff --git a/documents/invoice/service_test.go b/documents/invoice/service_test.go index 3df5ebd44..3bded1b7b 100644 --- a/documents/invoice/service_test.go +++ b/documents/invoice/service_test.go @@ -4,25 +4,24 @@ package invoice import ( "context" + "encoding/json" "testing" "github.com/centrifuge/centrifuge-protobufs/gen/go/coredocument" "github.com/centrifuge/go-centrifuge/documents" "github.com/centrifuge/go-centrifuge/errors" + "github.com/centrifuge/go-centrifuge/identity" "github.com/centrifuge/go-centrifuge/jobs" - clientinvoicepb "github.com/centrifuge/go-centrifuge/protobufs/gen/go/invoice" "github.com/centrifuge/go-centrifuge/storage" "github.com/centrifuge/go-centrifuge/storage/leveldb" "github.com/centrifuge/go-centrifuge/testingutils" "github.com/centrifuge/go-centrifuge/testingutils/anchors" "github.com/centrifuge/go-centrifuge/testingutils/commons" "github.com/centrifuge/go-centrifuge/testingutils/config" - "github.com/centrifuge/go-centrifuge/testingutils/documents" "github.com/centrifuge/go-centrifuge/testingutils/identity" "github.com/centrifuge/go-centrifuge/testingutils/testingjobs" "github.com/centrifuge/go-centrifuge/utils" "github.com/centrifuge/gocelery" - "github.com/ethereum/go-ethereum/common/hexutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -33,7 +32,7 @@ var ( accountID = did[:] ) -func getServiceWithMockedLayers() (testingcommons.MockIdentityService, Service) { +func getServiceWithMockedLayers() (testingcommons.MockIdentityService, documents.Service) { c := &testingconfig.MockConfig{} c.On("GetIdentityID").Return(didBytes, nil) idService := testingcommons.MockIdentityService{} @@ -44,7 +43,7 @@ func getServiceWithMockedLayers() (testingcommons.MockIdentityService, Service) repo := testRepo() anchorRepo := &testinganchors.MockAnchorRepo{} anchorRepo.On("GetAnchorData", mock.Anything).Return(nil, errors.New("missing")) - docSrv := documents.DefaultService(cfg, repo, anchorRepo, documents.NewServiceRegistry(), &idService) + docSrv := documents.DefaultService(cfg, repo, anchorRepo, documents.NewServiceRegistry(), &idService, nil, nil) return idService, DefaultService( docSrv, repo, @@ -54,160 +53,46 @@ func getServiceWithMockedLayers() (testingcommons.MockIdentityService, Service) } func TestService_Update(t *testing.T) { - _, srv := getServiceWithMockedLayers() - invSrv := srv.(service) - ctxh := testingconfig.CreateAccountContext(t, cfg) - - // missing last version - model, _ := createCDWithEmbeddedInvoice(t) - _, _, _, err := invSrv.Update(ctxh, model) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrDocumentNotFound, err)) - assert.NoError(t, testRepo().Create(accountID, model.CurrentVersion(), model)) - - // calculate data root fails - nm := new(mockModel) - nm.On("ID").Return(model.ID(), nil).Once() - _, _, _, err = invSrv.Update(ctxh, nm) - nm.AssertExpectations(t) - assert.Error(t, err) - assert.Contains(t, err.Error(), "unknown document type") - - // success - data, err := invSrv.DeriveInvoiceData(model) - assert.Nil(t, err) - data.GrossAmount = "100" - collab := testingidentity.GenerateRandomDID().String() - newInv, err := invSrv.DeriveFromUpdatePayload(ctxh, &clientinvoicepb.InvoiceUpdatePayload{ - DocumentId: hexutil.Encode(model.ID()), - WriteAccess: []string{collab}, - Data: data, - }) - assert.Nil(t, err) - newData, err := invSrv.DeriveInvoiceData(newInv) - assert.Nil(t, err) - assert.Equal(t, data, newData) - - model, _, _, err = invSrv.Update(ctxh, newInv) - assert.Nil(t, err) - assert.NotNil(t, model) - assert.True(t, testRepo().Exists(accountID, model.ID())) - assert.True(t, testRepo().Exists(accountID, model.CurrentVersion())) - assert.True(t, testRepo().Exists(accountID, model.PreviousVersion())) - - newData, err = invSrv.DeriveInvoiceData(model) - assert.Nil(t, err) - assert.Equal(t, data, newData) -} - -func TestService_DeriveFromUpdatePayload(t *testing.T) { _, invSrv := getServiceWithMockedLayers() - // nil payload - doc, err := invSrv.DeriveFromUpdatePayload(nil, nil) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrDocumentNil, err)) - assert.Nil(t, doc) - - // nil payload data - doc, err = invSrv.DeriveFromUpdatePayload(nil, &clientinvoicepb.InvoiceUpdatePayload{}) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrDocumentNil, err)) - assert.Nil(t, doc) - - // messed up identifier - contextHeader := testingconfig.CreateAccountContext(t, cfg) - payload := &clientinvoicepb.InvoiceUpdatePayload{DocumentId: "some identifier", Data: &clientinvoicepb.InvoiceData{}} - doc, err = invSrv.DeriveFromUpdatePayload(contextHeader, payload) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrDocumentIdentifier, err)) - assert.Contains(t, err.Error(), "failed to decode identifier") - assert.Nil(t, doc) + ctxh := testingconfig.CreateAccountContext(t, cfg) // missing last version - id := utils.RandomSlice(32) - payload.DocumentId = hexutil.Encode(id) - doc, err = invSrv.DeriveFromUpdatePayload(contextHeader, payload) + inv, _ := CreateInvoiceWithEmbedCD(t, ctxh, did, nil) + _, _, _, err := invSrv.Update(ctxh, inv) assert.Error(t, err) assert.True(t, errors.IsOfType(documents.ErrDocumentNotFound, err)) - assert.Nil(t, doc) - - // failed to load from data - old, _ := createCDWithEmbeddedInvoice(t) - err = testRepo().Create(accountID, old.CurrentVersion(), old) - assert.Nil(t, err) - payload.Data = &clientinvoicepb.InvoiceData{ - Sender: "0xed03Fa80291fF5DDC284DE6b51E716B130b05e20", - Recipient: "0xEA939D5C0494b072c51565b191eE59B5D34fbf79", - Payee: "some data", - GrossAmount: "42", - Currency: "EUR", - } - - payload.DocumentId = hexutil.Encode(old.ID()) - doc, err = invSrv.DeriveFromUpdatePayload(contextHeader, payload) - assert.Error(t, err) - assert.Nil(t, doc) - - // failed core document new version - payload.Data.Payee = "0x087D8ca6A16E6ce8d9fF55672E551A2828Ab8e8C" - payload.WriteAccess = []string{"some wrong ID"} - doc, err = invSrv.DeriveFromUpdatePayload(contextHeader, payload) - assert.Error(t, err) - assert.Nil(t, doc) // success - wantCollab := testingidentity.GenerateRandomDID() - payload.WriteAccess = []string{wantCollab.String()} - doc, err = invSrv.DeriveFromUpdatePayload(contextHeader, payload) - assert.Nil(t, err) - assert.NotNil(t, doc) - cs, err := doc.GetCollaborators() + assert.NoError(t, testRepo().Create(accountID, inv.CurrentVersion(), inv)) + dec, err := documents.NewDecimal("100") assert.NoError(t, err) - assert.Len(t, cs.ReadWriteCollaborators, 3) - assert.Contains(t, cs.ReadWriteCollaborators, wantCollab) - assert.Equal(t, old.ID(), doc.ID()) - assert.Equal(t, payload.DocumentId, hexutil.Encode(doc.ID())) - assert.Equal(t, old.CurrentVersion(), doc.PreviousVersion()) - assert.Equal(t, old.NextVersion(), doc.CurrentVersion()) - assert.NotNil(t, doc.NextVersion()) - data, err := doc.(*Invoice).getClientData() + data := inv.GetData().(Data) + data.NetAmount = dec + d, err := json.Marshal(data) assert.NoError(t, err) - assert.Equal(t, payload.Data, data) -} - -func TestService_DeriveFromCreatePayload(t *testing.T) { - invSrv := service{} - ctxh := testingconfig.CreateAccountContext(t, cfg) - - // nil payload - m, err := invSrv.DeriveFromCreatePayload(ctxh, nil) - assert.Nil(t, m) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrDocumentNil, err)) - - // nil data payload - m, err = invSrv.DeriveFromCreatePayload(ctxh, &clientinvoicepb.InvoiceCreatePayload{}) - assert.Nil(t, m) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrDocumentNil, err)) - - // Init fails - payload := &clientinvoicepb.InvoiceCreatePayload{ - Data: &clientinvoicepb.InvoiceData{ - Payee: "some payee", + collab := testingidentity.GenerateRandomDID() + newInv := new(Invoice) + assert.NoError(t, newInv.unpackFromUpdatePayloadOld(inv, documents.UpdatePayload{ + DocumentID: inv.ID(), + CreatePayload: documents.CreatePayload{ + Collaborators: documents.CollaboratorsAccess{ + ReadWriteCollaborators: []identity.DID{collab}, + }, + Data: d, }, - } - - m, err = invSrv.DeriveFromCreatePayload(ctxh, payload) - assert.Nil(t, m) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrDocumentInvalid, err)) + })) + newData := newInv.GetData() + assert.Equal(t, data, newData) + invModel, _, _, err := invSrv.Update(ctxh, newInv) + assert.Nil(t, err) + assert.NotNil(t, invModel) + assert.True(t, testRepo().Exists(accountID, invModel.ID())) + assert.True(t, testRepo().Exists(accountID, invModel.CurrentVersion())) + assert.True(t, testRepo().Exists(accountID, invModel.PreviousVersion())) - // success - payload.Data.Payee = testingidentity.GenerateRandomDID().String() - m, err = invSrv.DeriveFromCreatePayload(ctxh, payload) + newData = invModel.GetData() assert.Nil(t, err) - assert.NotNil(t, m) + assert.Equal(t, data, newData) } func TestService_DeriveFromCoreDocument(t *testing.T) { @@ -219,7 +104,7 @@ func TestService_DeriveFromCoreDocument(t *testing.T) { inv, ok := m.(*Invoice) assert.True(t, ok, "must be true") assert.Equal(t, inv.Data.Recipient.String(), "0xEA939D5C0494b072c51565b191eE59B5D34fbf79") - assert.Equal(t, inv.Data.GrossAmount.String(), "42") + assert.Equal(t, inv.Data.Currency, "EUR") } func TestService_Create(t *testing.T) { @@ -234,66 +119,29 @@ func TestService_Create(t *testing.T) { assert.Contains(t, err.Error(), "unknown document type") // success - inv, err := invSrv.DeriveFromCreatePayload(ctxh, testingdocuments.CreateInvoicePayload()) - assert.Nil(t, err) + inv, _ := CreateInvoiceWithEmbedCDWithPayload(t, ctxh, did, CreateInvoicePayload(t, nil)) m, _, _, err = invSrv.Create(ctxh, inv) assert.Nil(t, err) assert.True(t, testRepo().Exists(accountID, m.ID())) assert.True(t, testRepo().Exists(accountID, m.CurrentVersion())) } -func TestService_DeriveInvoiceData(t *testing.T) { - _, invSrv := getServiceWithMockedLayers() - - // some random model - _, err := invSrv.DeriveInvoiceData(&mockModel{}) - assert.Error(t, err, "Derive must fail") - - // success - payload := testingdocuments.CreateInvoicePayload() - inv, err := invSrv.DeriveFromCreatePayload(testingconfig.CreateAccountContext(t, cfg), payload) - assert.Nil(t, err, "must be non nil") - data, err := invSrv.DeriveInvoiceData(inv) - assert.Nil(t, err, "Derive must succeed") - assert.NotNil(t, data, "data must be non nil") -} - -func TestService_DeriveInvoiceResponse(t *testing.T) { - // success - invSrv := service{repo: testRepo(), tokenRegFinder: func() documents.TokenRegistry { - return nil - }} - - // derive data failed - m := new(mockModel) - r, err := invSrv.DeriveInvoiceResponse(m) - m.AssertExpectations(t) - assert.Nil(t, r) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrDocumentInvalidType, err)) - - // success - inv, _ := createCDWithEmbeddedInvoice(t) - r, err = invSrv.DeriveInvoiceResponse(inv) - payload := testingdocuments.CreateInvoicePayload() - assert.Nil(t, err) - assert.Equal(t, payload.Data, r.Data) - assert.Contains(t, r.Header.WriteAccess, did.String()) -} - func TestService_GetCurrentVersion(t *testing.T) { _, invSrv := getServiceWithMockedLayers() - doc, _ := createCDWithEmbeddedInvoice(t) ctxh := testingconfig.CreateAccountContext(t, cfg) - + doc, _ := CreateInvoiceWithEmbedCD(t, ctxh, did, nil) err := testRepo().Create(accountID, doc.CurrentVersion(), doc) assert.Nil(t, err) - data, err := doc.(*Invoice).getClientData() - assert.NoError(t, err) + data := doc.Data data.Currency = "INR" + d, err := json.Marshal(data) + assert.NoError(t, err) doc2 := new(Invoice) - assert.NoError(t, doc2.PrepareNewVersion(doc, data, documents.CollaboratorsAccess{}, doc.(*Invoice).Attributes)) + assert.NoError(t, doc2.unpackFromUpdatePayloadOld(doc, documents.UpdatePayload{ + CreatePayload: documents.CreatePayload{Data: d}, + DocumentID: doc.ID(), + })) assert.NoError(t, testRepo().Create(accountID, doc2.CurrentVersion(), doc2)) doc3, err := invSrv.GetCurrentVersion(ctxh, doc.ID()) @@ -340,8 +188,10 @@ func TestService_calculateDataRoot(t *testing.T) { assert.Contains(t, err.Error(), "unknown document type") // failed validator - inv, err = invSrv.DeriveFromCreatePayload(ctxh, testingdocuments.CreateInvoicePayload()) - assert.Nil(t, err) + inv = new(Invoice) + payload := CreateInvoicePayload(t, nil) + payload.Collaborators.ReadWriteCollaborators = append(payload.Collaborators.ReadWriteCollaborators, did) + assert.NoError(t, inv.(*Invoice).DeriveFromCreatePayload(ctxh, payload)) v := documents.ValidatorFunc(func(_, _ documents.Model) error { return errors.New("validations fail") }) @@ -351,8 +201,8 @@ func TestService_calculateDataRoot(t *testing.T) { assert.Contains(t, err.Error(), "validations fail") // create failed - inv, err = invSrv.DeriveFromCreatePayload(ctxh, testingdocuments.CreateInvoicePayload()) - assert.Nil(t, err) + inv = new(Invoice) + assert.NoError(t, inv.(*Invoice).DeriveFromCreatePayload(ctxh, payload)) err = invSrv.repo.Create(accountID, inv.CurrentVersion(), inv) assert.Nil(t, err) inv, err = invSrv.validateAndPersist(ctxh, nil, inv, CreateValidator()) @@ -361,8 +211,8 @@ func TestService_calculateDataRoot(t *testing.T) { assert.Contains(t, err.Error(), storage.ErrRepositoryModelCreateKeyExists) // success - inv, err = invSrv.DeriveFromCreatePayload(ctxh, testingdocuments.CreateInvoicePayload()) - assert.Nil(t, err) + inv = new(Invoice) + assert.NoError(t, inv.(*Invoice).DeriveFromCreatePayload(ctxh, payload)) inv, err = invSrv.validateAndPersist(ctxh, nil, inv, CreateValidator()) assert.Nil(t, err) assert.NotNil(t, inv) @@ -386,10 +236,9 @@ func testRepo() documents.Repository { func createCDWithEmbeddedInvoice(t *testing.T) (documents.Model, coredocumentpb.CoreDocument) { i := new(Invoice) - err := i.InitInvoiceInput(testingdocuments.CreateInvoicePayload(), did) - assert.NoError(t, err) + i = InitInvoice(t, did, CreateInvoicePayload(t, nil)) i.GetTestCoreDocWithReset() - _, err = i.CalculateDataRoot() + _, err := i.CalculateDataRoot() assert.NoError(t, err) _, err = i.CalculateSigningRoot() assert.NoError(t, err) @@ -431,7 +280,7 @@ func TestService_CreateModel(t *testing.T) { payload.Data = validDataWithCurrency(t) srv.repo = testRepo() jm := testingjobs.MockJobManager{} - jm.On("ExecuteWithinJob", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(jobs.NilJobID(), make(chan bool), nil) + jm.On("ExecuteWithinJob", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(jobs.NilJobID(), make(chan error), nil) srv.jobManager = jm m, _, err := srv.CreateModel(ctxh, payload) assert.NoError(t, err) @@ -464,7 +313,7 @@ func TestService_UpdateModel(t *testing.T) { assert.True(t, errors.IsOfType(documents.ErrDocumentNotFound, err)) // payload invalid - inv := createInvoice(t) + inv, _ := CreateInvoiceWithEmbedCD(t, nil, did, nil) err = testRepo().Create(did[:], inv.ID(), inv) assert.NoError(t, err) payload.DocumentID = inv.ID() @@ -481,7 +330,7 @@ func TestService_UpdateModel(t *testing.T) { // Success payload.Data = validDataWithCurrency(t) jm := testingjobs.MockJobManager{} - jm.On("ExecuteWithinJob", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(jobs.NilJobID(), make(chan bool), nil) + jm.On("ExecuteWithinJob", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(jobs.NilJobID(), make(chan error), nil) srv.jobManager = jm m, _, err := srv.UpdateModel(ctxh, payload) assert.NoError(t, err) @@ -489,3 +338,9 @@ func TestService_UpdateModel(t *testing.T) { assert.Equal(t, m.CurrentVersion(), inv.NextVersion()) jm.AssertExpectations(t) } + +func TestService_Validate(t *testing.T) { + srv := service{} + err := srv.Validate(context.Background(), nil, nil) + assert.NoError(t, err) +} diff --git a/documents/invoice/test_bootstrapper.go b/documents/invoice/test_bootstrapper.go deleted file mode 100644 index 1b5dcf471..000000000 --- a/documents/invoice/test_bootstrapper.go +++ /dev/null @@ -1,11 +0,0 @@ -// +build integration unit - -package invoice - -func (b *Bootstrapper) TestBootstrap(context map[string]interface{}) error { - return b.Bootstrap(context) -} - -func (*Bootstrapper) TestTearDown() error { - return nil -} diff --git a/documents/invoice/test_invoice.go b/documents/invoice/test_invoice.go new file mode 100644 index 000000000..bb954a113 --- /dev/null +++ b/documents/invoice/test_invoice.go @@ -0,0 +1,99 @@ +// +build integration unit testworld + +package invoice + +import ( + "context" + "encoding/json" + "testing" + + coredocumentpb "github.com/centrifuge/centrifuge-protobufs/gen/go/coredocument" + "github.com/centrifuge/go-centrifuge/contextutil" + "github.com/centrifuge/go-centrifuge/documents" + "github.com/centrifuge/go-centrifuge/identity" + testingidentity "github.com/centrifuge/go-centrifuge/testingutils/identity" + "github.com/stretchr/testify/assert" +) + +func (b *Bootstrapper) TestBootstrap(context map[string]interface{}) error { + return b.Bootstrap(context) +} + +func (*Bootstrapper) TestTearDown() error { + return nil +} + +func invoiceData(t *testing.T) []byte { + did, err := identity.NewDIDFromString("0xEA939D5C0494b072c51565b191eE59B5D34fbf79") + assert.NoError(t, err) + dec, err := documents.NewDecimal("42") + assert.NoError(t, err) + data := Data{ + Recipient: &did, + Currency: "EUR", + LineItems: []*LineItem{ + { + ItemNumber: "12312431", + Description: "just", + Quantity: dec, + }, + }, + } + + d, err := json.Marshal(data) + assert.NoError(t, err) + return d +} + +func CreateInvoicePayload(t *testing.T, collaborators []identity.DID) documents.CreatePayload { + if collaborators == nil { + collaborators = []identity.DID{testingidentity.GenerateRandomDID()} + } + return documents.CreatePayload{ + Scheme: Scheme, + Collaborators: documents.CollaboratorsAccess{ + ReadWriteCollaborators: collaborators, + }, + Data: invoiceData(t), + } +} + +func InitInvoice(t *testing.T, did identity.DID, payload documents.CreatePayload) *Invoice { + inv := new(Invoice) + payload.Collaborators.ReadWriteCollaborators = append(payload.Collaborators.ReadWriteCollaborators, did) + assert.NoError(t, inv.DeriveFromCreatePayload(context.Background(), payload)) + return inv +} + +func CreateInvoiceWithEmbedCDWithPayload(t *testing.T, ctx context.Context, did identity.DID, payload documents.CreatePayload) (*Invoice, coredocumentpb.CoreDocument) { + inv := new(Invoice) + payload.Collaborators.ReadWriteCollaborators = append(payload.Collaborators.ReadWriteCollaborators, did) + err := inv.DeriveFromCreatePayload(ctx, payload) + assert.NoError(t, err) + inv.GetTestCoreDocWithReset() + _, err = inv.CalculateDataRoot() + assert.NoError(t, err) + sr, err := inv.CalculateSigningRoot() + assert.NoError(t, err) + // if acc errors out, just skip it + if ctx == nil { + ctx = context.Background() + } + acc, err := contextutil.Account(ctx) + if err == nil { + sig, err := acc.SignMsg(sr) + assert.NoError(t, err) + inv.AppendSignatures(sig) + } + _, err = inv.CalculateDocumentRoot() + assert.NoError(t, err) + cd, err := inv.PackCoreDocument() + assert.NoError(t, err) + + return inv, cd +} + +func CreateInvoiceWithEmbedCD(t *testing.T, ctx context.Context, did identity.DID, collaborators []identity.DID) (*Invoice, coredocumentpb.CoreDocument) { + payload := CreateInvoicePayload(t, collaborators) + return CreateInvoiceWithEmbedCDWithPayload(t, ctx, did, payload) +} diff --git a/documents/model.go b/documents/model.go index 575448f19..88573f319 100644 --- a/documents/model.go +++ b/documents/model.go @@ -147,6 +147,12 @@ type Model interface { // GetData returns the document data. Ex: invoice.Data GetData() interface{} + + // GetStatus returns the status of the document. + GetStatus() Status + + // SetStatus set the status of the document. + SetStatus(st Status) error } // TokenRegistry defines NFT related functions. @@ -171,3 +177,13 @@ type UpdatePayload struct { CreatePayload DocumentID []byte } + +// Deriver defines the functions that can derive Document from the Payloads. +type Deriver interface { + // DeriveFromCreatePayload loads the payload into self. + DeriveFromCreatePayload(ctx context.Context, payload CreatePayload) error + + // DeriveFromUpdatePayload create the next version of the document. + // Patches the old data with Payload data + DeriveFromUpdatePayload(ctx context.Context, payload UpdatePayload) (Model, error) +} diff --git a/documents/processor.go b/documents/processor.go index b1a865d26..9651ae3da 100644 --- a/documents/processor.go +++ b/documents/processor.go @@ -13,6 +13,7 @@ import ( "github.com/centrifuge/go-centrifuge/identity" "github.com/centrifuge/go-centrifuge/utils" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" ) // Config defines required methods required for the documents package. @@ -61,7 +62,7 @@ func DefaultProcessor(idService identity.Service, p2pClient Client, repository a // Send sends the given defaultProcessor to the given recipient on the P2P layer func (dp defaultProcessor) Send(ctx context.Context, cd coredocumentpb.CoreDocument, id identity.DID) (err error) { - log.Infof("sending document %x to recipient %x", cd.DocumentIdentifier, id) + log.Infof("sending document %s to recipient %s", hexutil.Encode(cd.DocumentIdentifier), id.String()) ctx, cancel := context.WithTimeout(ctx, dp.config.GetP2PConnectionTimeout()) defer cancel() @@ -70,7 +71,7 @@ func (dp defaultProcessor) Send(ctx context.Context, cd coredocumentpb.CoreDocum return errors.New("failed to send document to the node: %v", err) } - log.Infof("Sent document to %x\n", id) + log.Infof("Sent document to %s\n", id.String()) return nil } @@ -86,11 +87,7 @@ func (dp defaultProcessor) PrepareForSignatureRequests(ctx context.Context, mode return err } - id, err := self.GetIdentityID() - if err != nil { - return err - } - + id := self.GetIdentityID() did, err := identity.NewDIDFromBytes(id) if err != nil { return err @@ -172,8 +169,8 @@ func (dp defaultProcessor) PreAnchorDocument(ctx context.Context, model Model) e return err } - isDone := <-done - if !isDone { + err = <-done + if err != nil { return errors.New("failed to pre-commit anchor: %v", err) } @@ -220,8 +217,8 @@ func (dp defaultProcessor) AnchorDocument(ctx context.Context, model Model) erro return errors.New("failed to commit anchor: %v", err) } - isDone := <-done - if !isDone { + err = <-done + if err != nil { return errors.New("failed to commit anchor: %v", err) } diff --git a/documents/processor_test.go b/documents/processor_test.go index 71781fc66..41a861d55 100644 --- a/documents/processor_test.go +++ b/documents/processor_test.go @@ -30,6 +30,16 @@ type mockModel struct { sigs []*coredocumentpb.Signature } +func (m *mockModel) Scheme() string { + args := m.Called() + return args.String(0) +} + +func (m *mockModel) SetStatus(st Status) error { + args := m.Called(st) + return args.Error(0) +} + func (m *mockModel) NFTs() []*coredocumentpb.NFT { args := m.Called() dr, _ := args.Get(0).([]*coredocumentpb.NFT) @@ -240,8 +250,7 @@ func TestDefaultProcessor_RequestSignatures(t *testing.T) { self, err := contextutil.Account(ctxh) assert.NoError(t, err) - did, err := self.GetIdentityID() - assert.NoError(t, err) + did := self.GetIdentityID() sr := utils.RandomSlice(32) sig, err := self.SignMsg(sr) assert.NoError(t, err) @@ -334,8 +343,7 @@ func TestDefaultProcessor_PrepareForAnchoring(t *testing.T) { ctxh := testingconfig.CreateAccountContext(t, cfg) self, err := contextutil.Account(ctxh) assert.NoError(t, err) - did, err := self.GetIdentityID() - assert.NoError(t, err) + did := self.GetIdentityID() sr := utils.RandomSlice(32) sig, err := self.SignMsg(sr) assert.NoError(t, err) @@ -392,9 +400,9 @@ type mockRepo struct { anchors.AnchorRepository } -func (m mockRepo) CommitAnchor(ctx context.Context, anchorID anchors.AnchorID, documentRoot anchors.DocumentRoot, documentProof [32]byte) (done chan bool, err error) { +func (m mockRepo) CommitAnchor(ctx context.Context, anchorID anchors.AnchorID, documentRoot anchors.DocumentRoot, documentProof [32]byte) (done chan error, err error) { args := m.Called(anchorID, documentRoot, documentProof) - c, _ := args.Get(0).(chan bool) + c, _ := args.Get(0).(chan error) return c, args.Error(1) } @@ -411,7 +419,7 @@ func TestDefaultProcessor_AnchorDocument(t *testing.T) { ctxh := testingconfig.CreateAccountContext(t, cfg) self, err := contextutil.Account(ctxh) assert.NoError(t, err) - did, err := self.GetIdentityID() + did := self.GetIdentityID() assert.NoError(t, err) sr := utils.RandomSlice(32) sig, err := self.SignMsg(sr) @@ -490,8 +498,8 @@ func TestDefaultProcessor_AnchorDocument(t *testing.T) { srv.On("ValidateSignature", cid, sig.PublicKey, sig.Signature, sr, tm).Return(nil).Once() dp.identityService = srv repo := mockRepo{} - ch := make(chan bool, 1) - ch <- true + ch := make(chan error, 1) + ch <- nil repo.On("CommitAnchor", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(ch, nil).Once() dp.anchorRepository = repo err = dp.AnchorDocument(ctxh, model) @@ -508,7 +516,7 @@ func TestDefaultProcessor_SendDocument(t *testing.T) { ctxh := testingconfig.CreateAccountContext(t, cfg) self, err := contextutil.Account(ctxh) assert.NoError(t, err) - didb, err := self.GetIdentityID() + didb := self.GetIdentityID() assert.NoError(t, err) did1, err := identity.NewDIDFromBytes(didb) assert.NoError(t, err) diff --git a/documents/purchaseorder/bootstrapper.go b/documents/purchaseorder/bootstrapper.go deleted file mode 100644 index fdf48f289..000000000 --- a/documents/purchaseorder/bootstrapper.go +++ /dev/null @@ -1,81 +0,0 @@ -package purchaseorder - -import ( - "github.com/centrifuge/centrifuge-protobufs/documenttypes" - "github.com/centrifuge/go-centrifuge/anchors" - "github.com/centrifuge/go-centrifuge/bootstrap" - "github.com/centrifuge/go-centrifuge/config" - "github.com/centrifuge/go-centrifuge/documents" - "github.com/centrifuge/go-centrifuge/errors" - "github.com/centrifuge/go-centrifuge/jobs" - "github.com/centrifuge/go-centrifuge/queue" -) - -const ( - // BootstrappedPOHandler maps to grc handler for PO - BootstrappedPOHandler = "BootstrappedPOHandler" -) - -// Bootstrapper implements bootstrap.Bootstrapper. -type Bootstrapper struct{} - -// Bootstrap initialises required services for purchaseorder. -func (Bootstrapper) Bootstrap(ctx map[string]interface{}) error { - docSrv, ok := ctx[documents.BootstrappedDocumentService].(documents.Service) - if !ok { - return errors.New("document service not initialised") - } - - registry, ok := ctx[documents.BootstrappedRegistry].(*documents.ServiceRegistry) - if !ok { - return errors.New("service registry not initialised") - } - - repo, ok := ctx[documents.BootstrappedDocumentRepository].(documents.Repository) - if !ok { - return errors.New("document db repository not initialised") - } - repo.Register(&PurchaseOrder{}) - - queueSrv, ok := ctx[bootstrap.BootstrappedQueueServer].(*queue.Server) - if !ok { - return errors.New("queue server not initialised") - } - - jobManager, ok := ctx[jobs.BootstrappedService].(jobs.Manager) - if !ok { - return errors.New("job manager service not initialised") - } - - cfgSrv, ok := ctx[config.BootstrappedConfigStorage].(config.Service) - if !ok { - return errors.New("config service not initialised") - } - - anchorRepo, ok := ctx[anchors.BootstrappedAnchorRepo].(anchors.AnchorRepository) - if !ok { - return anchors.ErrAnchorRepoNotInitialised - } - - // register service - srv := DefaultService(docSrv, repo, queueSrv, jobManager, func() documents.TokenRegistry { - tokenRegistry, ok := ctx[bootstrap.BootstrappedInvoiceUnpaid].(documents.TokenRegistry) - if !ok { - panic("token registry initialisation error") - } - return tokenRegistry - }, anchorRepo) - - err := registry.Register(documenttypes.PurchaseOrderDataTypeUrl, srv) - if err != nil { - return errors.New("failed to register purchase order service") - } - - err = registry.Register(scheme, srv) - if err != nil { - return errors.New("failed to register purchase order service: %v", err) - } - - ctx[BootstrappedPOHandler] = GRPCHandler(cfgSrv, srv) - return nil -} diff --git a/documents/purchaseorder/bootstrapper_test.go b/documents/purchaseorder/bootstrapper_test.go deleted file mode 100644 index b6911430f..000000000 --- a/documents/purchaseorder/bootstrapper_test.go +++ /dev/null @@ -1,14 +0,0 @@ -// +build unit - -package purchaseorder - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestBootstrapper_Bootstrap(t *testing.T) { - err := (&Bootstrapper{}).Bootstrap(map[string]interface{}{}) - assert.Error(t, err, "Should throw an error because of empty context") -} diff --git a/documents/purchaseorder/converters.go b/documents/purchaseorder/converters.go deleted file mode 100644 index ad418877f..000000000 --- a/documents/purchaseorder/converters.go +++ /dev/null @@ -1,382 +0,0 @@ -package purchaseorder - -import ( - "github.com/centrifuge/centrifuge-protobufs/gen/go/purchaseorder" - "github.com/centrifuge/go-centrifuge/documents" - clientpb "github.com/centrifuge/go-centrifuge/protobufs/gen/go/purchaseorder" - "github.com/centrifuge/go-centrifuge/utils/timeutils" -) - -func toClientLineItems(items []*LineItem) ([]*clientpb.LineItem, error) { - var citems []*clientpb.LineItem - for _, i := range items { - decs := documents.DecimalsToStrings(i.UnitOfMeasure, i.Quantity, i.PricePerUnit, i.AmountInvoiced, i.AmountTotal, i.ReceivedQuantity) - pts, err := timeutils.ToProtoTimestamps(i.DateCreated, i.DateUpdated) - if err != nil { - return nil, err - } - - activities, err := toClientActivities(i.Activities) - if err != nil { - return nil, err - } - - citems = append(citems, &clientpb.LineItem{ - Status: i.Status, - Description: i.Description, - ItemNumber: i.ItemNumber, - UnitOfMeasure: decs[0], - Quantity: decs[1], - PricePerUnit: decs[2], - AmountInvoiced: decs[3], - AmountTotal: decs[4], - ReceivedQuantity: decs[5], - DateCreated: pts[0], - DateUpdated: pts[1], - PartNo: i.PartNumber, - RequisitionItem: i.RequisitionItem, - RequisitionNumber: i.RequisitionNumber, - RevisionNumber: int64(i.RevisionNumber), - Activities: activities, - TaxItems: toClientTaxItems(i.TaxItems), - }) - } - - return citems, nil -} - -func toClientActivities(activities []*LineItemActivity) ([]*clientpb.LineItemActivity, error) { - var cactivities []*clientpb.LineItemActivity - for _, a := range activities { - pts, err := timeutils.ToProtoTimestamps(a.Date) - if err != nil { - return nil, err - } - - decs := documents.DecimalsToStrings(a.Quantity, a.Amount) - cactivities = append(cactivities, &clientpb.LineItemActivity{ - Quantity: decs[0], - Amount: decs[1], - ItemNumber: a.ItemNumber, - Status: a.Status, - Date: pts[0], - ReferenceDocumentId: a.ReferenceDocumentID, - ReferenceDocumentItem: a.ReferenceDocumentItem, - }) - } - - return cactivities, nil -} - -func toClientTaxItems(items []*TaxItem) []*clientpb.TaxItem { - var citems []*clientpb.TaxItem - for _, i := range items { - decs := documents.DecimalsToStrings(i.TaxAmount, i.TaxBaseAmount, i.TaxCode, i.TaxRate) - citems = append(citems, &clientpb.TaxItem{ - ItemNumber: i.ItemNumber, - PurchaseOrderItemNumber: i.PurchaseOrderItemNumber, - TaxAmount: decs[0], - TaxBaseAmount: decs[1], - TaxCode: decs[2], - TaxRate: decs[3], - }) - } - - return citems -} - -func toP2PLineItems(items []*LineItem) ([]*purchaseorderpb.LineItem, error) { - var pitems []*purchaseorderpb.LineItem - for _, i := range items { - decs, err := documents.DecimalsToBytes(i.UnitOfMeasure, i.Quantity, i.PricePerUnit, i.AmountInvoiced, i.AmountTotal, i.ReceivedQuantity) - if err != nil { - return nil, err - } - - patts, err := toP2PActivities(i.Activities) - if err != nil { - return nil, err - } - - pti, err := toP2PTaxItems(i.TaxItems) - if err != nil { - return nil, err - } - - pts, err := timeutils.ToProtoTimestamps(i.DateCreated, i.DateUpdated) - if err != nil { - return nil, err - } - - pitems = append(pitems, &purchaseorderpb.LineItem{ - Status: i.Status, - Description: i.Description, - ItemNumber: i.ItemNumber, - UnitOfMeasure: decs[0], - Quantity: decs[1], - PricePerUnit: decs[2], - AmountInvoiced: decs[3], - AmountTotal: decs[4], - ReceivedQuantity: decs[5], - DateCreated: pts[0], - DateUpdated: pts[1], - PartNo: i.PartNumber, - RequisitionItem: i.RequisitionItem, - RequisitionNumber: i.RequisitionNumber, - RevisionNumber: int64(i.RevisionNumber), - Activities: patts, - TaxItems: pti, - }) - } - - return pitems, nil -} - -func toP2PActivities(activities []*LineItemActivity) ([]*purchaseorderpb.LineItemActivity, error) { - var pactivities []*purchaseorderpb.LineItemActivity - for _, a := range activities { - decs, err := documents.DecimalsToBytes(a.Quantity, a.Amount) - if err != nil { - return nil, err - } - - pts, err := timeutils.ToProtoTimestamps(a.Date) - if err != nil { - return nil, err - } - - pactivities = append(pactivities, &purchaseorderpb.LineItemActivity{ - Quantity: decs[0], - Amount: decs[1], - ItemNumber: a.ItemNumber, - Status: a.Status, - Date: pts[0], - ReferenceDocumentId: a.ReferenceDocumentID, - ReferenceDocumentItem: a.ReferenceDocumentItem, - }) - } - - return pactivities, nil -} - -func toP2PTaxItems(items []*TaxItem) ([]*purchaseorderpb.TaxItem, error) { - var pitems []*purchaseorderpb.TaxItem - for _, i := range items { - decs, err := documents.DecimalsToBytes(i.TaxAmount, i.TaxBaseAmount, i.TaxCode, i.TaxRate) - if err != nil { - return nil, err - } - pitems = append(pitems, &purchaseorderpb.TaxItem{ - ItemNumber: i.ItemNumber, - PurchaseOrderItemNumber: i.PurchaseOrderItemNumber, - TaxAmount: decs[0], - TaxBaseAmount: decs[1], - TaxCode: decs[2], - TaxRate: decs[3], - }) - } - - return pitems, nil -} - -func fromClientLineItems(citems []*clientpb.LineItem) ([]*LineItem, error) { - var items []*LineItem - for _, ci := range citems { - decs, err := documents.StringsToDecimals( - ci.AmountInvoiced, - ci.AmountTotal, - ci.PricePerUnit, - ci.UnitOfMeasure, - ci.Quantity, - ci.ReceivedQuantity) - if err != nil { - return nil, err - } - - ti, err := fromClientTaxItems(ci.TaxItems) - if err != nil { - return nil, err - } - - la, err := fromClientLineItemActivities(ci.Activities) - if err != nil { - return nil, err - } - - tms, err := timeutils.FromProtoTimestamps(ci.DateCreated, ci.DateUpdated) - if err != nil { - return nil, err - } - - items = append(items, &LineItem{ - Status: ci.Status, - ItemNumber: ci.ItemNumber, - Description: ci.Description, - AmountInvoiced: decs[0], - AmountTotal: decs[1], - RequisitionNumber: ci.RequisitionNumber, - RequisitionItem: ci.RequisitionItem, - RevisionNumber: int(ci.RevisionNumber), - PricePerUnit: decs[2], - UnitOfMeasure: decs[3], - Quantity: decs[4], - ReceivedQuantity: decs[5], - DateCreated: tms[0], - DateUpdated: tms[1], - PartNumber: ci.PartNo, - TaxItems: ti, - Activities: la, - }) - } - - return items, nil -} - -func fromClientTaxItems(citems []*clientpb.TaxItem) ([]*TaxItem, error) { - var items []*TaxItem - for _, ci := range citems { - decs, err := documents.StringsToDecimals(ci.TaxAmount, ci.TaxRate, ci.TaxCode, ci.TaxBaseAmount) - if err != nil { - return nil, err - } - - items = append(items, &TaxItem{ - ItemNumber: ci.ItemNumber, - PurchaseOrderItemNumber: ci.PurchaseOrderItemNumber, - TaxAmount: decs[0], - TaxRate: decs[1], - TaxCode: decs[2], - TaxBaseAmount: decs[3], - }) - } - - return items, nil -} - -func fromClientLineItemActivities(catts []*clientpb.LineItemActivity) ([]*LineItemActivity, error) { - var atts []*LineItemActivity - for _, ca := range catts { - decs, err := documents.StringsToDecimals(ca.Quantity, ca.Amount) - if err != nil { - return nil, err - } - - tms, err := timeutils.FromProtoTimestamps(ca.Date) - if err != nil { - return nil, err - } - - atts = append(atts, &LineItemActivity{ - ItemNumber: ca.ItemNumber, - Status: ca.Status, - Quantity: decs[0], - Amount: decs[1], - Date: tms[0], - ReferenceDocumentItem: ca.ReferenceDocumentItem, - ReferenceDocumentID: ca.ReferenceDocumentId, - }) - } - - return atts, nil -} - -func fromP2PLineItemActivities(patts []*purchaseorderpb.LineItemActivity) ([]*LineItemActivity, error) { - var atts []*LineItemActivity - for _, ca := range patts { - decs, err := documents.BytesToDecimals(ca.Quantity, ca.Amount) - if err != nil { - return nil, err - } - - tms, err := timeutils.FromProtoTimestamps(ca.Date) - if err != nil { - return nil, err - } - - atts = append(atts, &LineItemActivity{ - ItemNumber: ca.ItemNumber, - Status: ca.Status, - Quantity: decs[0], - Amount: decs[1], - Date: tms[0], - ReferenceDocumentItem: ca.ReferenceDocumentItem, - ReferenceDocumentID: ca.ReferenceDocumentId, - }) - } - - return atts, nil -} - -func fromP2PTaxItems(pitems []*purchaseorderpb.TaxItem) ([]*TaxItem, error) { - var items []*TaxItem - for _, ci := range pitems { - decs, err := documents.BytesToDecimals(ci.TaxAmount, ci.TaxRate, ci.TaxCode, ci.TaxBaseAmount) - if err != nil { - return nil, err - } - - items = append(items, &TaxItem{ - ItemNumber: ci.ItemNumber, - PurchaseOrderItemNumber: ci.PurchaseOrderItemNumber, - TaxAmount: decs[0], - TaxRate: decs[1], - TaxCode: decs[2], - TaxBaseAmount: decs[3], - }) - } - - return items, nil -} - -func fromP2PLineItems(pitems []*purchaseorderpb.LineItem) ([]*LineItem, error) { - var items []*LineItem - for _, ci := range pitems { - decs, err := documents.BytesToDecimals( - ci.AmountInvoiced, - ci.AmountTotal, - ci.PricePerUnit, - ci.UnitOfMeasure, - ci.Quantity, - ci.ReceivedQuantity) - if err != nil { - return nil, err - } - - ti, err := fromP2PTaxItems(ci.TaxItems) - if err != nil { - return nil, err - } - - la, err := fromP2PLineItemActivities(ci.Activities) - if err != nil { - return nil, err - } - - tms, err := timeutils.FromProtoTimestamps(ci.DateCreated, ci.DateUpdated) - if err != nil { - return nil, err - } - - items = append(items, &LineItem{ - Status: ci.Status, - ItemNumber: ci.ItemNumber, - Description: ci.Description, - AmountInvoiced: decs[0], - AmountTotal: decs[1], - RequisitionNumber: ci.RequisitionNumber, - RequisitionItem: ci.RequisitionItem, - RevisionNumber: int(ci.RevisionNumber), - PricePerUnit: decs[2], - UnitOfMeasure: decs[3], - Quantity: decs[4], - ReceivedQuantity: decs[5], - DateCreated: tms[0], - DateUpdated: tms[1], - PartNumber: ci.PartNo, - TaxItems: ti, - Activities: la, - }) - } - - return items, nil -} diff --git a/documents/purchaseorder/converters_test.go b/documents/purchaseorder/converters_test.go deleted file mode 100644 index ec640b592..000000000 --- a/documents/purchaseorder/converters_test.go +++ /dev/null @@ -1,137 +0,0 @@ -// +build unit - -package purchaseorder - -import ( - "testing" - - "github.com/centrifuge/go-centrifuge/documents" - "github.com/centrifuge/go-centrifuge/utils" - "github.com/stretchr/testify/assert" -) - -func TestLineItemActivities(t *testing.T) { - dec := new(documents.Decimal) - assert.NoError(t, dec.SetString("1.1")) - items := []*LineItemActivity{ - { - ItemNumber: "12345", - Status: "pending", - Amount: dec, - }, - } - - citems, err := toClientActivities(items) - assert.NoError(t, err) - pitems, err := toP2PActivities(items) - assert.NoError(t, err) - - fcitems, err := fromClientLineItemActivities(citems) - assert.NoError(t, err) - assert.Equal(t, items, fcitems) - - fpitems, err := fromP2PLineItemActivities(pitems) - assert.NoError(t, err) - assert.Equal(t, items, fpitems) - - citems[0].Amount = "0.1.1" - _, err = fromClientLineItemActivities(citems) - assert.Error(t, err) - - pitems[0].Amount = utils.RandomSlice(40) - _, err = fromP2PLineItemActivities(pitems) - assert.Error(t, err) -} - -func TestTaxItems(t *testing.T) { - dec := new(documents.Decimal) - assert.NoError(t, dec.SetString("1.1")) - items := []*TaxItem{ - { - ItemNumber: "12345", - TaxAmount: dec, - }, - } - - citems := toClientTaxItems(items) - pitems, err := toP2PTaxItems(items) - assert.NoError(t, err) - - fcitems, err := fromClientTaxItems(citems) - assert.NoError(t, err) - assert.Equal(t, items, fcitems) - - fpitems, err := fromP2PTaxItems(pitems) - assert.NoError(t, err) - assert.Equal(t, items, fpitems) - - citems[0].TaxAmount = "1.1.1" - _, err = fromClientTaxItems(citems) - assert.Error(t, err) - - pitems[0].TaxAmount = utils.RandomSlice(40) - _, err = fromP2PTaxItems(pitems) - assert.Error(t, err) -} - -func TestLineItems(t *testing.T) { - dec := new(documents.Decimal) - assert.NoError(t, dec.SetString("1.1")) - items := []*LineItem{ - { - Status: "pending", - AmountTotal: dec, - Activities: []*LineItemActivity{ - { - ItemNumber: "12345", - Status: "pending", - Amount: dec, - }, - }, - TaxItems: []*TaxItem{ - { - ItemNumber: "12345", - TaxAmount: dec, - }, - }, - }, - } - - citems, err := toClientLineItems(items) - assert.NoError(t, err) - pitems, err := toP2PLineItems(items) - assert.NoError(t, err) - - fcitems, err := fromClientLineItems(citems) - assert.NoError(t, err) - assert.Equal(t, items, fcitems) - - fpitems, err := fromP2PLineItems(pitems) - assert.NoError(t, err) - assert.Equal(t, items, fpitems) - - citems[0].Activities[0].Amount = "0.1.1" - _, err = fromClientLineItems(citems) - assert.Error(t, err) - - citems[0].TaxItems[0].TaxAmount = "1.1.1" - _, err = fromClientLineItems(citems) - assert.Error(t, err) - - citems[0].AmountTotal = "0.1.1" - _, err = fromClientLineItems(citems) - assert.Error(t, err) - - rdec := utils.RandomSlice(40) - pitems[0].Activities[0].Amount = rdec - _, err = fromP2PLineItems(pitems) - assert.Error(t, err) - - pitems[0].TaxItems[0].TaxAmount = rdec - _, err = fromP2PLineItems(pitems) - assert.Error(t, err) - - pitems[0].AmountTotal = rdec - _, err = fromP2PLineItems(pitems) - assert.Error(t, err) -} diff --git a/documents/purchaseorder/handler.go b/documents/purchaseorder/handler.go deleted file mode 100644 index 41a69798b..000000000 --- a/documents/purchaseorder/handler.go +++ /dev/null @@ -1,157 +0,0 @@ -package purchaseorder - -import ( - "github.com/centrifuge/go-centrifuge/centerrors" - "github.com/centrifuge/go-centrifuge/config" - "github.com/centrifuge/go-centrifuge/contextutil" - clientpurchaseorderpb "github.com/centrifuge/go-centrifuge/protobufs/gen/go/purchaseorder" - "github.com/ethereum/go-ethereum/common/hexutil" - logging "github.com/ipfs/go-log" - "golang.org/x/net/context" -) - -var apiLog = logging.Logger("purchaseorder-api") - -// grpcHandler handles all the purchase order document related actions -// anchoring, sending, finding stored purchase order document -type grpcHandler struct { - service Service - config config.Service -} - -// GRPCHandler returns an implementation of the purchaseorder DocumentServiceServer -func GRPCHandler(config config.Service, srv Service) clientpurchaseorderpb.PurchaseOrderServiceServer { - return grpcHandler{ - service: srv, - config: config, - } -} - -// Create validates the purchase order, persists it to DB, and anchors it the chain -func (h grpcHandler) Create(ctx context.Context, req *clientpurchaseorderpb.PurchaseOrderCreatePayload) (*clientpurchaseorderpb.PurchaseOrderResponse, error) { - apiLog.Debugf("Create request %v", req) - ctxh, err := contextutil.Context(ctx, h.config) - if err != nil { - apiLog.Error(err) - return nil, err - } - - doc, err := h.service.DeriveFromCreatePayload(ctxh, req) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "could not derive create payload") - } - - // validate, persist, and anchor - doc, jobID, _, err := h.service.Create(ctxh, doc) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "could not create document") - } - - resp, err := h.service.DerivePurchaseOrderResponse(doc) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "could not derive response") - } - - resp.Header.JobId = jobID.String() - return resp, nil -} - -// Update handles the document update and anchoring -func (h grpcHandler) Update(ctx context.Context, payload *clientpurchaseorderpb.PurchaseOrderUpdatePayload) (*clientpurchaseorderpb.PurchaseOrderResponse, error) { - apiLog.Debugf("Update request %v", payload) - ctxHeader, err := contextutil.Context(ctx, h.config) - if err != nil { - apiLog.Error(err) - return nil, err - } - - doc, err := h.service.DeriveFromUpdatePayload(ctxHeader, payload) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "could not derive update payload") - } - - doc, jobID, _, err := h.service.Update(ctxHeader, doc) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "could not update document") - } - - resp, err := h.service.DerivePurchaseOrderResponse(doc) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "could not derive response") - } - - resp.Header.JobId = jobID.String() - return resp, nil -} - -// GetVersion returns the requested version of a purchase order -func (h grpcHandler) GetVersion(ctx context.Context, req *clientpurchaseorderpb.GetVersionRequest) (*clientpurchaseorderpb.PurchaseOrderResponse, error) { - apiLog.Debugf("GetVersion request %v", req) - ctxHeader, err := contextutil.Context(ctx, h.config) - if err != nil { - apiLog.Error(err) - return nil, err - } - - identifier, err := hexutil.Decode(req.DocumentId) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "identifier is invalid") - } - - version, err := hexutil.Decode(req.VersionId) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "version is invalid") - } - - model, err := h.service.GetVersion(ctxHeader, identifier, version) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "document not found") - } - - resp, err := h.service.DerivePurchaseOrderResponse(model) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "could not derive response") - } - - return resp, nil -} - -// Get returns the purchase order the latest version of the document with given identifier -func (h grpcHandler) Get(ctx context.Context, getRequest *clientpurchaseorderpb.GetRequest) (*clientpurchaseorderpb.PurchaseOrderResponse, error) { - apiLog.Debugf("Get request %v", getRequest) - ctxHeader, err := contextutil.Context(ctx, h.config) - if err != nil { - apiLog.Error(err) - return nil, err - } - - identifier, err := hexutil.Decode(getRequest.DocumentId) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "identifier is an invalid hex string") - } - - model, err := h.service.GetCurrentVersion(ctxHeader, identifier) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "document not found") - } - - resp, err := h.service.DerivePurchaseOrderResponse(model) - if err != nil { - apiLog.Error(err) - return nil, centerrors.Wrap(err, "could not derive response") - } - - return resp, nil -} diff --git a/documents/purchaseorder/handler_test.go b/documents/purchaseorder/handler_test.go deleted file mode 100644 index c65139fde..000000000 --- a/documents/purchaseorder/handler_test.go +++ /dev/null @@ -1,248 +0,0 @@ -// +build unit - -package purchaseorder - -import ( - "context" - "testing" - - "github.com/centrifuge/centrifuge-protobufs/gen/go/coredocument" - "github.com/centrifuge/go-centrifuge/contextutil" - "github.com/centrifuge/go-centrifuge/documents" - "github.com/centrifuge/go-centrifuge/errors" - "github.com/centrifuge/go-centrifuge/jobs" - "github.com/centrifuge/go-centrifuge/protobufs/gen/go/document" - clientpopb "github.com/centrifuge/go-centrifuge/protobufs/gen/go/purchaseorder" - "github.com/centrifuge/go-centrifuge/testingutils/config" - "github.com/centrifuge/go-centrifuge/testingutils/documents" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - -type mockService struct { - Service - mock.Mock -} - -func (m mockService) Create(ctx context.Context, model documents.Model) (documents.Model, jobs.JobID, chan bool, error) { - args := m.Called(ctx, model) - model, _ = args.Get(0).(documents.Model) - return model, contextutil.Job(ctx), nil, args.Error(2) -} - -func (m mockService) Update(ctx context.Context, model documents.Model) (documents.Model, jobs.JobID, chan bool, error) { - args := m.Called(ctx, model) - model, _ = args.Get(0).(documents.Model) - return model, contextutil.Job(ctx), nil, args.Error(2) -} - -func (m mockService) DeriveFromCreatePayload(ctx context.Context, payload *clientpopb.PurchaseOrderCreatePayload) (documents.Model, error) { - args := m.Called(ctx, payload) - model, _ := args.Get(0).(documents.Model) - return model, args.Error(1) -} - -func (m mockService) GetCurrentVersion(ctx context.Context, documentID []byte) (documents.Model, error) { - args := m.Called(ctx, documentID) - data, _ := args.Get(0).(documents.Model) - return data, args.Error(1) -} - -func (m mockService) GetVersion(ctx context.Context, documentID []byte, version []byte) (documents.Model, error) { - args := m.Called(ctx, documentID, version) - data, _ := args.Get(0).(documents.Model) - return data, args.Error(1) -} - -func (m mockService) DerivePurchaseOrderData(po documents.Model) (*clientpopb.PurchaseOrderData, error) { - args := m.Called(po) - data, _ := args.Get(0).(*clientpopb.PurchaseOrderData) - return data, args.Error(1) -} - -func (m mockService) DerivePurchaseOrderResponse(po documents.Model) (*clientpopb.PurchaseOrderResponse, error) { - args := m.Called(po) - data, _ := args.Get(0).(*clientpopb.PurchaseOrderResponse) - return data, args.Error(1) -} - -func (m mockService) DeriveFromUpdatePayload(ctx context.Context, payload *clientpopb.PurchaseOrderUpdatePayload) (documents.Model, error) { - args := m.Called(ctx, payload) - doc, _ := args.Get(0).(documents.Model) - return doc, args.Error(1) -} - -func TestGRPCHandler_Create(t *testing.T) { - h := getHandler() - req := testingdocuments.CreatePOPayload() - ctx := testingconfig.HandlerContext(configService) - model := &testingdocuments.MockModel{} - - // derive fails - srv := h.service.(*mockService) - srv.On("DeriveFromCreatePayload", mock.Anything, req).Return(nil, errors.New("derive failed")).Once() - h.service = srv - resp, err := h.Create(ctx, req) - srv.AssertExpectations(t) - assert.Nil(t, resp) - assert.Error(t, err) - assert.Contains(t, err.Error(), "derive failed") - - // create fails - srv.On("DeriveFromCreatePayload", mock.Anything, req).Return(model, nil).Once() - srv.On("Create", mock.Anything, model).Return(nil, jobs.NilJobID().String(), errors.New("create failed")).Once() - h.service = srv - resp, err = h.Create(ctx, req) - srv.AssertExpectations(t) - assert.Nil(t, resp) - assert.Error(t, err) - assert.Contains(t, err.Error(), "create failed") - - // derive response fails - srv.On("DeriveFromCreatePayload", mock.Anything, req).Return(model, nil).Once() - srv.On("Create", mock.Anything, model).Return(model, jobs.NilJobID().String(), nil).Once() - srv.On("DerivePurchaseOrderResponse", model).Return(nil, errors.New("derive response fails")).Once() - h.service = srv - resp, err = h.Create(ctx, req) - srv.AssertExpectations(t) - assert.Nil(t, resp) - assert.Error(t, err) - assert.Contains(t, err.Error(), "derive response fails") - - // success - eresp := &clientpopb.PurchaseOrderResponse{Header: new(documentpb.ResponseHeader)} - srv.On("DeriveFromCreatePayload", mock.Anything, req).Return(model, nil).Once() - srv.On("Create", mock.Anything, model).Return(model, jobs.NilJobID().String(), nil).Once() - srv.On("DerivePurchaseOrderResponse", model).Return(eresp, nil).Once() - h.service = srv - resp, err = h.Create(ctx, req) - srv.AssertExpectations(t) - assert.Nil(t, err) - assert.NotNil(t, resp) - assert.Equal(t, eresp, resp) -} - -func TestGrpcHandler_Update(t *testing.T) { - h := getHandler() - p := testingdocuments.CreatePOPayload() - req := &clientpopb.PurchaseOrderUpdatePayload{ - Data: p.Data, - ReadAccess: p.ReadAccess, - WriteAccess: p.WriteAccess, - } - ctx := testingconfig.HandlerContext(configService) - model := &testingdocuments.MockModel{} - - // derive fails - srv := h.service.(*mockService) - srv.On("DeriveFromUpdatePayload", mock.Anything, req).Return(nil, errors.New("derive failed")).Once() - h.service = srv - resp, err := h.Update(ctx, req) - srv.AssertExpectations(t) - assert.Nil(t, resp) - assert.Error(t, err) - assert.Contains(t, err.Error(), "derive failed") - - // create fails - srv.On("DeriveFromUpdatePayload", mock.Anything, req).Return(model, nil).Once() - srv.On("Update", mock.Anything, model).Return(nil, jobs.NilJobID().String(), errors.New("update failed")).Once() - h.service = srv - resp, err = h.Update(ctx, req) - srv.AssertExpectations(t) - assert.Nil(t, resp) - assert.Error(t, err) - assert.Contains(t, err.Error(), "update failed") - - // derive response fails - srv.On("DeriveFromUpdatePayload", mock.Anything, req).Return(model, nil).Once() - srv.On("Update", mock.Anything, model).Return(model, jobs.NilJobID().String(), nil).Once() - srv.On("DerivePurchaseOrderResponse", model).Return(nil, errors.New("derive response fails")).Once() - h.service = srv - resp, err = h.Update(ctx, req) - srv.AssertExpectations(t) - assert.Nil(t, resp) - assert.Error(t, err) - assert.Contains(t, err.Error(), "derive response fails") - - // success - eresp := &clientpopb.PurchaseOrderResponse{Header: new(documentpb.ResponseHeader)} - srv.On("DeriveFromUpdatePayload", mock.Anything, req).Return(model, nil).Once() - srv.On("Update", mock.Anything, model).Return(model, jobs.NilJobID().String(), nil).Once() - srv.On("DerivePurchaseOrderResponse", model).Return(eresp, nil).Once() - h.service = srv - resp, err = h.Update(ctx, req) - srv.AssertExpectations(t) - assert.Nil(t, err) - assert.NotNil(t, resp) - assert.Equal(t, eresp, resp) -} - -type mockModel struct { - documents.Model - mock.Mock - CoreDocument *coredocumentpb.CoreDocument -} - -func getHandler() *grpcHandler { - return &grpcHandler{service: &mockService{}, config: configService} -} - -func TestGrpcHandler_Get(t *testing.T) { - documentID := "0x01010101" - identifierBytes, _ := hexutil.Decode(documentID) - h := getHandler() - srv := h.service.(*mockService) - model := new(mockModel) - payload := &clientpopb.GetRequest{DocumentId: documentID} - response := &clientpopb.PurchaseOrderResponse{} - srv.On("GetCurrentVersion", mock.Anything, identifierBytes).Return(model, nil) - srv.On("DerivePurchaseOrderResponse", model).Return(response, nil) - res, err := h.Get(testingconfig.HandlerContext(configService), payload) - model.AssertExpectations(t) - srv.AssertExpectations(t) - assert.Nil(t, err, "must be nil") - assert.NotNil(t, res, "must be non nil") - assert.Equal(t, res, response) -} - -func TestGrpcHandler_GetVersion_invalid_input(t *testing.T) { - h := getHandler() - srv := h.service.(*mockService) - payload := &clientpopb.GetVersionRequest{DocumentId: "0x0x", VersionId: "0x00"} - res, err := h.GetVersion(testingconfig.HandlerContext(configService), payload) - assert.Error(t, err) - assert.EqualError(t, err, "identifier is invalid: invalid hex string") - payload.VersionId = "0x0x" - payload.DocumentId = "0x01" - - res, err = h.GetVersion(testingconfig.HandlerContext(configService), payload) - assert.Error(t, err) - assert.EqualError(t, err, "version is invalid: invalid hex string") - payload.VersionId = "0x00" - payload.DocumentId = "0x01" - - mockErr := errors.New("not found") - srv.On("GetVersion", mock.Anything, []byte{0x01}, []byte{0x00}).Return(nil, mockErr) - res, err = h.GetVersion(testingconfig.HandlerContext(configService), payload) - srv.AssertExpectations(t) - assert.EqualError(t, err, "document not found: not found") - assert.Nil(t, res) -} - -func TestGrpcHandler_GetVersion(t *testing.T) { - h := getHandler() - srv := h.service.(*mockService) - model := new(mockModel) - payload := &clientpopb.GetVersionRequest{DocumentId: "0x01", VersionId: "0x00"} - - response := &clientpopb.PurchaseOrderResponse{} - srv.On("GetVersion", mock.Anything, []byte{0x01}, []byte{0x00}).Return(model, nil) - srv.On("DerivePurchaseOrderResponse", model).Return(response, nil) - res, err := h.GetVersion(testingconfig.HandlerContext(configService), payload) - model.AssertExpectations(t) - srv.AssertExpectations(t) - assert.Nil(t, err) - assert.NotNil(t, res) - assert.Equal(t, res, response) -} diff --git a/documents/purchaseorder/model.go b/documents/purchaseorder/model.go deleted file mode 100644 index d12d76803..000000000 --- a/documents/purchaseorder/model.go +++ /dev/null @@ -1,657 +0,0 @@ -package purchaseorder - -import ( - "encoding/json" - "reflect" - "time" - - "github.com/centrifuge/centrifuge-protobufs/documenttypes" - "github.com/centrifuge/centrifuge-protobufs/gen/go/coredocument" - "github.com/centrifuge/centrifuge-protobufs/gen/go/purchaseorder" - "github.com/centrifuge/go-centrifuge/documents" - "github.com/centrifuge/go-centrifuge/errors" - "github.com/centrifuge/go-centrifuge/identity" - clientpurchaseorderpb "github.com/centrifuge/go-centrifuge/protobufs/gen/go/purchaseorder" - "github.com/centrifuge/go-centrifuge/utils/timeutils" - "github.com/centrifuge/precise-proofs/proofs" - "github.com/centrifuge/precise-proofs/proofs/proto" - "github.com/ethereum/go-ethereum/common" - "github.com/golang/protobuf/proto" - "github.com/golang/protobuf/ptypes/any" -) - -const ( - prefix string = "po" - scheme = "purchase_order" - - // ErrPOInvalidData sentinel error when data unmarshal is failed. - ErrPOInvalidData = errors.Error("invalid purchase order data") -) - -// tree prefixes for specific to documents use the second byte of a 4 byte slice by convention -func compactPrefix() []byte { return []byte{0, 2, 0, 0} } - -// Data represents Purchase Order Data. -type Data struct { - Status string `json:"status"` - Number string `json:"number"` - SenderOrderID string `json:"sender_order_id"` - RecipientOrderID string `json:"recipient_order_id"` - RequisitionID string `json:"requisition_id"` - RequesterName string `json:"requester_name"` - RequesterEmail string `json:"requester_email"` - ShipToCompanyName string `json:"ship_to_company_name"` - ShipToContactPersonName string `json:"ship_to_contact_person_name"` - ShipToStreet1 string `json:"ship_to_street_1"` - ShipToStreet2 string `json:"ship_to_street_2"` - ShipToCity string `json:"ship_to_city"` - ShipToZipcode string `json:"ship_to_zipcode"` - ShipToState string `json:"ship_to_state"` - ShipToCountry string `json:"ship_to_country"` - PaymentTerms string `json:"payment_terms"` - Currency string `json:"currency"` - TotalAmount *documents.Decimal `json:"total_amount"` - Recipient *identity.DID `json:"recipient"` - Sender *identity.DID `json:"sender"` - Comment string `json:"comment"` - DateSent *time.Time `json:"date_sent"` - DateConfirmed *time.Time `json:"date_confirmed"` - DateUpdated *time.Time `json:"date_updated"` - DateCreated *time.Time `json:"date_created"` - Attachments []*documents.BinaryAttachment `json:"attachments"` - LineItems []*LineItem `json:"line_items"` - PaymentDetails []*documents.PaymentDetails `json:"payment_details"` -} - -// PurchaseOrder implements the documents.Model keeps track of purchase order related fields and state -type PurchaseOrder struct { - *documents.CoreDocument - Data Data `json:"data"` -} - -// LineItemActivity describes a single line item activity. -type LineItemActivity struct { - ItemNumber string `json:"item_number"` - Status string `json:"status"` - Quantity *documents.Decimal `json:"quantity"` - Amount *documents.Decimal `json:"amount"` - ReferenceDocumentID string `json:"reference_document_id"` - ReferenceDocumentItem string `json:"reference_document_item"` - Date *time.Time `json:"date"` -} - -// TaxItem describes a single Purchase Order tax item. -type TaxItem struct { - ItemNumber string `json:"item_number"` - PurchaseOrderItemNumber string `json:"purchase_order_item_number"` - TaxAmount *documents.Decimal `json:"tax_amount"` - TaxRate *documents.Decimal `json:"tax_rate"` - TaxCode *documents.Decimal `json:"tax_code"` - TaxBaseAmount *documents.Decimal `json:"tax_base_amount"` -} - -// LineItem describes a single LineItem Activity -type LineItem struct { - Status string `json:"status"` - ItemNumber string `json:"item_number"` - Description string `json:"description"` - AmountInvoiced *documents.Decimal `json:"amount_invoiced"` - AmountTotal *documents.Decimal `json:"amount_total"` - RequisitionNumber string `json:"requisition_number"` - RequisitionItem string `json:"requisition_item"` - PartNumber string `json:"part_number"` - PricePerUnit *documents.Decimal `json:"price_per_unit"` - UnitOfMeasure *documents.Decimal `json:"unit_of_measure"` - Quantity *documents.Decimal `json:"quantity"` - ReceivedQuantity *documents.Decimal `json:"received_quantity"` - DateUpdated *time.Time `json:"date_updated"` - DateCreated *time.Time `json:"date_created"` - RevisionNumber int `json:"revision_number"` - Activities []*LineItemActivity `json:"activities"` - TaxItems []*TaxItem `json:"tax_items"` -} - -// getClientData returns the client data from the purchaseOrder model -func (p *PurchaseOrder) getClientData() (*clientpurchaseorderpb.PurchaseOrderData, error) { - data := p.Data - decs := documents.DecimalsToStrings(data.TotalAmount) - dids := identity.DIDsToStrings(data.Recipient, data.Sender) - - pd, err := documents.ToClientPaymentDetails(data.PaymentDetails) - if err != nil { - return nil, err - } - - pts, err := timeutils.ToProtoTimestamps(data.DateCreated, data.DateUpdated, data.DateConfirmed, data.DateSent) - if err != nil { - return nil, err - } - - lis, err := toClientLineItems(data.LineItems) - if err != nil { - return nil, err - } - - return &clientpurchaseorderpb.PurchaseOrderData{ - Status: data.Status, - Number: data.Number, - SenderOrderId: data.SenderOrderID, - TotalAmount: decs[0], - Recipient: dids[0], - Sender: dids[1], - DateCreated: pts[0], - DateUpdated: pts[1], - RequesterName: data.RequesterName, - RequesterEmail: data.RequesterEmail, - Comment: data.Comment, - Currency: data.Currency, - ShipToCountry: data.ShipToCountry, - ShipToState: data.ShipToState, - ShipToZipcode: data.ShipToZipcode, - ShipToCity: data.ShipToCity, - ShipToStreet1: data.ShipToStreet1, - ShipToStreet2: data.ShipToStreet2, - ShipToContactPersonName: data.ShipToContactPersonName, - ShipToCompanyName: data.ShipToCompanyName, - DateConfirmed: pts[2], - DateSent: pts[3], - PaymentTerms: data.PaymentTerms, - RecipientOrderId: data.RecipientOrderID, - RequisitionId: data.RequisitionID, - PaymentDetails: pd, - Attachments: documents.ToClientAttachments(data.Attachments), - LineItems: lis, - }, nil -} - -// createP2PProtobuf returns centrifuge protobuf specific purchaseOrderData -func (p *PurchaseOrder) createP2PProtobuf() (*purchaseorderpb.PurchaseOrderData, error) { - data := p.Data - decs, err := documents.DecimalsToBytes(data.TotalAmount) - if err != nil { - return nil, err - } - - pd, err := documents.ToProtocolPaymentDetails(data.PaymentDetails) - if err != nil { - return nil, err - } - - li, err := toP2PLineItems(data.LineItems) - if err != nil { - return nil, err - } - - pts, err := timeutils.ToProtoTimestamps(data.DateCreated, data.DateUpdated, data.DateConfirmed, data.DateSent) - if err != nil { - return nil, err - } - - dids := identity.DIDsToBytes(data.Recipient, data.Sender) - return &purchaseorderpb.PurchaseOrderData{ - Status: data.Status, - Number: data.Number, - SenderOrderId: data.SenderOrderID, - TotalAmount: decs[0], - Recipient: dids[0], - Sender: dids[1], - DateCreated: pts[0], - DateUpdated: pts[1], - RequesterName: data.RequesterName, - RequesterEmail: data.RequesterEmail, - Comment: data.Comment, - Currency: data.Currency, - ShipToCountry: data.ShipToCountry, - ShipToState: data.ShipToState, - ShipToZipcode: data.ShipToZipcode, - ShipToCity: data.ShipToCity, - ShipToStreet1: data.ShipToStreet1, - ShipToStreet2: data.ShipToStreet2, - ShipToContactPersonName: data.ShipToContactPersonName, - ShipToCompanyName: data.ShipToCompanyName, - DateConfirmed: pts[2], - DateSent: pts[3], - PaymentTerms: data.PaymentTerms, - RecipientOrderId: data.RecipientOrderID, - RequisitionId: data.RequisitionID, - PaymentDetails: pd, - Attachments: documents.ToProtocolAttachments(data.Attachments), - LineItems: li, - }, nil - -} - -// InitPurchaseOrderInput initialize the model based on the received parameters from the rest api call -func (p *PurchaseOrder) InitPurchaseOrderInput(payload *clientpurchaseorderpb.PurchaseOrderCreatePayload, self identity.DID) error { - err := p.initPurchaseOrderFromData(payload.Data) - if err != nil { - return err - } - - cs, err := documents.FromClientCollaboratorAccess(payload.ReadAccess, payload.WriteAccess) - if err != nil { - return err - } - cs.ReadWriteCollaborators = append(cs.ReadWriteCollaborators, self) - - attrs, err := documents.FromClientAttributes(payload.Attributes) - if err != nil { - return err - } - - cd, err := documents.NewCoreDocument(compactPrefix(), cs, attrs) - if err != nil { - return errors.New("failed to init core document: %v", err) - } - - p.CoreDocument = cd - return nil -} - -// initPurchaseOrderFromData initialises purchase order from purchaseOrderData -func (p *PurchaseOrder) initPurchaseOrderFromData(data *clientpurchaseorderpb.PurchaseOrderData) error { - atts, err := documents.FromClientAttachments(data.Attachments) - if err != nil { - return err - } - - pdetails, err := documents.FromClientPaymentDetails(data.PaymentDetails) - if err != nil { - return err - } - - decs, err := documents.StringsToDecimals(data.TotalAmount) - if err != nil { - return err - } - - dids, err := identity.StringsToDIDs(data.Recipient, data.Sender) - if err != nil { - return err - } - - li, err := fromClientLineItems(data.LineItems) - if err != nil { - return err - } - - tms, err := timeutils.FromProtoTimestamps(data.DateSent, data.DateUpdated, data.DateCreated, data.DateConfirmed) - if err != nil { - return err - } - - var d Data - d.Status = data.Status - d.Number = data.Number - d.SenderOrderID = data.SenderOrderId - d.RecipientOrderID = data.RecipientOrderId - d.RequisitionID = data.RequisitionId - d.RequesterEmail = data.RequesterEmail - d.RequesterName = data.RequesterName - d.ShipToCompanyName = data.ShipToCompanyName - d.ShipToContactPersonName = data.ShipToContactPersonName - d.ShipToStreet1 = data.ShipToStreet1 - d.ShipToStreet2 = data.ShipToStreet2 - d.ShipToCity = data.ShipToCity - d.ShipToZipcode = data.ShipToZipcode - d.ShipToState = data.ShipToState - d.ShipToCountry = data.ShipToCountry - d.PaymentTerms = data.PaymentTerms - d.Currency = data.Currency - d.Comment = data.Comment - d.DateSent = tms[0] - d.DateUpdated = tms[1] - d.DateCreated = tms[2] - d.DateConfirmed = tms[3] - d.Attachments = atts - d.PaymentDetails = pdetails - d.TotalAmount = decs[0] - d.Recipient = dids[0] - d.Sender = dids[1] - d.LineItems = li - p.Data = d - return nil -} - -// loadFromP2PProtobuf loads the purcase order from centrifuge protobuf purchase order data -func (p *PurchaseOrder) loadFromP2PProtobuf(data *purchaseorderpb.PurchaseOrderData) error { - pdetails, err := documents.FromProtocolPaymentDetails(data.PaymentDetails) - if err != nil { - return err - } - - decs, err := documents.BytesToDecimals(data.TotalAmount) - if err != nil { - return err - } - - dids, err := identity.BytesToDIDs(data.Recipient, data.Sender) - if err != nil { - return err - } - - li, err := fromP2PLineItems(data.LineItems) - if err != nil { - return err - } - - tms, err := timeutils.FromProtoTimestamps(data.DateSent, data.DateUpdated, data.DateCreated, data.DateConfirmed) - if err != nil { - return err - } - - var d Data - d.Status = data.Status - d.Number = data.Number - d.SenderOrderID = data.SenderOrderId - d.RecipientOrderID = data.RecipientOrderId - d.RequisitionID = data.RequisitionId - d.RequesterEmail = data.RequesterEmail - d.RequesterName = data.RequesterName - d.ShipToCompanyName = data.ShipToCompanyName - d.ShipToContactPersonName = data.ShipToContactPersonName - d.ShipToStreet1 = data.ShipToStreet1 - d.ShipToStreet2 = data.ShipToStreet2 - d.ShipToCity = data.ShipToCity - d.ShipToZipcode = data.ShipToZipcode - d.ShipToState = data.ShipToState - d.ShipToCountry = data.ShipToCountry - d.PaymentTerms = data.PaymentTerms - d.Currency = data.Currency - d.Comment = data.Comment - d.DateSent = tms[0] - d.DateUpdated = tms[1] - d.DateCreated = tms[2] - d.DateConfirmed = tms[3] - d.Attachments = documents.FromProtocolAttachments(data.Attachments) - d.PaymentDetails = pdetails - d.TotalAmount = decs[0] - d.Recipient = dids[0] - d.Sender = dids[1] - d.LineItems = li - p.Data = d - return nil -} - -// PackCoreDocument packs the PurchaseOrder into a Core Document -func (p *PurchaseOrder) PackCoreDocument() (cd coredocumentpb.CoreDocument, err error) { - poData, err := p.createP2PProtobuf() - if err != nil { - return cd, err - } - - data, err := proto.Marshal(poData) - if err != nil { - return cd, errors.New("failed to marshal po data: %v", err) - } - - embedData := &any.Any{ - TypeUrl: p.DocumentType(), - Value: data, - } - - return p.CoreDocument.PackCoreDocument(embedData), nil -} - -// UnpackCoreDocument unpacks the core document into PurchaseOrder -func (p *PurchaseOrder) UnpackCoreDocument(cd coredocumentpb.CoreDocument) error { - if cd.EmbeddedData == nil || - cd.EmbeddedData.TypeUrl != p.DocumentType() { - return errors.New("trying to convert document with incorrect schema") - } - - poData := new(purchaseorderpb.PurchaseOrderData) - err := proto.Unmarshal(cd.EmbeddedData.Value, poData) - if err != nil { - return err - } - - err = p.loadFromP2PProtobuf(poData) - if err != nil { - return err - } - - p.CoreDocument, err = documents.NewCoreDocumentFromProtobuf(cd) - return err - -} - -// JSON marshals PurchaseOrder into a json bytes -func (p *PurchaseOrder) JSON() ([]byte, error) { - return p.CoreDocument.MarshalJSON(p) -} - -// FromJSON unmarshals the json bytes into PurchaseOrder -func (p *PurchaseOrder) FromJSON(jsonData []byte) error { - if p.CoreDocument == nil { - p.CoreDocument = new(documents.CoreDocument) - } - - return p.CoreDocument.UnmarshalJSON(jsonData, p) -} - -// Type gives the PurchaseOrder type -func (p *PurchaseOrder) Type() reflect.Type { - return reflect.TypeOf(p) -} - -// CalculateDataRoot calculates the data root and sets the root to core document -func (p *PurchaseOrder) CalculateDataRoot() ([]byte, error) { - t, err := p.getDocumentDataTree() - if err != nil { - return nil, errors.New("failed to get data tree: %v", err) - } - - return t.RootHash(), nil -} - -// getDocumentDataTree creates precise-proofs data tree for the model -func (p *PurchaseOrder) getDocumentDataTree() (tree *proofs.DocumentTree, err error) { - poProto, err := p.createP2PProtobuf() - if err != nil { - return nil, err - } - t := p.CoreDocument.DefaultTreeWithPrefix(prefix, compactPrefix()) - err = t.AddLeavesFromDocument(poProto) - if err != nil { - return nil, errors.New("getDocumentDataTree error %v", err) - } - err = t.Generate() - if err != nil { - return nil, errors.New("getDocumentDataTree error %v", err) - } - - return t, nil -} - -// CreateProofs generates proofs for given fields. -func (p *PurchaseOrder) CreateProofs(fields []string) (proofs []*proofspb.Proof, err error) { - tree, err := p.getDocumentDataTree() - if err != nil { - return nil, errors.New("createProofs error %v", err) - } - - return p.CoreDocument.CreateProofs(p.DocumentType(), tree, fields) -} - -// DocumentType returns the po document type. -func (*PurchaseOrder) DocumentType() string { - return documenttypes.PurchaseOrderDataTypeUrl -} - -// PrepareNewVersion prepares new version from the old invoice. -func (p *PurchaseOrder) PrepareNewVersion(old documents.Model, data *clientpurchaseorderpb.PurchaseOrderData, collaborators documents.CollaboratorsAccess, attrs map[documents.AttrKey]documents.Attribute) error { - err := p.initPurchaseOrderFromData(data) - if err != nil { - return err - } - - oldCD := old.(*PurchaseOrder).CoreDocument - p.CoreDocument, err = oldCD.PrepareNewVersion(compactPrefix(), collaborators, attrs) - if err != nil { - return err - } - - return nil -} - -// AddNFT adds NFT to the Purchase Order. -func (p *PurchaseOrder) AddNFT(grantReadAccess bool, registry common.Address, tokenID []byte) error { - cd, err := p.CoreDocument.AddNFT(grantReadAccess, registry, tokenID) - if err != nil { - return err - } - - p.CoreDocument = cd - return nil -} - -// CalculateSigningRoot returns the signing root of the document. -// Calculates it if not generated yet. -func (p *PurchaseOrder) CalculateSigningRoot() ([]byte, error) { - dr, err := p.CalculateDataRoot() - if err != nil { - return dr, err - } - return p.CoreDocument.CalculateSigningRoot(p.DocumentType(), dr) -} - -// CalculateDocumentRoot calculates the document root -func (p *PurchaseOrder) CalculateDocumentRoot() ([]byte, error) { - dr, err := p.CalculateDataRoot() - if err != nil { - return dr, err - } - return p.CoreDocument.CalculateDocumentRoot(p.DocumentType(), dr) -} - -// DocumentRootTree creates and returns the document root tree -func (p *PurchaseOrder) DocumentRootTree() (tree *proofs.DocumentTree, err error) { - dr, err := p.CalculateDataRoot() - if err != nil { - return nil, err - } - return p.CoreDocument.DocumentRootTree(p.DocumentType(), dr) -} - -// CreateNFTProofs creates proofs specific to NFT minting. -func (p *PurchaseOrder) CreateNFTProofs( - account identity.DID, - registry common.Address, - tokenID []byte, - nftUniqueProof, readAccessProof bool) (proofs []*proofspb.Proof, err error) { - - tree, err := p.getDocumentDataTree() - if err != nil { - return nil, err - } - - return p.CoreDocument.CreateNFTProofs( - p.DocumentType(), - tree, - account, registry, tokenID, nftUniqueProof, readAccessProof) -} - -// CollaboratorCanUpdate checks if the account can update the document. -func (p *PurchaseOrder) CollaboratorCanUpdate(updated documents.Model, collaborator identity.DID) error { - newPo, ok := updated.(*PurchaseOrder) - if !ok { - return errors.NewTypedError(documents.ErrDocumentInvalidType, errors.New("expecting a purchase order but got %T", updated)) - } - - // check the core document changes - err := p.CoreDocument.CollaboratorCanUpdate(newPo.CoreDocument, collaborator, p.DocumentType()) - if err != nil { - return err - } - - // check purchase order specific changes - oldTree, err := p.getDocumentDataTree() - if err != nil { - return err - } - - newTree, err := newPo.getDocumentDataTree() - if err != nil { - return err - } - - rules := p.CoreDocument.TransitionRulesFor(collaborator) - cf := documents.GetChangedFields(oldTree, newTree) - return documents.ValidateTransitions(rules, cf) -} - -// AddAttributes adds attributes to the PurchaseOrder model. -func (p *PurchaseOrder) AddAttributes(ca documents.CollaboratorsAccess, prepareNewVersion bool, attrs ...documents.Attribute) error { - ncd, err := p.CoreDocument.AddAttributes(ca, prepareNewVersion, compactPrefix(), attrs...) - if err != nil { - return errors.NewTypedError(documents.ErrCDAttribute, err) - } - - p.CoreDocument = ncd - return nil -} - -// DeleteAttribute deletes the attribute from the model. -func (p *PurchaseOrder) DeleteAttribute(key documents.AttrKey, prepareNewVersion bool) error { - ncd, err := p.CoreDocument.DeleteAttribute(key, prepareNewVersion, compactPrefix()) - if err != nil { - return errors.NewTypedError(documents.ErrCDAttribute, err) - } - - p.CoreDocument = ncd - return nil -} - -// GetData returns purchase order data -func (p *PurchaseOrder) GetData() interface{} { - return p.Data -} - -// loadData unmarshals json blob to Data. -func (p *PurchaseOrder) loadData(data []byte) error { - var d Data - err := json.Unmarshal(data, &d) - if err != nil { - return err - } - - p.Data = d - return nil -} - -// unpackFromCreatePayload unpacks the invoice data from the Payload. -func (p *PurchaseOrder) unpackFromCreatePayload(did identity.DID, payload documents.CreatePayload) error { - if err := p.loadData(payload.Data); err != nil { - return errors.NewTypedError(ErrPOInvalidData, err) - } - - payload.Collaborators.ReadWriteCollaborators = append(payload.Collaborators.ReadWriteCollaborators, did) - cd, err := documents.NewCoreDocument(compactPrefix(), payload.Collaborators, payload.Attributes) - if err != nil { - return errors.NewTypedError(documents.ErrCDCreate, err) - } - - p.CoreDocument = cd - return nil -} - -// unpackFromUpdatePayload unpacks the update payload and prepares a new version. -func (p *PurchaseOrder) unpackFromUpdatePayload(old *PurchaseOrder, payload documents.UpdatePayload) error { - if err := p.loadData(payload.Data); err != nil { - return errors.NewTypedError(ErrPOInvalidData, err) - } - - ncd, err := old.CoreDocument.PrepareNewVersion(compactPrefix(), payload.Collaborators, payload.Attributes) - if err != nil { - return err - } - - p.CoreDocument = ncd - return nil -} - -// Scheme returns the purchase order scheme. -func (p *PurchaseOrder) Scheme() string { - return scheme -} diff --git a/documents/purchaseorder/model_test.go b/documents/purchaseorder/model_test.go deleted file mode 100644 index 25b19946c..000000000 --- a/documents/purchaseorder/model_test.go +++ /dev/null @@ -1,627 +0,0 @@ -// +build unit - -package purchaseorder - -import ( - "encoding/json" - "fmt" - "os" - "testing" - "time" - - "github.com/centrifuge/centrifuge-protobufs/documenttypes" - "github.com/centrifuge/centrifuge-protobufs/gen/go/coredocument" - "github.com/centrifuge/go-centrifuge/anchors" - "github.com/centrifuge/go-centrifuge/bootstrap" - "github.com/centrifuge/go-centrifuge/bootstrap/bootstrappers/testlogging" - "github.com/centrifuge/go-centrifuge/config" - "github.com/centrifuge/go-centrifuge/config/configstore" - "github.com/centrifuge/go-centrifuge/contextutil" - "github.com/centrifuge/go-centrifuge/documents" - "github.com/centrifuge/go-centrifuge/errors" - "github.com/centrifuge/go-centrifuge/ethereum" - "github.com/centrifuge/go-centrifuge/identity" - "github.com/centrifuge/go-centrifuge/identity/ideth" - "github.com/centrifuge/go-centrifuge/jobs" - "github.com/centrifuge/go-centrifuge/p2p" - clientpurchaseorderpb "github.com/centrifuge/go-centrifuge/protobufs/gen/go/purchaseorder" - "github.com/centrifuge/go-centrifuge/queue" - "github.com/centrifuge/go-centrifuge/storage/leveldb" - "github.com/centrifuge/go-centrifuge/testingutils/config" - "github.com/centrifuge/go-centrifuge/testingutils/documents" - "github.com/centrifuge/go-centrifuge/testingutils/identity" - "github.com/centrifuge/go-centrifuge/testingutils/testingjobs" - "github.com/centrifuge/go-centrifuge/utils" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/golang/protobuf/ptypes/any" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - -var ctx = map[string]interface{}{} -var cfg config.Configuration -var configService config.Service -var defaultDID = testingidentity.GenerateRandomDID() - -func TestMain(m *testing.M) { - ethClient := ðereum.MockEthClient{} - ethClient.On("GetEthClient").Return(nil) - ctx[ethereum.BootstrappedEthereumClient] = ethClient - jobManager := &testingjobs.MockJobManager{} - ctx[jobs.BootstrappedService] = jobManager - done := make(chan bool) - jobManager.On("ExecuteWithinJob", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(jobs.NilJobID(), done, nil) - ctx[bootstrap.BootstrappedInvoiceUnpaid] = new(testingdocuments.MockRegistry) - ibootstrappers := []bootstrap.TestBootstrapper{ - &testlogging.TestLoggingBootstrapper{}, - &config.Bootstrapper{}, - &leveldb.Bootstrapper{}, - &queue.Bootstrapper{}, - &ideth.Bootstrapper{}, - &configstore.Bootstrapper{}, - anchors.Bootstrapper{}, - documents.Bootstrapper{}, - p2p.Bootstrapper{}, - documents.PostBootstrapper{}, - &Bootstrapper{}, - &queue.Starter{}, - } - bootstrap.RunTestBootstrappers(ibootstrappers, ctx) - cfg = ctx[bootstrap.BootstrappedConfig].(config.Configuration) - cfg.Set("identityId", did.String()) - configService = ctx[config.BootstrappedConfigStorage].(config.Service) - result := m.Run() - bootstrap.RunTestTeardown(ibootstrappers) - os.Exit(result) -} - -func TestPurchaseOrder_PackCoreDocument(t *testing.T) { - ctx := testingconfig.CreateAccountContext(t, cfg) - did, err := contextutil.AccountDID(ctx) - assert.NoError(t, err) - - po := new(PurchaseOrder) - assert.NoError(t, po.InitPurchaseOrderInput(testingdocuments.CreatePOPayload(), did)) - - cd, err := po.PackCoreDocument() - assert.NoError(t, err) - assert.NotNil(t, cd.EmbeddedData) -} - -func TestPurchaseOrder_JSON(t *testing.T) { - po := new(PurchaseOrder) - ctx := testingconfig.CreateAccountContext(t, cfg) - did, err := contextutil.AccountDID(ctx) - assert.NoError(t, err) - assert.NoError(t, po.InitPurchaseOrderInput(testingdocuments.CreatePOPayload(), did)) - - cd, err := po.PackCoreDocument() - assert.NoError(t, err) - jsonBytes, err := po.JSON() - assert.NoError(t, err, "marshal to json didn't work correctly") - assert.True(t, json.Valid(jsonBytes), "json format not correct") - - po = new(PurchaseOrder) - err = po.FromJSON(jsonBytes) - assert.NoError(t, err, "unmarshal JSON didn't work correctly") - - ncd, err := po.PackCoreDocument() - assert.NoError(t, err, "JSON unmarshal damaged invoice variables") - assert.Equal(t, cd, ncd) -} - -func TestPO_UnpackCoreDocument(t *testing.T) { - var model = new(PurchaseOrder) - var err error - - // embed data missing - err = model.UnpackCoreDocument(coredocumentpb.CoreDocument{}) - assert.Error(t, err) - - // embed data type is wrong - err = model.UnpackCoreDocument(coredocumentpb.CoreDocument{EmbeddedData: new(any.Any)}) - assert.Error(t, err, "unpack must fail due to missing embed data") - - // embed data is wrong - err = model.UnpackCoreDocument(coredocumentpb.CoreDocument{ - EmbeddedData: &any.Any{ - Value: utils.RandomSlice(32), - TypeUrl: documenttypes.PurchaseOrderDataTypeUrl, - }, - }) - assert.Error(t, err) - - // successful - po, cd := createCDWithEmbeddedPO(t) - err = model.UnpackCoreDocument(cd) - assert.NoError(t, err) - data, err := model.getClientData() - assert.NoError(t, err) - data1, err := po.(*PurchaseOrder).getClientData() - assert.NoError(t, err) - assert.Equal(t, data, data1) - assert.Equal(t, model.ID(), po.ID()) - assert.Equal(t, model.CurrentVersion(), po.CurrentVersion()) - assert.Equal(t, model.PreviousVersion(), po.PreviousVersion()) -} - -func TestPOModel_getClientData(t *testing.T) { - poData := testingdocuments.CreatePOData() - poModel := new(PurchaseOrder) - poModel.CoreDocument = &documents.CoreDocument{} - err := poModel.loadFromP2PProtobuf(&poData) - assert.NoError(t, err) - - data, err := poModel.getClientData() - assert.NoError(t, err) - assert.NotNil(t, data, "purchase order data should not be nil") - assert.Equal(t, data.TotalAmount, data.TotalAmount, "gross amount must match") - assert.Equal(t, data.Recipient, poModel.Data.Recipient.String(), "recipient should match") -} - -func TestPOOrderModel_InitPOInput(t *testing.T) { - ctx := testingconfig.CreateAccountContext(t, cfg) - did, err := contextutil.AccountDID(ctx) - assert.NoError(t, err) - - // fail recipient - data := &clientpurchaseorderpb.PurchaseOrderData{ - Recipient: "some recipient", - } - poModel := new(PurchaseOrder) - err = poModel.InitPurchaseOrderInput(&clientpurchaseorderpb.PurchaseOrderCreatePayload{Data: data}, did) - assert.Error(t, err, "must return err") - assert.Contains(t, err.Error(), "malformed address provided") - assert.Nil(t, poModel.Data.Recipient) - - data.Recipient = "0xed03fa80291ff5ddc284de6b51e716b130b05e20" - err = poModel.InitPurchaseOrderInput(&clientpurchaseorderpb.PurchaseOrderCreatePayload{Data: data}, did) - assert.Nil(t, err) - assert.NotNil(t, poModel.Data.Recipient) - - collabs := []string{"0x010102040506", "some id"} - err = poModel.InitPurchaseOrderInput(&clientpurchaseorderpb.PurchaseOrderCreatePayload{Data: data, WriteAccess: collabs}, did) - assert.Error(t, err) - assert.Contains(t, err.Error(), "malformed address provided") - - collab1, err := identity.NewDIDFromString("0xBAEb33a61f05e6F269f1c4b4CFF91A901B54DaF7") - assert.NoError(t, err) - collab2, err := identity.NewDIDFromString("0xBAEb33a61f05e6F269f1c4b4CFF91A901B54DaF3") - assert.NoError(t, err) - collabs = []string{collab1.String(), collab2.String()} - err = poModel.InitPurchaseOrderInput(&clientpurchaseorderpb.PurchaseOrderCreatePayload{Data: data, WriteAccess: collabs}, did) - assert.Nil(t, err, "must be nil") - - did, err = identity.NewDIDFromString("0xed03fa80291ff5ddc284de6b51e716b130b05e20") - assert.NoError(t, err) - assert.Equal(t, poModel.Data.Recipient[:], did[:]) -} - -func TestPOModel_calculateDataRoot(t *testing.T) { - ctx := testingconfig.CreateAccountContext(t, cfg) - did, err := contextutil.AccountDID(ctx) - assert.NoError(t, err) - poModel := new(PurchaseOrder) - err = poModel.InitPurchaseOrderInput(testingdocuments.CreatePOPayload(), did) - assert.Nil(t, err, "Init must pass") - - dr, err := poModel.CalculateDataRoot() - assert.Nil(t, err, "calculate must pass") - assert.False(t, utils.IsEmptyByteSlice(dr)) -} - -func TestPOModel_CreateProofs(t *testing.T) { - po := createPurchaseOrder(t) - assert.NotNil(t, po) - rk := po.CoreDocument.GetTestCoreDocWithReset().Roles[0].RoleKey - pf := fmt.Sprintf(documents.CDTreePrefix+".roles[%s].collaborators[0]", hexutil.Encode(rk)) - proof, err := po.CreateProofs([]string{"po.number", pf, documents.CDTreePrefix + ".document_type", "po.line_items[0].status"}) - assert.Nil(t, err) - assert.NotNil(t, proof) - tree, err := po.DocumentRootTree() - assert.NoError(t, err) - - // Validate po_number - valid, err := tree.ValidateProof(proof[0]) - assert.Nil(t, err) - assert.True(t, valid) - - // Validate roles collaborators - valid, err = tree.ValidateProof(proof[1]) - assert.Nil(t, err) - assert.True(t, valid) - - // Validate []byte value - acc, err := identity.NewDIDFromBytes(proof[1].Value) - assert.NoError(t, err) - assert.True(t, po.AccountCanRead(acc)) - - // Validate document_type - valid, err = tree.ValidateProof(proof[2]) - assert.Nil(t, err) - assert.True(t, valid) - - // validate line items - valid, err = tree.ValidateProof(proof[3]) - assert.Nil(t, err) - assert.True(t, valid) -} - -func TestPOModel_createProofsFieldDoesNotExist(t *testing.T) { - poModel := createPurchaseOrder(t) - _, err := poModel.CreateProofs([]string{"nonexisting"}) - assert.NotNil(t, err) -} - -func TestPOModel_getDocumentDataTree(t *testing.T) { - na := new(documents.Decimal) - assert.NoError(t, na.SetString("2")) - poModel := createPurchaseOrder(t) - poModel.Data.Number = "123" - poModel.Data.TotalAmount = na - tree, err := poModel.getDocumentDataTree() - assert.Nil(t, err, "tree should be generated without error") - _, leaf := tree.GetLeafByProperty("po.number") - assert.NotNil(t, leaf) - assert.Equal(t, "po.number", leaf.Property.ReadableName()) - assert.Equal(t, []byte(poModel.Data.Number), leaf.Value) -} - -func createPurchaseOrder(t *testing.T) *PurchaseOrder { - po := new(PurchaseOrder) - payload := testingdocuments.CreatePOPayload() - payload.Data.LineItems = []*clientpurchaseorderpb.LineItem{ - { - Status: "pending", - AmountTotal: "1.1", - Activities: []*clientpurchaseorderpb.LineItemActivity{ - { - ItemNumber: "12345", - Status: "pending", - Amount: "1.1", - }, - }, - TaxItems: []*clientpurchaseorderpb.TaxItem{ - { - ItemNumber: "12345", - TaxAmount: "1.1", - }, - }, - }, - } - err := po.InitPurchaseOrderInput(payload, defaultDID) - assert.NoError(t, err) - po.GetTestCoreDocWithReset() - _, err = po.CalculateDataRoot() - assert.NoError(t, err) - _, err = po.CalculateSigningRoot() - assert.NoError(t, err) - _, err = po.CalculateDocumentRoot() - assert.NoError(t, err) - return po -} - -func TestPurchaseOrder_CollaboratorCanUpdate(t *testing.T) { - po := createPurchaseOrder(t) - id1 := defaultDID - id2 := testingidentity.GenerateRandomDID() - id3 := testingidentity.GenerateRandomDID() - - // wrong type - err := po.CollaboratorCanUpdate(new(mockModel), id1) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrDocumentInvalidType, err)) - assert.NoError(t, testRepo().Create(id1[:], po.CurrentVersion(), po)) - - // update the document - model, err := testRepo().Get(id1[:], po.CurrentVersion()) - assert.NoError(t, err) - oldPO := model.(*PurchaseOrder) - data, err := oldPO.getClientData() - assert.NoError(t, err) - data.TotalAmount = "50" - err = po.PrepareNewVersion(po, data, documents.CollaboratorsAccess{ - ReadWriteCollaborators: []identity.DID{id3}, - }, oldPO.Attributes) - assert.NoError(t, err) - - // id1 should have permission - assert.NoError(t, oldPO.CollaboratorCanUpdate(po, id1)) - - // id2 should fail since it doesn't have the permission to update - assert.Error(t, oldPO.CollaboratorCanUpdate(po, id2)) - - // update the id3 rules to update only total amount - po.CoreDocument.Document.TransitionRules[3].MatchType = coredocumentpb.FieldMatchType_FIELD_MATCH_TYPE_EXACT - po.CoreDocument.Document.TransitionRules[3].Field = append(compactPrefix(), 0, 0, 0, 18) - assert.NoError(t, testRepo().Create(id1[:], po.CurrentVersion(), po)) - - // fetch the document - model, err = testRepo().Get(id1[:], po.CurrentVersion()) - assert.NoError(t, err) - oldPO = model.(*PurchaseOrder) - data, err = oldPO.getClientData() - assert.NoError(t, err) - data.TotalAmount = "55" - data.Currency = "INR" - err = po.PrepareNewVersion(po, data, documents.CollaboratorsAccess{}, oldPO.Attributes) - assert.NoError(t, err) - - // id1 should have permission - assert.NoError(t, oldPO.CollaboratorCanUpdate(po, id1)) - - // id2 should fail since it doesn't have the permission to update - assert.Error(t, oldPO.CollaboratorCanUpdate(po, id2)) - - // id3 should fail with just one error since changing Currency is not allowed - err = oldPO.CollaboratorCanUpdate(po, id3) - assert.Error(t, err) - assert.Equal(t, 1, errors.Len(err)) - assert.Contains(t, err.Error(), "po.currency") -} - -func TestPurchaseOrder_AddAttributes(t *testing.T) { - po, _ := createCDWithEmbeddedPO(t) - label := "some key" - value := "some value" - attr, err := documents.NewAttribute(label, documents.AttrString, value) - assert.NoError(t, err) - - // success - err = po.AddAttributes(documents.CollaboratorsAccess{}, true, attr) - assert.NoError(t, err) - assert.True(t, po.AttributeExists(attr.Key)) - gattr, err := po.GetAttribute(attr.Key) - assert.NoError(t, err) - assert.Equal(t, attr, gattr) - - // fail - attr.Value.Type = documents.AttributeType("some attr") - err = po.AddAttributes(documents.CollaboratorsAccess{}, true, attr) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrCDAttribute, err)) -} - -func TestPurchaseOrder_DeleteAttribute(t *testing.T) { - po, _ := createCDWithEmbeddedPO(t) - label := "some key" - value := "some value" - attr, err := documents.NewAttribute(label, documents.AttrString, value) - assert.NoError(t, err) - - // failed - err = po.DeleteAttribute(attr.Key, true) - assert.Error(t, err) - - // success - assert.NoError(t, po.AddAttributes(documents.CollaboratorsAccess{}, true, attr)) - assert.True(t, po.AttributeExists(attr.Key)) - assert.NoError(t, po.DeleteAttribute(attr.Key, true)) - assert.False(t, po.AttributeExists(attr.Key)) -} - -func TestPurchaseOrder_GetData(t *testing.T) { - po := createPurchaseOrder(t) - data := po.GetData() - assert.Equal(t, po.Data, data) -} - -func marshallData(t *testing.T, m map[string]interface{}) []byte { - data, err := json.Marshal(m) - assert.NoError(t, err) - return data -} - -func emptyDecimalData(t *testing.T) []byte { - d := map[string]interface{}{ - "total_amount": "", - } - - return marshallData(t, d) -} - -func invalidDecimalData(t *testing.T) []byte { - d := map[string]interface{}{ - "total_amount": "10.10.", - } - - return marshallData(t, d) -} - -func emptyDIDData(t *testing.T) []byte { - d := map[string]interface{}{ - "recipient": "", - } - - return marshallData(t, d) -} - -func invalidDIDData(t *testing.T) []byte { - d := map[string]interface{}{ - "recipient": "1acdew123asdefres", - } - - return marshallData(t, d) -} - -func emptyTimeData(t *testing.T) []byte { - d := map[string]interface{}{ - "date_sent": "", - } - - return marshallData(t, d) -} - -func invalidTimeData(t *testing.T) []byte { - d := map[string]interface{}{ - "date_sent": "1920-12-10", - } - - return marshallData(t, d) -} - -func validData(t *testing.T) []byte { - d := map[string]interface{}{ - "number": "12345", - "status": "unpaid", - "total_amount": "12.345", - "recipient": "0xBAEb33a61f05e6F269f1c4b4CFF91A901B54DaF7", - "date_sent": "2019-05-24T14:48:44.308854Z", // rfc3339nano - "date_confirmed": "2019-05-24T14:48:44Z", // rfc3339 - "attachments": []map[string]interface{}{ - { - "name": "test", - "file_type": "pdf", - "size": 1000202, - "data": "0xBAEb33a61f05e6F269f1c4b4CFF91A901B54DaF7", - "checksum": "0xBAEb33a61f05e6F269f1c4b4CFF91A901B54DaF3", - }, - }, - } - - return marshallData(t, d) -} - -func validDataWithCurrency(t *testing.T) []byte { - d := map[string]interface{}{ - "number": "12345", - "status": "unpaid", - "total_amount": "12.345", - "recipient": "0xBAEb33a61f05e6F269f1c4b4CFF91A901B54DaF7", - "date_sent": "2019-05-24T14:48:44.308854Z", // rfc3339nano - "date_confirmed": "2019-05-24T14:48:44Z", // rfc3339 - "currency": "EUR", - "attachments": []map[string]interface{}{ - { - "name": "test", - "file_type": "pdf", - "size": 1000202, - "data": "0xBAEb33a61f05e6F269f1c4b4CFF91A901B54DaF7", - "checksum": "0xBAEb33a61f05e6F269f1c4b4CFF91A901B54DaF3", - }, - }, - } - - return marshallData(t, d) -} - -func checkPOPayloadDataError(t *testing.T, po *PurchaseOrder, payload documents.CreatePayload) { - err := po.loadData(payload.Data) - assert.Error(t, err) -} - -func TestPurchaseOrder_loadData(t *testing.T) { - po := new(PurchaseOrder) - payload := documents.CreatePayload{} - - // empty decimal data - payload.Data = emptyDecimalData(t) - checkPOPayloadDataError(t, po, payload) - - // invalid decimal data - payload.Data = invalidDecimalData(t) - checkPOPayloadDataError(t, po, payload) - - // empty did data - payload.Data = emptyDIDData(t) - checkPOPayloadDataError(t, po, payload) - - // invalid did data - payload.Data = invalidDIDData(t) - checkPOPayloadDataError(t, po, payload) - - // empty time data - payload.Data = emptyTimeData(t) - checkPOPayloadDataError(t, po, payload) - - // invalid time data - payload.Data = invalidTimeData(t) - checkPOPayloadDataError(t, po, payload) - - // valid data - payload.Data = validData(t) - err := po.loadData(payload.Data) - assert.NoError(t, err) - data := po.GetData().(Data) - assert.Equal(t, data.Number, "12345") - assert.Equal(t, data.Status, "unpaid") - assert.Equal(t, data.TotalAmount.String(), "12.345") - assert.Equal(t, data.Recipient.String(), "0xBAEb33a61f05e6F269f1c4b4CFF91A901B54DaF7") - assert.Equal(t, data.DateSent.UTC().Format(time.RFC3339Nano), "2019-05-24T14:48:44.308854Z") - assert.Equal(t, data.DateConfirmed.UTC().Format(time.RFC3339), "2019-05-24T14:48:44Z") - assert.Len(t, data.Attachments, 1) - assert.Equal(t, data.Attachments[0].Name, "test") - assert.Equal(t, data.Attachments[0].FileType, "pdf") - assert.Equal(t, data.Attachments[0].Size, 1000202) - assert.Equal(t, hexutil.Encode(data.Attachments[0].Checksum), "0xbaeb33a61f05e6f269f1c4b4cff91a901b54daf3") - assert.Equal(t, hexutil.Encode(data.Attachments[0].Data), "0xbaeb33a61f05e6f269f1c4b4cff91a901b54daf7") -} - -func TestPurchaseOrder_unpackFromCreatePayload(t *testing.T) { - payload := documents.CreatePayload{} - po := new(PurchaseOrder) - - // invalid data - payload.Data = invalidDecimalData(t) - err := po.unpackFromCreatePayload(did, payload) - assert.Error(t, err) - assert.True(t, errors.IsOfType(ErrPOInvalidData, err)) - - // invalid attributes - attr, err := documents.NewAttribute("test", documents.AttrString, "value") - assert.NoError(t, err) - val := attr.Value - val.Type = documents.AttributeType("some type") - attr.Value = val - payload.Attributes = map[documents.AttrKey]documents.Attribute{ - attr.Key: attr, - } - payload.Data = validData(t) - err = po.unpackFromCreatePayload(did, payload) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrCDCreate, err)) - - // valid - val.Type = documents.AttrString - attr.Value = val - payload.Attributes = map[documents.AttrKey]documents.Attribute{ - attr.Key: attr, - } - err = po.unpackFromCreatePayload(did, payload) - assert.NoError(t, err) -} - -func TestPurchaseOrder_unpackFromUpdatePayload(t *testing.T) { - payload := documents.UpdatePayload{} - old := createPurchaseOrder(t) - po := new(PurchaseOrder) - - // invalid data - payload.Data = invalidDecimalData(t) - err := po.unpackFromUpdatePayload(old, payload) - assert.Error(t, err) - assert.True(t, errors.IsOfType(ErrPOInvalidData, err)) - - // invalid attributes - attr, err := documents.NewAttribute("test", documents.AttrString, "value") - assert.NoError(t, err) - val := attr.Value - val.Type = documents.AttributeType("some type") - attr.Value = val - payload.Attributes = map[documents.AttrKey]documents.Attribute{ - attr.Key: attr, - } - payload.Data = validData(t) - err = po.unpackFromUpdatePayload(old, payload) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrCDNewVersion, err)) - - // valid - val.Type = documents.AttrString - attr.Value = val - payload.Attributes = map[documents.AttrKey]documents.Attribute{ - attr.Key: attr, - } - err = po.unpackFromUpdatePayload(old, payload) - assert.NoError(t, err) -} diff --git a/documents/purchaseorder/service.go b/documents/purchaseorder/service.go deleted file mode 100644 index b2ab8f60f..000000000 --- a/documents/purchaseorder/service.go +++ /dev/null @@ -1,311 +0,0 @@ -package purchaseorder - -import ( - "context" - - "github.com/centrifuge/centrifuge-protobufs/gen/go/coredocument" - "github.com/centrifuge/go-centrifuge/anchors" - "github.com/centrifuge/go-centrifuge/contextutil" - "github.com/centrifuge/go-centrifuge/documents" - "github.com/centrifuge/go-centrifuge/errors" - "github.com/centrifuge/go-centrifuge/jobs" - clientpopb "github.com/centrifuge/go-centrifuge/protobufs/gen/go/purchaseorder" - "github.com/centrifuge/go-centrifuge/queue" - "github.com/ethereum/go-ethereum/common/hexutil" -) - -// Service defines specific functions for purchase order -type Service interface { - documents.Service - - // DeriverFromPayload derives purchase order from clientPayload - DeriveFromCreatePayload(ctx context.Context, payload *clientpopb.PurchaseOrderCreatePayload) (documents.Model, error) - - // DeriveFromUpdatePayload derives purchase order from update payload - DeriveFromUpdatePayload(ctx context.Context, payload *clientpopb.PurchaseOrderUpdatePayload) (documents.Model, error) - - // DerivePurchaseOrderData returns the purchase order data as client data - DerivePurchaseOrderData(po documents.Model) (*clientpopb.PurchaseOrderData, error) - - // DerivePurchaseOrderResponse returns the purchase order in our standard client format - DerivePurchaseOrderResponse(po documents.Model) (*clientpopb.PurchaseOrderResponse, error) -} - -// service implements Service and handles all purchase order related persistence and validations -// service always returns errors of type `errors.Error` or `errors.TypedError` -type service struct { - documents.Service - repo documents.Repository - queueSrv queue.TaskQueuer - jobManager jobs.Manager - tokenRegFinder func() documents.TokenRegistry - anchorRepo anchors.AnchorRepository -} - -// DefaultService returns the default implementation of the service -func DefaultService( - srv documents.Service, - repo documents.Repository, - queueSrv queue.TaskQueuer, - jobManager jobs.Manager, - tokenRegFinder func() documents.TokenRegistry, - anchorRepo anchors.AnchorRepository, -) Service { - return service{ - repo: repo, - queueSrv: queueSrv, - jobManager: jobManager, - Service: srv, - tokenRegFinder: tokenRegFinder, - anchorRepo: anchorRepo, - } -} - -// DeriveFromCoreDocument takes a core document model and returns a purchase order -func (s service) DeriveFromCoreDocument(cd coredocumentpb.CoreDocument) (documents.Model, error) { - po := new(PurchaseOrder) - err := po.UnpackCoreDocument(cd) - if err != nil { - return nil, errors.NewTypedError(documents.ErrDocumentUnPackingCoreDocument, err) - } - - return po, nil -} - -// validateAndPersist validates the document, and persists to DB -func (s service) validateAndPersist(ctx context.Context, old, new documents.Model, validator documents.Validator) (documents.Model, error) { - selfDID, err := contextutil.AccountDID(ctx) - if err != nil { - return nil, errors.NewTypedError(documents.ErrDocumentConfigAccountID, err) - } - - po, ok := new.(*PurchaseOrder) - if !ok { - return nil, errors.NewTypedError(documents.ErrDocumentInvalidType, errors.New("unknown document type: %T", new)) - } - - // validate the invoice - err = validator.Validate(old, po) - if err != nil { - return nil, errors.NewTypedError(documents.ErrDocumentInvalid, err) - } - - // we use CurrentVersion as the id since that will be unique across multiple versions of the same document - err = s.repo.Create(selfDID[:], po.CurrentVersion(), po) - if err != nil { - return nil, errors.NewTypedError(documents.ErrDocumentPersistence, err) - } - - return po, nil -} - -// Create validates, persists, and anchors a purchase order -func (s service) Create(ctx context.Context, po documents.Model) (documents.Model, jobs.JobID, chan bool, error) { - selfDID, err := contextutil.AccountDID(ctx) - if err != nil { - return nil, jobs.NilJobID(), nil, errors.NewTypedError(documents.ErrDocumentConfigAccountID, err) - } - - po, err = s.validateAndPersist(ctx, nil, po, CreateValidator()) - if err != nil { - return nil, jobs.NilJobID(), nil, err - } - - jobID := contextutil.Job(ctx) - jobID, done, err := documents.CreateAnchorJob(ctx, s.jobManager, s.queueSrv, selfDID, jobID, po.CurrentVersion()) - if err != nil { - return nil, jobs.NilJobID(), nil, nil - } - return po, jobID, done, nil -} - -// Update validates, persists, and anchors a new version of purchase order -func (s service) Update(ctx context.Context, new documents.Model) (documents.Model, jobs.JobID, chan bool, error) { - selfDID, err := contextutil.AccountDID(ctx) - if err != nil { - return nil, jobs.NilJobID(), nil, errors.NewTypedError(documents.ErrDocumentConfigAccountID, err) - } - - old, err := s.GetCurrentVersion(ctx, new.ID()) - if err != nil { - return nil, jobs.NilJobID(), nil, errors.NewTypedError(documents.ErrDocumentNotFound, err) - } - - new, err = s.validateAndPersist(ctx, old, new, UpdateValidator(s.anchorRepo)) - if err != nil { - return nil, jobs.NilJobID(), nil, err - } - - jobID := contextutil.Job(ctx) - jobID, done, err := documents.CreateAnchorJob(ctx, s.jobManager, s.queueSrv, selfDID, jobID, new.CurrentVersion()) - if err != nil { - return nil, jobs.NilJobID(), nil, err - } - return new, jobID, done, nil -} - -// DeriveFromCreatePayload derives purchase order from create payload -func (s service) DeriveFromCreatePayload(ctx context.Context, payload *clientpopb.PurchaseOrderCreatePayload) (documents.Model, error) { - if payload == nil || payload.Data == nil { - return nil, documents.ErrDocumentNil - } - - self, err := contextutil.AccountDID(ctx) - if err != nil { - return nil, documents.ErrDocumentConfigAccountID - } - - po := new(PurchaseOrder) - err = po.InitPurchaseOrderInput(payload, self) - if err != nil { - return nil, errors.NewTypedError(documents.ErrDocumentInvalid, err) - } - - return po, nil -} - -// DeriveFromUpdatePayload derives purchase order from update payload -func (s service) DeriveFromUpdatePayload(ctx context.Context, payload *clientpopb.PurchaseOrderUpdatePayload) (documents.Model, error) { - if payload == nil || payload.Data == nil { - return nil, documents.ErrDocumentNil - } - - // get latest old version of the document - id, err := hexutil.Decode(payload.DocumentId) - if err != nil { - return nil, errors.NewTypedError(documents.ErrDocumentIdentifier, errors.New("failed to decode identifier: %v", err)) - } - - old, err := s.GetCurrentVersion(ctx, id) - if err != nil { - return nil, err - } - - cs, err := documents.FromClientCollaboratorAccess(payload.ReadAccess, payload.WriteAccess) - if err != nil { - return nil, err - } - - attrs, err := documents.FromClientAttributes(payload.Attributes) - if err != nil { - return nil, err - } - - // load purchase order data - po := new(PurchaseOrder) - err = po.PrepareNewVersion(old, payload.Data, cs, attrs) - if err != nil { - return nil, errors.NewTypedError(documents.ErrDocumentInvalid, errors.New("failed to load purchase order from data: %v", err)) - } - - return po, nil -} - -// DerivePurchaseOrderData returns po data from the model -func (s service) DerivePurchaseOrderData(doc documents.Model) (*clientpopb.PurchaseOrderData, error) { - po, ok := doc.(*PurchaseOrder) - if !ok { - return nil, documents.ErrDocumentInvalidType - } - - return po.getClientData() -} - -// DerivePurchaseOrderResponse returns po response from the model -func (s service) DerivePurchaseOrderResponse(doc documents.Model) (*clientpopb.PurchaseOrderResponse, error) { - data, err := s.DerivePurchaseOrderData(doc) - if err != nil { - return nil, err - } - - h, err := documents.DeriveResponseHeader(s.tokenRegFinder(), doc) - if err != nil { - return nil, err - } - - attrs, err := documents.ToClientAttributes(doc.GetAttributes()) - if err != nil { - return nil, err - } - - return &clientpopb.PurchaseOrderResponse{ - Header: h, - Data: data, - Attributes: attrs, - }, nil -} - -// CreateModel creates purchase order from the payload, validates, persists, and returns the purchase order. -func (s service) CreateModel(ctx context.Context, payload documents.CreatePayload) (documents.Model, jobs.JobID, error) { - if payload.Data == nil { - return nil, jobs.NilJobID(), documents.ErrDocumentNil - } - - did, err := contextutil.AccountDID(ctx) - if err != nil { - return nil, jobs.NilJobID(), documents.ErrDocumentConfigAccountID - } - - po := new(PurchaseOrder) - if err := po.unpackFromCreatePayload(did, payload); err != nil { - return nil, jobs.NilJobID(), errors.NewTypedError(documents.ErrDocumentInvalid, err) - } - - // validate po - err = CreateValidator().Validate(nil, po) - if err != nil { - return nil, jobs.NilJobID(), errors.NewTypedError(documents.ErrDocumentInvalid, err) - } - - // we use CurrentVersion as the id since that will be unique across multiple versions of the same document - err = s.repo.Create(did[:], po.CurrentVersion(), po) - if err != nil { - return nil, jobs.NilJobID(), errors.NewTypedError(documents.ErrDocumentPersistence, err) - } - - jobID := contextutil.Job(ctx) - jobID, _, err = documents.CreateAnchorJob(ctx, s.jobManager, s.queueSrv, did, jobID, po.CurrentVersion()) - return po, jobID, err -} - -// UpdateModel updates the migrates the current purchase order to next version with data from the update payload -func (s service) UpdateModel(ctx context.Context, payload documents.UpdatePayload) (documents.Model, jobs.JobID, error) { - if payload.Data == nil { - return nil, jobs.NilJobID(), documents.ErrDocumentNil - } - - did, err := contextutil.AccountDID(ctx) - if err != nil { - return nil, jobs.NilJobID(), documents.ErrDocumentConfigAccountID - } - - old, err := s.GetCurrentVersion(ctx, payload.DocumentID) - if err != nil { - return nil, jobs.NilJobID(), err - } - - oldPO, ok := old.(*PurchaseOrder) - if !ok { - return nil, jobs.NilJobID(), errors.NewTypedError(documents.ErrDocumentInvalidType, errors.New("%v is not a purchase order", hexutil.Encode(payload.DocumentID))) - } - - po := new(PurchaseOrder) - err = po.unpackFromUpdatePayload(oldPO, payload) - if err != nil { - return nil, jobs.NilJobID(), errors.NewTypedError(documents.ErrDocumentInvalid, err) - } - - err = UpdateValidator(s.anchorRepo).Validate(old, po) - if err != nil { - return nil, jobs.NilJobID(), errors.NewTypedError(documents.ErrDocumentInvalid, err) - } - - err = s.repo.Create(did[:], po.CurrentVersion(), po) - if err != nil { - return nil, jobs.NilJobID(), errors.NewTypedError(documents.ErrDocumentPersistence, err) - } - - jobID := contextutil.Job(ctx) - jobID, _, err = documents.CreateAnchorJob(ctx, s.jobManager, s.queueSrv, did, jobID, po.CurrentVersion()) - return po, jobID, err -} diff --git a/documents/purchaseorder/service_test.go b/documents/purchaseorder/service_test.go deleted file mode 100644 index 117ec4c5a..000000000 --- a/documents/purchaseorder/service_test.go +++ /dev/null @@ -1,480 +0,0 @@ -// +build unit - -package purchaseorder - -import ( - "context" - "testing" - - "github.com/centrifuge/centrifuge-protobufs/gen/go/coredocument" - "github.com/centrifuge/go-centrifuge/documents" - "github.com/centrifuge/go-centrifuge/errors" - "github.com/centrifuge/go-centrifuge/jobs" - clientpurchaseorderpb "github.com/centrifuge/go-centrifuge/protobufs/gen/go/purchaseorder" - "github.com/centrifuge/go-centrifuge/storage" - "github.com/centrifuge/go-centrifuge/storage/leveldb" - "github.com/centrifuge/go-centrifuge/testingutils" - "github.com/centrifuge/go-centrifuge/testingutils/anchors" - "github.com/centrifuge/go-centrifuge/testingutils/commons" - "github.com/centrifuge/go-centrifuge/testingutils/config" - "github.com/centrifuge/go-centrifuge/testingutils/documents" - "github.com/centrifuge/go-centrifuge/testingutils/identity" - "github.com/centrifuge/go-centrifuge/testingutils/testingjobs" - "github.com/centrifuge/go-centrifuge/utils" - "github.com/centrifuge/gocelery" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - -var ( - did = testingidentity.GenerateRandomDID() - accountID = did[:] -) - -func getServiceWithMockedLayers() (*testingcommons.MockIdentityService, Service) { - idService := &testingcommons.MockIdentityService{} - idService.On("IsSignedWithPurpose", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(true, nil).Once() - queueSrv := new(testingutils.MockQueue) - queueSrv.On("EnqueueJob", mock.Anything, mock.Anything).Return(&gocelery.AsyncResult{}, nil) - jobManager := ctx[jobs.BootstrappedService].(jobs.Manager) - repo := testRepo() - anchorRepo := &testinganchors.MockAnchorRepo{} - anchorRepo.On("GetAnchorData", mock.Anything).Return(nil, errors.New("missing")) - docSrv := documents.DefaultService(cfg, repo, anchorRepo, documents.NewServiceRegistry(), idService) - return idService, DefaultService(docSrv, repo, queueSrv, jobManager, func() documents.TokenRegistry { - return nil - }, anchorRepo) -} - -func TestService_Update(t *testing.T) { - _, poSrv := getServiceWithMockedLayers() - ctxh := testingconfig.CreateAccountContext(t, cfg) - - // missing last version - po, _ := createCDWithEmbeddedPO(t) - _, _, _, err := poSrv.Update(ctxh, po) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrDocumentNotFound, err)) - - assert.NoError(t, testRepo().Create(accountID, po.CurrentVersion(), po)) - // success - data, err := poSrv.DerivePurchaseOrderData(po) - assert.Nil(t, err) - data.TotalAmount = "100" - collab := testingidentity.GenerateRandomDID().String() - newPO, err := poSrv.DeriveFromUpdatePayload(ctxh, &clientpurchaseorderpb.PurchaseOrderUpdatePayload{ - DocumentId: hexutil.Encode(po.ID()), - WriteAccess: []string{collab}, - Data: data, - }) - assert.Nil(t, err) - newData, err := poSrv.DerivePurchaseOrderData(newPO) - assert.Nil(t, err) - assert.Equal(t, data, newData) - po, _, _, err = poSrv.Update(ctxh, newPO) - assert.Nil(t, err) - assert.NotNil(t, po) - assert.True(t, testRepo().Exists(accountID, po.ID())) - assert.True(t, testRepo().Exists(accountID, po.CurrentVersion())) - assert.True(t, testRepo().Exists(accountID, po.PreviousVersion())) - - newData, err = poSrv.DerivePurchaseOrderData(po) - assert.Nil(t, err) - assert.Equal(t, data, newData) -} - -func TestService_DeriveFromUpdatePayload(t *testing.T) { - _, poSrv := getServiceWithMockedLayers() - ctxh := testingconfig.CreateAccountContext(t, cfg) - - // nil payload - doc, err := poSrv.DeriveFromUpdatePayload(ctxh, nil) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrDocumentNil, err)) - assert.Nil(t, doc) - - // nil payload data - doc, err = poSrv.DeriveFromUpdatePayload(ctxh, &clientpurchaseorderpb.PurchaseOrderUpdatePayload{}) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrDocumentNil, err)) - assert.Nil(t, doc) - - // messed up identifier - contextHeader := testingconfig.CreateAccountContext(t, cfg) - payload := &clientpurchaseorderpb.PurchaseOrderUpdatePayload{DocumentId: "some identifier", Data: &clientpurchaseorderpb.PurchaseOrderData{}} - doc, err = poSrv.DeriveFromUpdatePayload(contextHeader, payload) - assert.Error(t, err) - assert.Contains(t, err.Error(), "failed to decode identifier") - assert.Nil(t, doc) - - // missing last version - id := utils.RandomSlice(32) - payload.DocumentId = hexutil.Encode(id) - doc, err = poSrv.DeriveFromUpdatePayload(contextHeader, payload) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrDocumentNotFound, err)) - assert.Nil(t, doc) - - // failed to load from data - old, _ := createCDWithEmbeddedPO(t) - err = testRepo().Create(accountID, old.CurrentVersion(), old) - assert.Nil(t, err) - payload.Data = &clientpurchaseorderpb.PurchaseOrderData{ - Recipient: "some recipient", - Currency: "EUR", - } - - payload.DocumentId = hexutil.Encode(old.ID()) - doc, err = poSrv.DeriveFromUpdatePayload(contextHeader, payload) - assert.Error(t, err) - assert.Contains(t, err.Error(), "failed to load purchase order from data") - assert.Nil(t, doc) - - // failed core document new version - payload.Data.Recipient = "0xEA939D5C0494b072c51565b191eE59B5D34fbf79" - payload.WriteAccess = []string{"some wrong ID"} - doc, err = poSrv.DeriveFromUpdatePayload(contextHeader, payload) - assert.Error(t, err) - assert.Nil(t, doc) - - // success - wantCollab := testingidentity.GenerateRandomDID() - payload.WriteAccess = []string{wantCollab.String()} - doc, err = poSrv.DeriveFromUpdatePayload(contextHeader, payload) - assert.Nil(t, err) - assert.NotNil(t, doc) - cs, err := doc.GetCollaborators() - assert.NoError(t, err) - assert.Len(t, cs.ReadWriteCollaborators, 3) - assert.Contains(t, cs.ReadWriteCollaborators, wantCollab) - assert.Equal(t, old.ID(), doc.ID()) - assert.Equal(t, payload.DocumentId, hexutil.Encode(doc.ID())) - assert.Equal(t, old.CurrentVersion(), doc.PreviousVersion()) - assert.Equal(t, old.NextVersion(), doc.CurrentVersion()) - assert.NotNil(t, doc.NextVersion()) - data, err := doc.(*PurchaseOrder).getClientData() - assert.NoError(t, err) - assert.Equal(t, payload.Data, data) -} - -func TestService_DeriveFromCreatePayload(t *testing.T) { - poSrv := service{} - ctxh := testingconfig.CreateAccountContext(t, cfg) - - // nil payload - m, err := poSrv.DeriveFromCreatePayload(ctxh, nil) - assert.Nil(t, m) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrDocumentNil, err)) - - // nil data payload - m, err = poSrv.DeriveFromCreatePayload(ctxh, &clientpurchaseorderpb.PurchaseOrderCreatePayload{}) - assert.Nil(t, m) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrDocumentNil, err)) - - // Init fails - payload := &clientpurchaseorderpb.PurchaseOrderCreatePayload{ - Data: &clientpurchaseorderpb.PurchaseOrderData{ - Recipient: "some recipient", - }, - } - - m, err = poSrv.DeriveFromCreatePayload(ctxh, payload) - assert.Nil(t, m) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrDocumentInvalid, err)) - - // success - payload.Data.Recipient = "0xEA939D5C0494b072c51565b191eE59B5D34fbf79" - m, err = poSrv.DeriveFromCreatePayload(ctxh, payload) - assert.Nil(t, err) - assert.NotNil(t, m) -} - -func TestService_DeriveFromCoreDocument(t *testing.T) { - poSrv := service{repo: testRepo()} - _, cd := createCDWithEmbeddedPO(t) - m, err := poSrv.DeriveFromCoreDocument(cd) - assert.Nil(t, err, "must return model") - assert.NotNil(t, m, "model must be non-nil") - po, ok := m.(*PurchaseOrder) - assert.True(t, ok, "must be true") - assert.Equal(t, po.Data.Recipient.String(), "0xEA939D5C0494b072c51565b191eE59B5D34fbf79") - assert.Equal(t, po.Data.TotalAmount.String(), "42") -} - -func TestService_Create(t *testing.T) { - ctxh := testingconfig.CreateAccountContext(t, cfg) - _, poSrv := getServiceWithMockedLayers() - - // calculate data root fails - m, _, _, err := poSrv.Create(ctxh, &testingdocuments.MockModel{}) - assert.Nil(t, m) - assert.Error(t, err) - assert.Contains(t, err.Error(), "unknown document type") - - // success - po, err := poSrv.DeriveFromCreatePayload(ctxh, testingdocuments.CreatePOPayload()) - assert.Nil(t, err) - m, _, _, err = poSrv.Create(ctxh, po) - assert.Nil(t, err) - assert.NotNil(t, m) - - assert.Nil(t, err) - assert.True(t, testRepo().Exists(accountID, po.ID())) - assert.True(t, testRepo().Exists(accountID, po.CurrentVersion())) -} - -func TestService_DerivePurchaseOrderData(t *testing.T) { - var m documents.Model - _, poSrv := getServiceWithMockedLayers() - - // unknown type - m = &testingdocuments.MockModel{} - d, err := poSrv.DerivePurchaseOrderData(m) - assert.Nil(t, d) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrDocumentInvalidType, err)) - - // success - payload := testingdocuments.CreatePOPayload() - m, err = poSrv.DeriveFromCreatePayload(testingconfig.CreateAccountContext(t, cfg), payload) - assert.Nil(t, err) - d, err = poSrv.DerivePurchaseOrderData(m) - assert.Nil(t, err) - assert.Equal(t, d.Currency, payload.Data.Currency) -} - -func TestService_DerivePurchaseOrderResponse(t *testing.T) { - poSrv := service{tokenRegFinder: func() documents.TokenRegistry { - return nil - }} - - // derive data failed - m := &testingdocuments.MockModel{} - r, err := poSrv.DerivePurchaseOrderResponse(m) - m.AssertExpectations(t) - assert.Nil(t, r) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrDocumentInvalidType, err)) - - // success - payload := testingdocuments.CreatePOPayload() - po, err := poSrv.DeriveFromCreatePayload(testingconfig.CreateAccountContext(t, cfg), payload) - assert.Nil(t, err) - r, err = poSrv.DerivePurchaseOrderResponse(po) - assert.Nil(t, err) - assert.Equal(t, payload.Data, r.Data) - assert.Contains(t, r.Header.WriteAccess, did.String()) -} - -func TestService_GetCurrentVersion(t *testing.T) { - _, poSrv := getServiceWithMockedLayers() - doc, _ := createCDWithEmbeddedPO(t) - ctxh := testingconfig.CreateAccountContext(t, cfg) - - err := testRepo().Create(accountID, doc.CurrentVersion(), doc) - assert.Nil(t, err) - - data, err := doc.(*PurchaseOrder).getClientData() - assert.NoError(t, err) - data.Currency = "INR" - doc2 := new(PurchaseOrder) - assert.NoError(t, doc2.PrepareNewVersion(doc, data, documents.CollaboratorsAccess{}, doc.(*PurchaseOrder).Attributes)) - assert.NoError(t, testRepo().Create(accountID, doc2.CurrentVersion(), doc2)) - - doc3, err := poSrv.GetCurrentVersion(ctxh, doc.ID()) - assert.Nil(t, err) - assert.Equal(t, doc2, doc3) -} - -func TestService_GetVersion(t *testing.T) { - _, poSrv := getServiceWithMockedLayers() - po, _ := createCDWithEmbeddedPO(t) - err := testRepo().Create(accountID, po.CurrentVersion(), po) - assert.Nil(t, err) - - ctxh := testingconfig.CreateAccountContext(t, cfg) - mod, err := poSrv.GetVersion(ctxh, po.ID(), po.CurrentVersion()) - assert.Nil(t, err) - - mod, err = poSrv.GetVersion(ctxh, mod.ID(), []byte{}) - assert.Error(t, err) -} - -func TestService_Exists(t *testing.T) { - _, poSrv := getServiceWithMockedLayers() - po, _ := createCDWithEmbeddedPO(t) - err := testRepo().Create(accountID, po.CurrentVersion(), po) - assert.Nil(t, err) - - ctxh := testingconfig.CreateAccountContext(t, cfg) - exists := poSrv.Exists(ctxh, po.CurrentVersion()) - assert.True(t, exists, "purchase order should exist") - - exists = poSrv.Exists(ctxh, utils.RandomSlice(32)) - assert.False(t, exists, "purchase order should not exist") -} - -func TestService_calculateDataRoot(t *testing.T) { - poSrv := service{repo: testRepo()} - ctxh := testingconfig.CreateAccountContext(t, cfg) - - // type mismatch - po, err := poSrv.validateAndPersist(ctxh, nil, &testingdocuments.MockModel{}, nil) - assert.Nil(t, po) - assert.Error(t, err) - assert.Contains(t, err.Error(), "unknown document type") - - // failed validator - po, err = poSrv.DeriveFromCreatePayload(ctxh, testingdocuments.CreatePOPayload()) - assert.Nil(t, err) - v := documents.ValidatorFunc(func(_, _ documents.Model) error { - return errors.New("validations fail") - }) - po, err = poSrv.validateAndPersist(ctxh, nil, po, v) - assert.Nil(t, po) - assert.Error(t, err) - assert.Contains(t, err.Error(), "validations fail") - - // create failed - po, err = poSrv.DeriveFromCreatePayload(ctxh, testingdocuments.CreatePOPayload()) - assert.Nil(t, err) - err = poSrv.repo.Create(accountID, po.CurrentVersion(), po) - assert.Nil(t, err) - po, err = poSrv.validateAndPersist(ctxh, nil, po, CreateValidator()) - assert.Nil(t, po) - assert.Error(t, err) - assert.Contains(t, err.Error(), storage.ErrRepositoryModelCreateKeyExists) - - // success - po, err = poSrv.DeriveFromCreatePayload(ctxh, testingdocuments.CreatePOPayload()) - assert.Nil(t, err) - po, err = poSrv.validateAndPersist(ctxh, nil, po, CreateValidator()) - assert.Nil(t, err) - assert.NotNil(t, po) -} - -var testRepoGlobal documents.Repository - -func testRepo() documents.Repository { - if testRepoGlobal != nil { - return testRepoGlobal - } - - ldb, err := leveldb.NewLevelDBStorage(leveldb.GetRandomTestStoragePath()) - if err != nil { - panic(err) - } - testRepoGlobal = documents.NewDBRepository(leveldb.NewLevelDBRepository(ldb)) - testRepoGlobal.Register(&PurchaseOrder{}) - return testRepoGlobal -} - -func createCDWithEmbeddedPO(t *testing.T) (documents.Model, coredocumentpb.CoreDocument) { - po := new(PurchaseOrder) - err := po.InitPurchaseOrderInput(testingdocuments.CreatePOPayload(), did) - assert.NoError(t, err) - po.GetTestCoreDocWithReset() - _, err = po.CalculateDataRoot() - assert.NoError(t, err) - _, err = po.CalculateSigningRoot() - assert.NoError(t, err) - _, err = po.CalculateDocumentRoot() - assert.NoError(t, err) - cd, err := po.PackCoreDocument() - assert.NoError(t, err) - return po, cd -} - -func TestService_CreateModel(t *testing.T) { - payload := documents.CreatePayload{} - srv := service{} - - // nil model - _, _, err := srv.CreateModel(context.Background(), payload) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrDocumentNil, err)) - - // empty context - payload.Data = utils.RandomSlice(32) - _, _, err = srv.CreateModel(context.Background(), payload) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrDocumentConfigAccountID, err)) - - // invalid data - ctxh := testingconfig.CreateAccountContext(t, cfg) - _, _, err = srv.CreateModel(ctxh, payload) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrDocumentInvalid, err)) - - // validator failed - payload.Data = validData(t) - _, _, err = srv.CreateModel(ctxh, payload) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrDocumentInvalid, err)) - - // success - payload.Data = validDataWithCurrency(t) - srv.repo = testRepo() - jm := testingjobs.MockJobManager{} - jm.On("ExecuteWithinJob", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(jobs.NilJobID(), make(chan bool), nil) - srv.jobManager = jm - m, _, err := srv.CreateModel(ctxh, payload) - assert.NoError(t, err) - assert.NotNil(t, m) - jm.AssertExpectations(t) -} - -func TestService_UpdateModel(t *testing.T) { - payload := documents.UpdatePayload{} - srv := service{} - - // nil model - _, _, err := srv.UpdateModel(context.Background(), payload) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrDocumentNil, err)) - - // empty context - payload.Data = utils.RandomSlice(32) - _, _, err = srv.UpdateModel(context.Background(), payload) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrDocumentConfigAccountID, err)) - - // missing id - ctxh := testingconfig.CreateAccountContext(t, cfg) - _, srvr := getServiceWithMockedLayers() - srv = srvr.(service) - payload.DocumentID = utils.RandomSlice(32) - _, _, err = srv.UpdateModel(ctxh, payload) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrDocumentNotFound, err)) - - // payload invalid - po := createPurchaseOrder(t) - err = testRepo().Create(did[:], po.ID(), po) - assert.NoError(t, err) - payload.DocumentID = po.ID() - _, _, err = srv.UpdateModel(ctxh, payload) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrDocumentInvalid, err)) - - // validator failed - payload.Data = validData(t) - _, _, err = srv.UpdateModel(ctxh, payload) - assert.Error(t, err) - assert.True(t, errors.IsOfType(documents.ErrDocumentInvalid, err)) - - // Success - payload.Data = validDataWithCurrency(t) - jm := testingjobs.MockJobManager{} - jm.On("ExecuteWithinJob", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(jobs.NilJobID(), make(chan bool), nil) - srv.jobManager = jm - m, _, err := srv.UpdateModel(ctxh, payload) - assert.NoError(t, err) - assert.Equal(t, m.ID(), po.ID()) - assert.Equal(t, m.CurrentVersion(), po.NextVersion()) - jm.AssertExpectations(t) -} diff --git a/documents/purchaseorder/test_bootstrapper.go b/documents/purchaseorder/test_bootstrapper.go deleted file mode 100644 index aaefb62f5..000000000 --- a/documents/purchaseorder/test_bootstrapper.go +++ /dev/null @@ -1,11 +0,0 @@ -// +build integration unit - -package purchaseorder - -func (b *Bootstrapper) TestBootstrap(context map[string]interface{}) error { - return b.Bootstrap(context) -} - -func (*Bootstrapper) TestTearDown() error { - return nil -} diff --git a/documents/purchaseorder/validator.go b/documents/purchaseorder/validator.go deleted file mode 100644 index abdb32216..000000000 --- a/documents/purchaseorder/validator.go +++ /dev/null @@ -1,43 +0,0 @@ -package purchaseorder - -import ( - "github.com/centrifuge/go-centrifuge/anchors" - "github.com/centrifuge/go-centrifuge/documents" - "github.com/centrifuge/go-centrifuge/errors" -) - -// fieldValidateFunc validates the fields of the purchase order model -func fieldValidator() documents.Validator { - return documents.ValidatorFunc(func(_, new documents.Model) error { - if new == nil { - return errors.New("nil document") - } - - po, ok := new.(*PurchaseOrder) - if !ok { - return errors.New("unknown document type") - } - - var err error - if !documents.IsCurrencyValid(po.Data.Currency) { - err = errors.AppendError(err, documents.NewError("po_currency", "currency is invalid")) - } - - return err - }) -} - -// CreateValidator returns a validator group that should be run before creating the purchase order and persisting it to DB -func CreateValidator() documents.ValidatorGroup { - return documents.ValidatorGroup{ - fieldValidator(), - } -} - -// UpdateValidator returns a validator group that should be run before updating the purchase order -func UpdateValidator(repo anchors.AnchorRepository) documents.ValidatorGroup { - return documents.ValidatorGroup{ - fieldValidator(), - documents.UpdateVersionValidator(repo), - } -} diff --git a/documents/purchaseorder/validator_test.go b/documents/purchaseorder/validator_test.go deleted file mode 100644 index f46277a9d..000000000 --- a/documents/purchaseorder/validator_test.go +++ /dev/null @@ -1,54 +0,0 @@ -// +build unit - -package purchaseorder - -import ( - "testing" - - "github.com/centrifuge/go-centrifuge/errors" - "github.com/centrifuge/go-centrifuge/testingutils/documents" - "github.com/stretchr/testify/assert" -) - -func TestFieldValidator_Validate(t *testing.T) { - fv := fieldValidator() - - // nil error - err := fv.Validate(nil, nil) - assert.Error(t, err) - errs := errors.GetErrs(err) - assert.Len(t, errs, 1, "errors length must be one") - assert.Contains(t, errs[0].Error(), "nil document") - - // unknown type - err = fv.Validate(nil, &testingdocuments.MockModel{}) - assert.Error(t, err) - errs = errors.GetErrs(err) - assert.Len(t, errs, 1, "errors length must be one") - assert.Contains(t, errs[0].Error(), "unknown document type") - - // fail - err = fv.Validate(nil, new(PurchaseOrder)) - assert.Error(t, err) - errs = errors.GetErrs(err) - assert.Len(t, errs, 1, "errors length must be 2") - assert.Contains(t, errs[0].Error(), "currency is invalid") - - // success - err = fv.Validate(nil, &PurchaseOrder{ - Data: Data{ - Currency: "EUR", - }, - }) - assert.Nil(t, err) -} - -func TestCreateValidator(t *testing.T) { - cv := CreateValidator() - assert.Len(t, cv, 1) -} - -func TestUpdateValidator(t *testing.T) { - uv := UpdateValidator(nil) - assert.Len(t, uv, 2) -} diff --git a/documents/read_acls.go b/documents/read_acls.go index 45439b64c..a201d9a3d 100644 --- a/documents/read_acls.go +++ b/documents/read_acls.go @@ -10,7 +10,6 @@ import ( "github.com/centrifuge/go-centrifuge/crypto" "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/go-centrifuge/identity" - "github.com/centrifuge/go-centrifuge/protobufs/gen/go/document" "github.com/centrifuge/go-centrifuge/utils" "github.com/centrifuge/precise-proofs/proofs" "github.com/centrifuge/precise-proofs/proofs/proto" @@ -441,7 +440,7 @@ func (cd *CoreDocument) ATGranteeCanRead(ctx context.Context, docService Service } // AddAccessToken adds the AccessToken to the document -func (cd *CoreDocument) AddAccessToken(ctx context.Context, payload documentpb.AccessTokenParams) (*CoreDocument, error) { +func (cd *CoreDocument) AddAccessToken(ctx context.Context, payload AccessTokenParams) (*CoreDocument, error) { ncd, err := cd.PrepareNewVersion(nil, CollaboratorsAccess{}, nil) if err != nil { return nil, err @@ -458,20 +457,15 @@ func (cd *CoreDocument) AddAccessToken(ctx context.Context, payload documentpb.A } // DeleteAccessToken deletes an access token on the Document -func (cd *CoreDocument) DeleteAccessToken(ctx context.Context, granteeID string) (*CoreDocument, error) { +func (cd *CoreDocument) DeleteAccessToken(granteeID identity.DID) (*CoreDocument, error) { ncd, err := cd.PrepareNewVersion(nil, CollaboratorsAccess{}, nil) if err != nil { return nil, err } - _, err = identity.StringsToDIDs(granteeID) - if err != nil { - return nil, err - } - accessTokens := ncd.Document.AccessTokens for i, t := range accessTokens { - if hexutil.Encode(t.Grantee) == granteeID { + if bytes.Equal(t.Grantee, granteeID.ToAddress().Bytes()) { ncd.Document.AccessTokens = removeTokenAtIndex(i, accessTokens) ncd.Modified = true return ncd, nil @@ -488,16 +482,13 @@ func removeTokenAtIndex(idx int, tokens []*coredocumentpb.AccessToken) []*coredo } // assembleAccessToken assembles a Read Access Token from the payload received -func assembleAccessToken(ctx context.Context, payload documentpb.AccessTokenParams, docVersion []byte) (*coredocumentpb.AccessToken, error) { +func assembleAccessToken(ctx context.Context, payload AccessTokenParams, docVersion []byte) (*coredocumentpb.AccessToken, error) { account, err := contextutil.Account(ctx) if err != nil { return nil, err } tokenIdentifier := utils.RandomSlice(32) - id, err := account.GetIdentityID() - if err != nil { - return nil, err - } + id := account.GetIdentityID() granterID, err := identity.NewDIDFromBytes(id) if err != nil { return nil, err diff --git a/documents/read_acls_test.go b/documents/read_acls_test.go index 8b6542715..8e449a0ca 100644 --- a/documents/read_acls_test.go +++ b/documents/read_acls_test.go @@ -3,7 +3,7 @@ package documents import ( - "context" + "crypto/sha256" "fmt" "math/big" "testing" @@ -15,7 +15,6 @@ import ( "github.com/centrifuge/go-centrifuge/contextutil" "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/go-centrifuge/identity" - "github.com/centrifuge/go-centrifuge/protobufs/gen/go/document" "github.com/centrifuge/go-centrifuge/testingutils/commons" "github.com/centrifuge/go-centrifuge/testingutils/config" "github.com/centrifuge/go-centrifuge/testingutils/identity" @@ -28,16 +27,6 @@ import ( "github.com/stretchr/testify/mock" ) -type MockService struct { - Service - mock.Mock -} - -func (m *MockService) GetVersion(ctx context.Context, documentID []byte, version []byte) (Model, error) { - args := m.Called(documentID, version) - return args.Get(0).(Model), args.Error(1) -} - func TestReadACLs_initReadRules(t *testing.T) { cd, err := newCoreDocument() assert.NoError(t, err) @@ -257,7 +246,8 @@ func TestCoreDocument_getRoleProofKey(t *testing.T) { func TestCoreDocumentModel_GetNFTProofs(t *testing.T) { cd, err := newCoreDocument() assert.NoError(t, err) - testTree := cd.DefaultTreeWithPrefix("invoice", []byte{1, 0, 0, 0}) + testTree, err := cd.DefaultTreeWithPrefix("invoice", []byte{1, 0, 0, 0}) + assert.NoError(t, err) props := []proofs.Property{NewLeafProperty("invoice.sample_field", []byte{1, 0, 0, 0, 0, 0, 0, 200})} err = testTree.AddLeaf(proofs.LeafNode{Hash: utils.RandomSlice(32), Hashed: true, Property: props[0]}) assert.NoError(t, err) @@ -270,12 +260,10 @@ func TestCoreDocumentModel_GetNFTProofs(t *testing.T) { cd.initReadRules([]identity.DID{account}) registry := common.HexToAddress("0xf72855759a39fb75fc7341139f5d7a3974d4da08") tokenID := utils.RandomSlice(32) - _, err = cd.CalculateSigningRoot(documenttypes.InvoiceDataTypeUrl, testTree.RootHash()) - assert.NoError(t, err) - _, err = cd.CalculateDocumentRoot(documenttypes.InvoiceDataTypeUrl, testTree.RootHash()) - assert.NoError(t, err) cd, err = cd.AddNFT(true, registry, tokenID) assert.NoError(t, err) + signingRoot, err := cd.CalculateSigningRoot(documenttypes.InvoiceDataTypeUrl, testTree.RootHash()) + assert.NoError(t, err) _, err = cd.CalculateDocumentRoot(documenttypes.InvoiceDataTypeUrl, testTree.RootHash()) assert.NoError(t, err) @@ -324,9 +312,6 @@ func TestCoreDocumentModel_GetNFTProofs(t *testing.T) { }, } - tree, err := cd.DocumentRootTree(documenttypes.InvoiceDataTypeUrl, testTree.RootHash()) - assert.NoError(t, err) - for _, c := range tests { pfs, err := cd.CreateNFTProofs(documenttypes.InvoiceDataTypeUrl, testTree, account, c.registry, c.tokenID, c.nftUniqueProof, c.nftReadAccess) if c.error { @@ -338,7 +323,7 @@ func TestCoreDocumentModel_GetNFTProofs(t *testing.T) { assert.True(t, len(pfs) > 0) for _, pf := range pfs { - valid, err := tree.ValidateProof(pf) + valid, err := ValidateProof(pf, signingRoot, sha256.New()) assert.NoError(t, err) assert.True(t, valid) } @@ -350,15 +335,14 @@ func TestCoreDocumentModel_ATOwnerCanRead(t *testing.T) { account, _ := contextutil.Account(ctx) srv := new(testingcommons.MockIdentityService) docSrv := new(MockService) - id, err := account.GetIdentityID() - assert.NoError(t, err) + id := account.GetIdentityID() granteeID, err := identity.NewDIDFromString("0xBAEb33a61f05e6F269f1c4b4CFF91A901B54DaF7") assert.NoError(t, err) granterID, err := identity.NewDIDFromBytes(id) assert.NoError(t, err) cd, err := NewCoreDocument(nil, CollaboratorsAccess{ReadWriteCollaborators: []identity.DID{granterID}}, nil) assert.NoError(t, err) - payload := documentpb.AccessTokenParams{ + payload := AccessTokenParams{ Grantee: hexutil.Encode(granteeID[:]), DocumentIdentifier: hexutil.Encode(cd.Document.DocumentIdentifier), } @@ -407,7 +391,7 @@ func TestCoreDocumentModel_AddAccessToken(t *testing.T) { assert.Len(t, cd.AccessTokens, 0) // invalid centID format - payload := documentpb.AccessTokenParams{ + payload := AccessTokenParams{ // invalid grantee format Grantee: "randomCentID", DocumentIdentifier: "randomDocID", @@ -417,7 +401,7 @@ func TestCoreDocumentModel_AddAccessToken(t *testing.T) { assert.Contains(t, err.Error(), "failed to construct access token: malformed address provided") // invalid centID length invalidCentID := utils.RandomSlice(25) - payload = documentpb.AccessTokenParams{ + payload = AccessTokenParams{ Grantee: hexutil.Encode(invalidCentID), DocumentIdentifier: hexutil.Encode(m.Document.DocumentIdentifier), } @@ -425,10 +409,9 @@ func TestCoreDocumentModel_AddAccessToken(t *testing.T) { assert.Error(t, err) assert.Contains(t, err.Error(), "failed to construct access token: malformed address provided") // invalid docID length - id, err := account.GetIdentityID() - assert.NoError(t, err) + id := account.GetIdentityID() invalidDocID := utils.RandomSlice(33) - payload = documentpb.AccessTokenParams{ + payload = AccessTokenParams{ Grantee: hexutil.Encode(id), DocumentIdentifier: hexutil.Encode(invalidDocID), } @@ -437,7 +420,7 @@ func TestCoreDocumentModel_AddAccessToken(t *testing.T) { assert.Error(t, err) assert.Contains(t, err.Error(), "failed to construct access token: invalid identifier length") // valid - payload = documentpb.AccessTokenParams{ + payload = AccessTokenParams{ Grantee: hexutil.Encode(id), DocumentIdentifier: hexutil.Encode(m.Document.DocumentIdentifier), } @@ -455,19 +438,18 @@ func TestCoreDocumentModel_DeleteAccessToken(t *testing.T) { account, err := contextutil.Account(ctx) assert.NoError(t, err) - id, err := account.GetIdentityID() + id, err := identity.NewDIDFromBytes(account.GetIdentityID()) assert.NoError(t, err) - cd := m.Document assert.Len(t, cd.AccessTokens, 0) - payload := documentpb.AccessTokenParams{ - Grantee: hexutil.Encode(id), + payload := AccessTokenParams{ + Grantee: id.String(), DocumentIdentifier: hexutil.Encode(m.Document.DocumentIdentifier), } // no access token found - _, err = m.DeleteAccessToken(ctx, hexutil.Encode(id)) + _, err = m.DeleteAccessToken(id) assert.Error(t, err) // add access token @@ -476,7 +458,7 @@ func TestCoreDocumentModel_DeleteAccessToken(t *testing.T) { assert.Len(t, ncd.Document.AccessTokens, 1) // invalid granteeID - _, err = ncd.DeleteAccessToken(ctx, hexutil.Encode(utils.RandomSlice(32))) + _, err = ncd.DeleteAccessToken(testingidentity.GenerateRandomDID()) assert.Error(t, err) assert.Len(t, ncd.Document.AccessTokens, 1) @@ -487,7 +469,7 @@ func TestCoreDocumentModel_DeleteAccessToken(t *testing.T) { assert.NoError(t, err) assert.Len(t, updated.Document.AccessTokens, 2) - final, err := updated.DeleteAccessToken(ctx, hexutil.Encode(id)) + final, err := updated.DeleteAccessToken(id) assert.NoError(t, err) assert.Len(t, final.Document.AccessTokens, 1) assert.Equal(t, final.Document.AccessTokens[0].Grantee, did[:]) diff --git a/documents/repository.go b/documents/repository.go index 3535f4b0c..6d9ba95ab 100644 --- a/documents/repository.go +++ b/documents/repository.go @@ -1,13 +1,50 @@ package documents import ( + "bytes" + "encoding/json" + "reflect" + "time" + "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/go-centrifuge/storage" "github.com/ethereum/go-ethereum/common/hexutil" ) -// DocPrefix holds the generic prefix of a document in DB -const DocPrefix string = "document_" +const ( + // DocPrefix holds the generic prefix of a document in DB + DocPrefix string = "document_" + + // LatestPrefix is used to index latest version of the document. + LatestPrefix string = "latest_document_" +) + +type latestVersion struct { + // CurrentVersion is the current latest version of the document + CurrentVersion []byte `json:"current_version"` + + // NextVersion is the supposed next version of the document. + // this is stored to check and invalidate the old index + NextVersion []byte `json:"next_version"` + + // Timestamp is the time when document version is created. + Timestamp time.Time `json:"timestamp"` +} + +// JSON marshals latestVersion to json bytes. +func (l *latestVersion) JSON() ([]byte, error) { + return json.Marshal(l) +} + +// Type returns the type of latestVersion. +func (l *latestVersion) Type() reflect.Type { + return reflect.TypeOf(l) +} + +// FromJSON loads json bytes to latest version +func (l *latestVersion) FromJSON(data []byte) error { + return json.Unmarshal(data, l) +} // Repository defines the required methods for a document repository. // Can be implemented by any type that stores the documents. Ex: levelDB, sql etc... @@ -28,10 +65,14 @@ type Repository interface { // Register registers the model so that the DB can return the document without knowing the type Register(model Model) + + // GetLatest returns the latest version of the document. + GetLatest(accountID, docID []byte) (Model, error) } // NewDBRepository creates an instance of the documents Repository func NewDBRepository(db storage.Repository) Repository { + db.Register(new(latestVersion)) return &repo{db: db} } @@ -74,12 +115,117 @@ func (r *repo) Get(accountID, id []byte) (Model, error) { // should error out if the document exists. func (r *repo) Create(accountID, id []byte, model Model) error { key := r.getKey(accountID, id) - return r.db.Create(key, model) + if err := r.db.Create(key, model); err != nil { + return err + } + + return r.updateLatestIndex(accountID, model) } // Update strictly updates the model. // Will error out when the model doesn't exist in the DB. func (r *repo) Update(accountID, id []byte, model Model) error { key := r.getKey(accountID, id) - return r.db.Update(key, model) + if err := r.db.Update(key, model); err != nil { + return err + } + + return r.updateLatestIndex(accountID, model) +} + +// GetLatest returns thee latest version of the document. +func (r *repo) GetLatest(accountID, docID []byte) (Model, error) { + key := r.getLatestKey(accountID, docID) + lv, err := r.getLatest(key) + if err != nil { + return nil, err + } + + return r.Get(accountID, lv.CurrentVersion) +} + +func (r *repo) getLatest(key []byte) (*latestVersion, error) { + val, err := r.db.Get(key) + if err != nil { + return nil, err + } + + lv, ok := val.(*latestVersion) + if ok { + return lv, nil + } + + // delete key val if the type mismatches + err = r.db.Delete(key) + if err != nil { + return nil, err + } + + return nil, ErrDocumentNotFound +} + +// getLatestKey constructs the key to the latest version of the document. +// Note: DocumentIdentifier needs to be passed here not the versionID. +func (r *repo) getLatestKey(accountID, docID []byte) []byte { + hexKey := hexutil.Encode(append(accountID, docID...)) + return append([]byte(LatestPrefix), []byte(hexKey)...) +} + +// storeLatestIndex stores the latestVersion to db. +// If update is true, it is assumed that index is overwritten +// else, index is created first time. +func (r *repo) storeLatestIndex(key []byte, model Model, update bool) error { + lv := &latestVersion{ + CurrentVersion: model.CurrentVersion(), + NextVersion: model.NextVersion(), + } + + tm, err := model.Timestamp() + if err != nil { + // we will update this actual value when available + tm = time.Now().UTC() + } + lv.Timestamp = tm + + if update { + return r.db.Update(key, lv) + } + + return r.db.Create(key, lv) +} + +// updateLatestIndex updates the latest version index. +// We check if the latest index is present for a model. +// If not found, create a latest index and return. +// Note: anchor timestamp is not available immediately, so don't error out if the timestamp is empty +// If found, check if the next version matches the current version of the passed model. +// If matches, update the timestamp of anchor and return. +// If not matches, check the model timestamp is greater than stored timestamp. +// If greater update the latestVersion and return +// If not, skip update and return. +func (r *repo) updateLatestIndex(accID []byte, model Model) error { + key := r.getLatestKey(accID, model.ID()) + lv, err := r.getLatest(key) + if err != nil { + // no index is created yet. create one + return r.storeLatestIndex(key, model, false) + } + + if bytes.Equal(lv.NextVersion, model.CurrentVersion()) { + return r.storeLatestIndex(key, model, true) + } + + // compare timestamps + ts, err := model.Timestamp() + if err != nil { + ts = time.Now().UTC() + } + + if lv.Timestamp.Before(ts) { + // newer version found. so update + return r.storeLatestIndex(key, model, true) + } + + // must be an old version. + return nil } diff --git a/documents/repository_test.go b/documents/repository_test.go index a0f723c91..0d69eb81a 100644 --- a/documents/repository_test.go +++ b/documents/repository_test.go @@ -6,7 +6,9 @@ import ( "encoding/json" "reflect" "testing" + "time" + "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/go-centrifuge/storage" "github.com/centrifuge/go-centrifuge/utils" "github.com/stretchr/testify/assert" @@ -19,7 +21,9 @@ func getRepository(ctx map[string]interface{}) Repository { type doc struct { Model - SomeString string `json:"some_string"` + DocID, Current, Next []byte + SomeString string `json:"some_string"` + Time time.Time } type unknownDoc struct { @@ -38,6 +42,18 @@ func (u *unknownDoc) FromJSON(j []byte) error { return json.Unmarshal(j, u) } +func (m *doc) ID() []byte { + return m.DocID +} + +func (m *doc) CurrentVersion() []byte { + return m.Current +} + +func (m *doc) NextVersion() []byte { + return m.Next +} + func (m *doc) JSON() ([]byte, error) { return json.Marshal(m) } @@ -50,10 +66,14 @@ func (m *doc) Type() reflect.Type { return reflect.TypeOf(m) } +func (m *doc) Timestamp() (time.Time, error) { + return m.Time, nil +} + func TestLevelDBRepo_Create_Exists(t *testing.T) { repo := getRepository(ctx) - d := &doc{SomeString: "Hello, World!"} accountID, id := utils.RandomSlice(32), utils.RandomSlice(32) + d := &doc{SomeString: "Hello, World!", DocID: id} assert.False(t, repo.Exists(accountID, id), "doc must not be present") err := repo.Create(accountID, id, d) assert.Nil(t, err, "Create: unknown error") @@ -66,8 +86,8 @@ func TestLevelDBRepo_Create_Exists(t *testing.T) { func TestLevelDBRepo_Update_Exists(t *testing.T) { repo := getRepository(ctx) - d := &doc{SomeString: "Hello, World!"} accountID, id := utils.RandomSlice(32), utils.RandomSlice(32) + d := &doc{SomeString: "Hello, World!", DocID: id} assert.False(t, repo.Exists(accountID, id), "doc must not be present") err := repo.Update(accountID, id, d) assert.Error(t, err, "Update: should error out") @@ -90,7 +110,7 @@ func TestLevelDBRepo_Get_Create_Update(t *testing.T) { assert.Error(t, err, "must return error") assert.Nil(t, m) - d := &doc{SomeString: "Hello, Repo!"} + d := &doc{SomeString: "Hello, Repo!", DocID: id} err = repor.Create(accountID, id, d) assert.Nil(t, err, "Create: unknown error") @@ -126,3 +146,115 @@ func TestLevelDBRepo_Get_Create_Update(t *testing.T) { assert.Contains(t, err.Error(), "is not a model object") } } + +func TestRepo_GetLatest(t *testing.T) { + // missing latest key + acc := utils.RandomSlice(20) + id := utils.RandomSlice(32) + r := getRepository(ctx) + _, err := r.GetLatest(acc, id) + assert.Error(t, err) + assert.True(t, errors.IsOfType(storage.ErrModelRepositoryNotFound, err)) + + // different type + rr := r.(*repo) + r.Register(new(doc)) + err = rr.db.Create(rr.getLatestKey(acc, id), new(doc)) + assert.NoError(t, err) + _, err = r.GetLatest(acc, id) + assert.Error(t, err) + assert.True(t, errors.IsOfType(ErrDocumentNotFound, err)) + + // missing version + rr.db.Register(new(latestVersion)) + lv := &latestVersion{ + CurrentVersion: utils.RandomSlice(32), + } + err = rr.db.Create(rr.getLatestKey(acc, id), lv) + assert.NoError(t, err) + _, err = r.GetLatest(acc, id) + assert.Error(t, err) + assert.True(t, errors.IsOfType(storage.ErrModelRepositoryNotFound, err)) + assert.True(t, rr.db.Exists(rr.getLatestKey(acc, id))) + + // success + d := new(doc) + err = rr.db.Create(rr.getKey(acc, lv.CurrentVersion), d) + assert.NoError(t, err) + m, err := r.GetLatest(acc, id) + assert.NoError(t, err) + assert.Equal(t, d, m) +} + +func TestRepo_updateLatestIndex(t *testing.T) { + r := getRepository(ctx) + rr := r.(*repo) + acc := utils.RandomSlice(20) + id := utils.RandomSlice(32) + next := utils.RandomSlice(32) + tm := time.Now().UTC() + + // missing index, should create one + d := &doc{ + DocID: id, + Current: id, + Next: next, + Time: tm, + } + assert.False(t, rr.db.Exists(rr.getLatestKey(acc, id))) + err := rr.updateLatestIndex(acc, d) + assert.NoError(t, err) + assert.True(t, rr.db.Exists(rr.getLatestKey(acc, id))) + lv, err := rr.getLatest(rr.getLatestKey(acc, id)) + assert.Equal(t, &latestVersion{ + CurrentVersion: id, + Timestamp: tm, + NextVersion: next, + }, lv) + + // next version + d.Current = next + d.Next = utils.RandomSlice(32) + d.Time = time.Now().UTC() + err = rr.updateLatestIndex(acc, d) + assert.NoError(t, err) + assert.True(t, rr.db.Exists(rr.getLatestKey(acc, id))) + lv, err = rr.getLatest(rr.getLatestKey(acc, id)) + assert.Equal(t, &latestVersion{ + CurrentVersion: next, + Timestamp: d.Time, + NextVersion: d.Next, + }, lv) + + // later time + d.Current = utils.RandomSlice(32) + d.Next = utils.RandomSlice(32) + tm = time.Now().UTC() + assert.False(t, d.Time.Equal(tm)) + d.Time = tm + err = rr.updateLatestIndex(acc, d) + assert.NoError(t, err) + assert.True(t, rr.db.Exists(rr.getLatestKey(acc, id))) + lv, err = rr.getLatest(rr.getLatestKey(acc, id)) + assert.Equal(t, &latestVersion{ + CurrentVersion: d.Current, + Timestamp: d.Time, + NextVersion: d.Next, + }, lv) + + // older version, dont update index + d.Time = time.Now().UTC().Add(-time.Hour) + oldC := d.Current + oldN := d.Next + d.Current = utils.RandomSlice(32) + d.Next = utils.RandomSlice(32) + err = rr.updateLatestIndex(acc, d) + assert.NoError(t, err) + assert.True(t, rr.db.Exists(rr.getLatestKey(acc, id))) + lv, err = rr.getLatest(rr.getLatestKey(acc, id)) + assert.Equal(t, &latestVersion{ + CurrentVersion: oldC, + Timestamp: tm, + NextVersion: oldN, + }, lv) +} diff --git a/documents/service.go b/documents/service.go index 581a9591e..898cd93a1 100644 --- a/documents/service.go +++ b/documents/service.go @@ -6,7 +6,6 @@ import ( "time" "github.com/centrifuge/centrifuge-protobufs/gen/go/coredocument" - "github.com/centrifuge/centrifuge-protobufs/gen/go/notification" "github.com/centrifuge/go-centrifuge/anchors" "github.com/centrifuge/go-centrifuge/config" "github.com/centrifuge/go-centrifuge/contextutil" @@ -14,6 +13,7 @@ import ( "github.com/centrifuge/go-centrifuge/identity" "github.com/centrifuge/go-centrifuge/jobs" "github.com/centrifuge/go-centrifuge/notification" + "github.com/centrifuge/go-centrifuge/queue" "github.com/centrifuge/go-centrifuge/utils" "github.com/centrifuge/precise-proofs/proofs/proto" "github.com/ethereum/go-ethereum/common/hexutil" @@ -28,6 +28,12 @@ type DocumentProof struct { FieldProofs []*proofspb.Proof } +// Patcher interface defines a Patch method for inner Models +type Patcher interface { + // Patch merges payload data into model + Patch(payload UpdatePayload) error +} + // Service provides an interface for functions common to all document types type Service interface { @@ -35,6 +41,7 @@ type Service interface { GetCurrentVersion(ctx context.Context, documentID []byte) (Model, error) // Exists checks if a document exists + // Deprecated Exists(ctx context.Context, documentID []byte) bool // GetVersion reads a document from the database @@ -56,16 +63,35 @@ type Service interface { ReceiveAnchoredDocument(ctx context.Context, model Model, collaborator identity.DID) error // Create validates and persists Model and returns a Updated model - Create(ctx context.Context, model Model) (Model, jobs.JobID, chan bool, error) + // Deprecated + Create(ctx context.Context, model Model) (Model, jobs.JobID, chan error, error) // Update validates and updates the model and return the updated model - Update(ctx context.Context, model Model) (Model, jobs.JobID, chan bool, error) + // Deprecated + Update(ctx context.Context, model Model) (Model, jobs.JobID, chan error, error) // CreateModel creates a new model from the payload and initiates the anchor process. + // Deprecated CreateModel(ctx context.Context, payload CreatePayload) (Model, jobs.JobID, error) // UpdateModel prepares the next version from the payload and initiates the anchor process. + // Deprecated UpdateModel(ctx context.Context, payload UpdatePayload) (Model, jobs.JobID, error) + + // Derive derives the Model from the Payload. + // If document_id is provided, it will prepare a new version of the document + // Document Data will be patched from the old and attributes and collaborators are imported + // If not provided, it is a fresh document. + Derive(ctx context.Context, payload UpdatePayload) (Model, error) + + // Commit triggers validations, state change and anchor job + Commit(ctx context.Context, model Model) (jobs.JobID, error) + + // Validate takes care of document validation + Validate(ctx context.Context, model Model, old Model) error + + // New returns a new uninitialised document. + New(scheme string) (Model, error) } // service implements Service @@ -76,6 +102,8 @@ type service struct { anchorRepo anchors.AnchorRepository registry *ServiceRegistry idService identity.Service + queueSrv queue.TaskQueuer + jobManager jobs.Manager } var srvLog = logging.Logger("document-service") @@ -86,7 +114,9 @@ func DefaultService( repo Repository, anchorRepo anchors.AnchorRepository, registry *ServiceRegistry, - idService identity.Service) Service { + idService identity.Service, + queueSrv queue.TaskQueuer, + jobManager jobs.Manager) Service { return service{ config: config, repo: repo, @@ -94,30 +124,24 @@ func DefaultService( notifier: notification.NewWebhookSender(), registry: registry, idService: idService, + queueSrv: queueSrv, + jobManager: jobManager, } } -func (s service) searchVersion(ctx context.Context, m Model) (Model, error) { - id, next := m.ID(), m.NextVersion() - if !s.Exists(ctx, next) { - // at the latest locally known version - return m, nil - } - - m, err := s.getVersion(ctx, id, next) +func (s service) GetCurrentVersion(ctx context.Context, documentID []byte) (Model, error) { + acc, err := contextutil.Account(ctx) if err != nil { - return nil, err + return nil, ErrDocumentConfigAccountID } - return s.searchVersion(ctx, m) -} - -func (s service) GetCurrentVersion(ctx context.Context, documentID []byte) (Model, error) { - model, err := s.getVersion(ctx, documentID, documentID) + accID := acc.GetIdentityID() + m, err := s.repo.GetLatest(accID, documentID) if err != nil { return nil, errors.NewTypedError(ErrDocumentNotFound, err) } - return s.searchVersion(ctx, model) + + return m, nil } func (s service) GetVersion(ctx context.Context, documentID []byte, version []byte) (Model, error) { @@ -164,10 +188,7 @@ func (s service) RequestDocumentSignature(ctx context.Context, model Model, coll if err != nil { return nil, ErrDocumentConfigAccountID } - idBytes, err := acc.GetIdentityID() - if err != nil { - return nil, err - } + idBytes := acc.GetIdentityID() did, err := identity.NewDIDFromBytes(idBytes) if err != nil { return nil, err @@ -202,6 +223,11 @@ func (s service) RequestDocumentSignature(ctx context.Context, model Model, coll } model.AppendSignatures(sig) + // set the status to committing since we are at requesting signatures stage. + if err := model.SetStatus(Committing); err != nil { + return nil, err + } + // Logic for receiving version n (n > 1) of the document for the first time // TODO(ved): we should not save the new model with old identifier. We should sync from the peer. if !s.repo.Exists(did[:], model.ID()) && !utils.IsSameByteSlice(model.ID(), model.CurrentVersion()) { @@ -226,10 +252,7 @@ func (s service) ReceiveAnchoredDocument(ctx context.Context, model Model, colla return ErrDocumentConfigAccountID } - idBytes, err := acc.GetIdentityID() - if err != nil { - return err - } + idBytes := acc.GetIdentityID() did, err := identity.NewDIDFromBytes(idBytes) if err != nil { return err @@ -253,28 +276,33 @@ func (s service) ReceiveAnchoredDocument(ctx context.Context, model Model, colla return errors.NewTypedError(ErrDocumentInvalid, err) } - err = s.repo.Update(did[:], model.CurrentVersion(), model) - if err != nil { - return errors.NewTypedError(ErrDocumentPersistence, err) + // set the status to committed since the document is anchored already. + if err := model.SetStatus(Committed); err != nil { + return err } - ts, err := utils.ToTimestamp(time.Now().UTC()) + err = s.repo.Update(did[:], model.CurrentVersion(), model) if err != nil { - return errors.NewTypedError(ErrDocumentNotification, err) + return errors.NewTypedError(ErrDocumentPersistence, err) } - notificationMsg := ¬ificationpb.NotificationMessage{ - EventType: uint32(notification.ReceivedPayload), - AccountId: did.String(), - FromId: hexutil.Encode(collaborator[:]), - ToId: did.String(), - Recorded: ts, + notificationMsg := notification.Message{ + EventType: notification.ReceivedPayload, + AccountID: did.String(), + FromID: hexutil.Encode(collaborator[:]), + ToID: did.String(), + Recorded: time.Now().UTC(), DocumentType: model.DocumentType(), - DocumentId: hexutil.Encode(model.ID()), + DocumentID: hexutil.Encode(model.ID()), } - // Async until we add queuing - go s.notifier.Send(ctx, notificationMsg) + // async so that we don't return an error as the p2p reply + go func() { + _, err = s.notifier.Send(ctx, notificationMsg) + if err != nil { + log.Error(err) + } + }() return nil } @@ -284,10 +312,7 @@ func (s service) Exists(ctx context.Context, documentID []byte) bool { if err != nil { return false } - idBytes, err := acc.GetIdentityID() - if err != nil { - return false - } + idBytes := acc.GetIdentityID() return s.repo.Exists(idBytes, documentID) } @@ -296,10 +321,7 @@ func (s service) getVersion(ctx context.Context, documentID, version []byte) (Mo if err != nil { return nil, ErrDocumentConfigAccountID } - idBytes, err := acc.GetIdentityID() - if err != nil { - return nil, err - } + idBytes := acc.GetIdentityID() model, err := s.repo.Get(idBytes, version) if err != nil { return nil, errors.NewTypedError(ErrDocumentVersionNotFound, err) @@ -325,7 +347,7 @@ func (s service) DeriveFromCoreDocument(cd coredocumentpb.CoreDocument) (Model, return srv.DeriveFromCoreDocument(cd) } -func (s service) Create(ctx context.Context, model Model) (Model, jobs.JobID, chan bool, error) { +func (s service) Create(ctx context.Context, model Model) (Model, jobs.JobID, chan error, error) { srv, err := s.getService(model) if err != nil { return nil, jobs.NilJobID(), nil, errors.New("failed to get service: %v", err) @@ -334,7 +356,7 @@ func (s service) Create(ctx context.Context, model Model) (Model, jobs.JobID, ch return srv.Create(ctx, model) } -func (s service) Update(ctx context.Context, model Model) (Model, jobs.JobID, chan bool, error) { +func (s service) Update(ctx context.Context, model Model) (Model, jobs.JobID, chan error, error) { srv, err := s.getService(model) if err != nil { return nil, jobs.NilJobID(), nil, errors.New("failed to get service: %v", err) @@ -364,3 +386,109 @@ func (s service) UpdateModel(ctx context.Context, payload UpdatePayload) (Model, return srv.UpdateModel(ctx, payload) } + +// Derive looks for specific document type service based in the schema and delegates the Derivation to that service.Ëœ +func (s service) Derive(ctx context.Context, payload UpdatePayload) (Model, error) { + if len(payload.DocumentID) == 0 { + did, err := contextutil.AccountDID(ctx) + if err != nil { + return nil, ErrDocumentConfigAccountID + } + + doc, err := s.New(payload.Scheme) + if err != nil { + return nil, err + } + + payload.Collaborators.ReadWriteCollaborators = append(payload.Collaborators.ReadWriteCollaborators, did) + if err := doc.(Deriver).DeriveFromCreatePayload(ctx, payload.CreatePayload); err != nil { + return nil, errors.NewTypedError(ErrDocumentInvalid, err) + } + + return doc, nil + } + + old, err := s.GetCurrentVersion(ctx, payload.DocumentID) + if err != nil { + return nil, err + } + + // check if the scheme is correct + if old.Scheme() != payload.Scheme { + return nil, errors.NewTypedError(ErrDocumentInvalidType, errors.New("%v is not an %s", hexutil.Encode(payload.DocumentID), payload.Scheme)) + } + + doc, err := old.(Deriver).DeriveFromUpdatePayload(ctx, payload) + if err != nil { + return nil, errors.NewTypedError(ErrDocumentInvalid, err) + } + + return doc, nil +} + +// Validate takes care of document validation +func (s service) Validate(ctx context.Context, model Model, old Model) error { + srv, err := s.registry.LocateService(model.Scheme()) + if err != nil { + return errors.NewTypedError(ErrDocumentSchemeUnknown, err) + } + + // If old version provided + if old != nil { + if err := UpdateVersionValidator(s.anchorRepo).Validate(old, model); err != nil { + return errors.NewTypedError(ErrDocumentValidation, err) + } + } else { + if err := CreateVersionValidator(s.anchorRepo).Validate(nil, model); err != nil { + return errors.NewTypedError(ErrDocumentValidation, err) + } + } + + // Run document specific validations if any + return srv.Validate(ctx, model, old) +} + +// Commit triggers validations, state change and anchor job +func (s service) Commit(ctx context.Context, model Model) (jobs.JobID, error) { + did, err := contextutil.AccountDID(ctx) + if err != nil { + return jobs.NilJobID(), ErrDocumentConfigAccountID + } + + // Get latest committed version + old, err := s.GetCurrentVersion(ctx, model.ID()) + if err != nil && !errors.IsOfType(ErrDocumentNotFound, err) { + return jobs.NilJobID(), err + } + + if err := s.Validate(ctx, model, old); err != nil { + return jobs.NilJobID(), errors.NewTypedError(ErrDocumentValidation, err) + } + + if err := model.SetStatus(Committing); err != nil { + return jobs.NilJobID(), err + } + + err = s.repo.Create(did[:], model.CurrentVersion(), model) + if err != nil { + return jobs.NilJobID(), errors.NewTypedError(ErrDocumentPersistence, err) + } + + jobID := contextutil.Job(ctx) + jobID, _, err = CreateAnchorJob(ctx, s.jobManager, s.queueSrv, did, jobID, model.CurrentVersion()) + if err != nil { + return jobs.NilJobID(), err + } + + return jobID, nil +} + +// New returns a new uninitialised document for the scheme. +func (s service) New(scheme string) (Model, error) { + srv, err := s.registry.LocateService(scheme) + if err != nil { + return nil, ErrDocumentSchemeUnknown + } + + return srv.New(scheme) +} diff --git a/documents/service_test.go b/documents/service_test.go new file mode 100644 index 000000000..25e79edea --- /dev/null +++ b/documents/service_test.go @@ -0,0 +1,222 @@ +// +build unit + +package documents + +import ( + "context" + "testing" + "time" + + "github.com/centrifuge/go-centrifuge/errors" + "github.com/centrifuge/go-centrifuge/jobs" + testingconfig "github.com/centrifuge/go-centrifuge/testingutils/config" + "github.com/centrifuge/go-centrifuge/testingutils/testingjobs" + "github.com/centrifuge/go-centrifuge/utils" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func TestService_Validate(t *testing.T) { + r := NewServiceRegistry() + scheme := "invoice" + srv := new(MockService) + srv.On("Validate", mock.Anything, mock.Anything, mock.Anything).Return(nil) + err := r.Register(scheme, srv) + assert.NoError(t, err) + + // unsupported svc schema + m := new(mockModel) + m.On("Scheme", mock.Anything).Return("some scheme") + s := service{registry: r} + err = s.Validate(context.Background(), m, nil) + assert.Error(t, err) + + // create validation error, already anchored + id := utils.RandomSlice(32) + ctxh := testingconfig.CreateAccountContext(t, cfg) + m = new(mockModel) + nid := utils.RandomSlice(32) + m.On("ID", mock.Anything).Return(id) + m.On("CurrentVersion").Return(id) + m.On("NextVersion").Return(nid) + m.On("PreviousVersion").Return(nid) + m.On("Scheme", mock.Anything).Return("invoice") + anchorRepo := new(mockRepo) + anchorRepo.On("GetAnchorData", mock.Anything).Return(utils.RandomSlice(32), time.Now(), nil) + s.anchorRepo = anchorRepo + err = s.Validate(ctxh, m, nil) + assert.Error(t, err) + + // create validation success + anchorRepo = new(mockRepo) + anchorRepo.On("GetAnchorData", mock.Anything).Return(id, time.Now(), errors.New("anchor data missing")) + s.anchorRepo = anchorRepo + err = s.Validate(ctxh, m, nil) + assert.NoError(t, err) + + // Update validation error, already anchored + m1 := new(mockModel) + nid1 := utils.RandomSlice(32) + m1.On("ID", mock.Anything).Return(id) + m1.On("CurrentVersion").Return(nid) + m1.On("NextVersion").Return(nid1) + m1.On("PreviousVersion").Return(id) + m1.On("Scheme", mock.Anything).Return("invoice") + anchorRepo = new(mockRepo) + anchorRepo.On("GetAnchorData", mock.Anything).Return(utils.RandomSlice(32), time.Now(), nil) + s.anchorRepo = anchorRepo + err = s.Validate(ctxh, m1, m) + assert.Error(t, err) + + // update validation success + anchorRepo = new(mockRepo) + anchorRepo.On("GetAnchorData", mock.Anything).Return(id, time.Now(), errors.New("anchor data missing")) + s.anchorRepo = anchorRepo + err = s.Validate(ctxh, m1, m) + assert.NoError(t, err) + + // specific document validation error + r = NewServiceRegistry() + srv = new(MockService) + srv.On("Validate", mock.Anything, mock.Anything, mock.Anything).Return(errors.New("specific document error")) + err = r.Register(scheme, srv) + assert.NoError(t, err) + s.registry = r + err = s.Validate(ctxh, m1, m) + assert.Error(t, err) +} + +func TestService_Commit(t *testing.T) { + r := NewServiceRegistry() + scheme := "invoice" + srv := new(MockService) + srv.On("Validate", mock.Anything, mock.Anything, mock.Anything).Return(nil) + err := r.Register(scheme, srv) + assert.NoError(t, err) + s := service{registry: r} + m := new(mockModel) + id := utils.RandomSlice(32) + m.On("ID", mock.Anything).Return(id) + m.On("Scheme", mock.Anything).Return("invoice") + + // Account ID not set + _, err = s.Commit(context.Background(), m) + assert.Error(t, err) + + // db error when fetching + mr := new(MockRepository) + mr.On("GetLatest", mock.Anything, mock.Anything).Return(nil, errors.New("some db error")).Once() + s.repo = mr + _, err = s.Commit(context.Background(), m) + assert.Error(t, err) + + // Fail validation + nid := utils.RandomSlice(32) + m.On("CurrentVersion").Return(id) + m.On("NextVersion").Return(nid) + m.On("PreviousVersion").Return(nid) + mr = new(MockRepository) + mr.On("GetLatest", mock.Anything, mock.Anything).Return(nil, ErrDocumentVersionNotFound) + s.repo = mr + anchorRepo := new(mockRepo) + anchorRepo.On("GetAnchorData", mock.Anything).Return(utils.RandomSlice(32), time.Now(), nil) + s.anchorRepo = anchorRepo + ctxh := testingconfig.CreateAccountContext(t, cfg) + _, err = s.Commit(ctxh, m) + assert.Error(t, err) + + // Error create model + anchorRepo = new(mockRepo) + anchorRepo.On("GetAnchorData", mock.Anything).Return(nil, time.Now(), errors.New("anchor data missing")) + s.anchorRepo = anchorRepo + m.On("SetStatus", mock.Anything).Return(nil) + mr.On("Create", mock.Anything, mock.Anything, mock.Anything).Return(ErrDocumentPersistence) + _, err = s.Commit(ctxh, m) + assert.Error(t, err) + + // Error anchoring + jobMan := &testingjobs.MockJobManager{} + jobMan.On("ExecuteWithinJob", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(jobs.NilJobID(), make(chan error), errors.New("error anchoring")) + s.jobManager = jobMan + mr = new(MockRepository) + mr.On("GetLatest", mock.Anything, mock.Anything).Return(nil, ErrDocumentVersionNotFound) + mr.On("Create", mock.Anything, mock.Anything, mock.Anything).Return(nil) + s.repo = mr + _, err = s.Commit(ctxh, m) + assert.Error(t, err) + + // Commit success + jobMan = &testingjobs.MockJobManager{} + jobMan.On("ExecuteWithinJob", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(jobs.NilJobID(), make(chan error), nil) + s.jobManager = jobMan + _, err = s.Commit(ctxh, m) + assert.NoError(t, err) +} + +func TestService_Derive(t *testing.T) { + scheme := "invoice" + payload := UpdatePayload{CreatePayload: CreatePayload{Scheme: scheme}} + s := service{} + + // missing account ctx + ctx := context.Background() + _, err := s.Derive(ctx, payload) + assert.Error(t, err) + assert.True(t, errors.IsOfType(ErrDocumentConfigAccountID, err)) + + // unknown scheme + ctx = testingconfig.CreateAccountContext(t, cfg) + s.registry = NewServiceRegistry() + _, err = s.Derive(ctx, payload) + assert.Error(t, err) + assert.True(t, errors.IsOfType(ErrDocumentSchemeUnknown, err)) + + // derive failed + doc := new(MockModel) + docSrv := new(MockService) + docSrv.On("New", scheme).Return(doc, nil) + doc.On("DeriveFromCreatePayload", mock.Anything, mock.Anything).Return(errors.New("derive failed")).Once() + assert.NoError(t, s.registry.Register(scheme, docSrv)) + _, err = s.Derive(ctx, payload) + assert.Error(t, err) + assert.True(t, errors.IsOfType(ErrDocumentInvalid, err)) + + // create successful + doc.On("DeriveFromCreatePayload", mock.Anything, mock.Anything).Return(nil).Once() + gdoc, err := s.Derive(ctx, payload) + assert.NoError(t, err) + assert.Equal(t, doc, gdoc) + + // missing old version + docID := utils.RandomSlice(32) + repo := new(MockRepository) + repo.On("GetLatest", did[:], docID).Return(nil, ErrDocumentNotFound).Once() + s.repo = repo + payload.DocumentID = docID + _, err = s.Derive(ctx, payload) + assert.Error(t, err) + assert.True(t, errors.IsOfType(ErrDocumentNotFound, err)) + + // invalid type + doc.On("Scheme").Return("invalid").Once() + repo.On("GetLatest", did[:], docID).Return(doc, nil) + _, err = s.Derive(ctx, payload) + assert.Error(t, err) + assert.True(t, errors.IsOfType(ErrDocumentInvalidType, err)) + + // DeriveFromUpdatePayload failed + doc.On("Scheme").Return(scheme) + doc.On("DeriveFromUpdatePayload", mock.Anything, payload).Return(nil, ErrDocumentInvalid).Once() + _, err = s.Derive(ctx, payload) + assert.Error(t, err) + assert.True(t, errors.IsOfType(ErrDocumentInvalid, err)) + + // success + doc.On("DeriveFromUpdatePayload", mock.Anything, payload).Return(doc, nil).Once() + gdoc, err = s.Derive(ctx, payload) + assert.NoError(t, err) + assert.Equal(t, gdoc, doc) + doc.AssertExpectations(t) + repo.AssertExpectations(t) + docSrv.AssertExpectations(t) +} diff --git a/documents/test_bootstrapper.go b/documents/test_bootstrapper.go deleted file mode 100644 index 09514d8ef..000000000 --- a/documents/test_bootstrapper.go +++ /dev/null @@ -1,27 +0,0 @@ -// +build integration unit - -package documents - -import ( - "github.com/centrifuge/go-centrifuge/errors" - "github.com/centrifuge/go-centrifuge/storage" -) - -func (b Bootstrapper) TestBootstrap(context map[string]interface{}) error { - if _, ok := context[storage.BootstrappedDB]; !ok { - return errors.New("initializing LevelDB repository failed") - } - return b.Bootstrap(context) -} - -func (Bootstrapper) TestTearDown() error { - return nil -} - -func (b PostBootstrapper) TestBootstrap(ctx map[string]interface{}) error { - return b.Bootstrap(ctx) -} - -func (PostBootstrapper) TestTearDown() error { - return nil -} diff --git a/documents/test_coredocument.go b/documents/test_coredocument.go deleted file mode 100644 index 9df95fe05..000000000 --- a/documents/test_coredocument.go +++ /dev/null @@ -1,14 +0,0 @@ -// +build integration unit - -package documents - -import ( - "github.com/centrifuge/centrifuge-protobufs/gen/go/coredocument" -) - -// GetTestCoreDocWithReset must only be used by tests for manipulations. It gets the embedded coredoc protobuf. -// All calls to this function will cause a regeneration of salts next time for precise-proof trees. -func (cd *CoreDocument) GetTestCoreDocWithReset() *coredocumentpb.CoreDocument { - cd.Modified = true - return &cd.Document -} diff --git a/documents/test_documents.go b/documents/test_documents.go new file mode 100644 index 000000000..a0887d227 --- /dev/null +++ b/documents/test_documents.go @@ -0,0 +1,242 @@ +// +build integration unit testworld + +package documents + +import ( + "context" + "time" + + "github.com/centrifuge/centrifuge-protobufs/gen/go/coredocument" + "github.com/centrifuge/go-centrifuge/errors" + "github.com/centrifuge/go-centrifuge/identity" + "github.com/centrifuge/go-centrifuge/storage" + "github.com/stretchr/testify/mock" +) + +// GetTestCoreDocWithReset must only be used by tests for manipulations. It gets the embedded coredoc protobuf. +// All calls to this function will cause a regeneration of salts next time for precise-proof trees. +func (cd *CoreDocument) GetTestCoreDocWithReset() *coredocumentpb.CoreDocument { + cd.Modified = true + return &cd.Document +} + +type MockModel struct { + Model + mock.Mock +} + +func (m *MockModel) Scheme() string { + args := m.Called() + return args.String(0) +} + +func (m *MockModel) GetData() interface{} { + args := m.Called() + return args.Get(0) +} + +func (m *MockModel) PreviousVersion() []byte { + args := m.Called() + return args.Get(0).([]byte) +} + +func (m *MockModel) CurrentVersion() []byte { + args := m.Called() + return args.Get(0).([]byte) +} + +func (m *MockModel) CurrentVersionPreimage() []byte { + args := m.Called() + id, _ := args.Get(0).([]byte) + return id +} + +func (m *MockModel) PackCoreDocument() (coredocumentpb.CoreDocument, error) { + args := m.Called() + dm, _ := args.Get(0).(coredocumentpb.CoreDocument) + return dm, args.Error(1) +} + +func (m *MockModel) UnpackCoreDocument(cd coredocumentpb.CoreDocument) error { + args := m.Called(cd) + return args.Error(0) +} + +func (m *MockModel) JSON() ([]byte, error) { + args := m.Called() + data, _ := args.Get(0).([]byte) + return data, args.Error(1) +} + +type MockService struct { + Service + mock.Mock +} + +func (m *MockService) GetVersion(ctx context.Context, documentID []byte, version []byte) (Model, error) { + args := m.Called(documentID, version) + doc, _ := args.Get(0).(Model) + return doc, args.Error(1) +} + +func (m *MockService) Derive(ctx context.Context, payload UpdatePayload) (Model, error) { + args := m.Called(ctx, payload) + doc, _ := args.Get(0).(Model) + return doc, args.Error(1) +} + +func (m *MockService) Validate(ctx context.Context, model Model, old Model) error { + args := m.Called(ctx, model, old) + return args.Error(0) +} + +func (m *MockService) New(scheme string) (Model, error) { + args := m.Called(scheme) + doc, _ := args.Get(0).(Model) + return doc, args.Error(1) +} + +func (m *MockModel) ID() []byte { + args := m.Called() + id, _ := args.Get(0).([]byte) + return id +} + +func (m *MockModel) NFTs() []*coredocumentpb.NFT { + args := m.Called() + dr, _ := args.Get(0).([]*coredocumentpb.NFT) + return dr +} + +func (m *MockModel) Author() (identity.DID, error) { + args := m.Called() + id, _ := args.Get(0).(identity.DID) + return id, args.Error(1) +} + +func (m *MockModel) Timestamp() (time.Time, error) { + args := m.Called() + dr, _ := args.Get(0).(time.Time) + return dr, args.Error(1) +} + +func (m *MockModel) GetCollaborators(filterIDs ...identity.DID) (CollaboratorsAccess, error) { + args := m.Called(filterIDs) + cas, _ := args.Get(0).(CollaboratorsAccess) + return cas, args.Error(1) +} + +func (m *MockModel) GetAttributes() []Attribute { + args := m.Called() + attrs, _ := args.Get(0).([]Attribute) + return attrs +} + +func (m *MockModel) IsDIDCollaborator(did identity.DID) (bool, error) { + args := m.Called(did) + ok, _ := args.Get(0).(bool) + return ok, args.Error(1) +} + +func (m *MockModel) GetAccessTokens() ([]*coredocumentpb.AccessToken, error) { + args := m.Called() + ac, _ := args.Get(0).([]*coredocumentpb.AccessToken) + return ac, args.Error(1) +} + +func (m *MockModel) AttributeExists(key AttrKey) bool { + args := m.Called(key) + return args.Bool(0) +} + +func (m *MockModel) GetAttribute(key AttrKey) (Attribute, error) { + args := m.Called(key) + attr, _ := args.Get(0).(Attribute) + return attr, args.Error(1) +} + +func (m *MockModel) AddAttributes(ca CollaboratorsAccess, prepareNewVersion bool, attrs ...Attribute) error { + args := m.Called(ca, prepareNewVersion, attrs) + return args.Error(0) +} + +func (m *MockModel) GetStatus() Status { + args := m.Called() + return args.Get(0).(Status) +} + +func (m *MockModel) SetStatus(st Status) error { + args := m.Called(st) + return args.Error(0) +} + +func (m *MockModel) Patch(payload UpdatePayload) error { + args := m.Called(payload) + return args.Error(0) +} + +func (m *MockModel) DeriveFromCreatePayload(ctx context.Context, payload CreatePayload) error { + args := m.Called(ctx, payload) + return args.Error(0) +} + +func (m *MockModel) DeriveFromUpdatePayload(ctx context.Context, payload UpdatePayload) (Model, error) { + args := m.Called(ctx, payload) + doc, _ := args.Get(0).(Model) + return doc, args.Error(1) +} + +type MockRepository struct { + mock.Mock +} + +func (m *MockRepository) Exists(accountID, id []byte) bool { + args := m.Called(accountID, id) + return args.Get(0).(bool) +} + +func (m *MockRepository) Get(accountID, id []byte) (Model, error) { + args := m.Called(accountID, id) + doc, _ := args.Get(0).(Model) + return doc, args.Error(0) +} + +func (m *MockRepository) Create(accountID, id []byte, model Model) error { + args := m.Called(accountID, id) + return args.Error(0) +} + +func (m *MockRepository) Update(accountID, id []byte, model Model) error { + args := m.Called(accountID, id) + return args.Error(0) +} + +func (m *MockRepository) Register(model Model) { + m.Called(model) + return +} + +func (m *MockRepository) GetLatest(accountID, docID []byte) (Model, error) { + args := m.Called(accountID, docID) + doc, _ := args.Get(0).(Model) + return doc, args.Error(1) +} + +func (b Bootstrapper) TestBootstrap(context map[string]interface{}) error { + if _, ok := context[storage.BootstrappedDB]; !ok { + return errors.New("initializing LevelDB repository failed") + } + return b.Bootstrap(context) +} + +func (Bootstrapper) TestTearDown() error { + return nil +} + +func (b PostBootstrapper) TestBootstrap(ctx map[string]interface{}) error { + return b.Bootstrap(ctx) +} + +func (PostBootstrapper) TestTearDown() error { + return nil +} diff --git a/documents/tree.go b/documents/tree.go index c582d94bd..fd3dbe1fa 100644 --- a/documents/tree.go +++ b/documents/tree.go @@ -4,6 +4,7 @@ import ( "bytes" "crypto/rand" "crypto/sha256" + "hash" "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/precise-proofs/proofs" @@ -11,20 +12,20 @@ import ( ) // DefaultTreeWithPrefix returns a DocumentTree with default opts passing a prefix to the tree leaves -func (cd *CoreDocument) DefaultTreeWithPrefix(prefix string, compactPrefix []byte) *proofs.DocumentTree { +func (cd *CoreDocument) DefaultTreeWithPrefix(prefix string, compactPrefix []byte) (*proofs.DocumentTree, error) { var prop proofs.Property if prefix != "" { prop = NewLeafProperty(prefix, compactPrefix) } - t := proofs.NewDocumentTree(proofs.TreeOptions{ + t, err := proofs.NewDocumentTree(proofs.TreeOptions{ CompactProperties: true, EnableHashSorting: true, Hash: sha256.New(), ParentPrefix: prop, Salts: cd.DocumentSaltsFunc(), }) - return &t + return &t, err } // NewLeafProperty returns a proof property with the literal and the compact @@ -64,3 +65,22 @@ func (cd *CoreDocument) DocumentSaltsFunc() func(compact []byte) ([]byte, error) return randbytes, nil } } + +// ValidateProof by comparing it to the provided rootHash +func ValidateProof(proof *proofspb.Proof, rootHash []byte, hashFunc hash.Hash) (valid bool, err error) { + var fieldHash []byte + if len(proof.Hash) == 0 { + fieldHash, err = proofs.CalculateHashForProofField(proof, hashFunc) + } else { + fieldHash = proof.Hash + } + if err != nil { + return false, err + } + if len(proof.SortedHashes) > 0 { + valid, err = proofs.ValidateProofSortedHashes(fieldHash, proof.SortedHashes, rootHash, hashFunc) + } else { + valid, err = proofs.ValidateProofHashes(fieldHash, proof.Hashes, rootHash, hashFunc) + } + return valid, err +} diff --git a/documents/types.go b/documents/types.go new file mode 100644 index 000000000..5f9c05269 --- /dev/null +++ b/documents/types.go @@ -0,0 +1,39 @@ +package documents + +import ( + "github.com/centrifuge/go-centrifuge/utils/byteutils" + proofspb "github.com/centrifuge/precise-proofs/proofs/proto" +) + +// Proof represents a single proof +type Proof struct { + Property byteutils.HexBytes `json:"property" swaggertype:"primitive,string"` + Value byteutils.HexBytes `json:"value" swaggertype:"primitive,string"` + Salt byteutils.HexBytes `json:"salt" swaggertype:"primitive,string"` + Hash byteutils.HexBytes `json:"hash" swaggertype:"primitive,string"` + SortedHashes []byteutils.HexBytes `json:"sorted_hashes" swaggertype:"array,string"` +} + +// ConvertProofs converts proto proofs to JSON struct +func ConvertProofs(fieldProofs []*proofspb.Proof) []Proof { + var proofs []Proof + for _, pf := range fieldProofs { + pff := Proof{ + Value: pf.Value, + Hash: pf.Hash, + Salt: pf.Salt, + Property: pf.GetCompactName(), + } + + var hashes []byteutils.HexBytes + for _, h := range pf.SortedHashes { + h := h + hashes = append(hashes, h) + } + + pff.SortedHashes = hashes + proofs = append(proofs, pff) + } + + return proofs +} diff --git a/documents/types_test.go b/documents/types_test.go new file mode 100644 index 000000000..44d901752 --- /dev/null +++ b/documents/types_test.go @@ -0,0 +1,49 @@ +// +build unit + +package documents + +import ( + "testing" + + "github.com/centrifuge/go-centrifuge/utils" + proofspb "github.com/centrifuge/precise-proofs/proofs/proto" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/stretchr/testify/assert" +) + +func TestConvertProofs(t *testing.T) { + // nil input + pfs := ConvertProofs(nil) + assert.Empty(t, pfs) + + var input []*proofspb.Proof + p0 := &proofspb.Proof{ + Property: &proofspb.Proof_CompactName{CompactName: utils.RandomSlice(32)}, + Value: utils.RandomSlice(32), + Salt: utils.RandomSlice(32), + Hash: []byte{}, + SortedHashes: [][]byte{ + utils.RandomSlice(32), + utils.RandomSlice(32), + }, + } + p1 := &proofspb.Proof{ + Property: &proofspb.Proof_CompactName{CompactName: utils.RandomSlice(32)}, + Value: utils.RandomSlice(32), + Salt: utils.RandomSlice(32), + Hash: []byte{}, + SortedHashes: [][]byte{ + utils.RandomSlice(32), + utils.RandomSlice(32), + }, + } + input = append(input, p0) + input = append(input, p1) + + pfs = ConvertProofs(input) + assert.Len(t, pfs, 2) + assert.Equal(t, hexutil.Encode(p0.GetCompactName()), pfs[0].Property.String()) + assert.Equal(t, hexutil.Encode(p1.GetCompactName()), pfs[1].Property.String()) + assert.Len(t, pfs[0].SortedHashes, 2) + assert.Equal(t, hexutil.Encode(p0.SortedHashes[0]), pfs[0].SortedHashes[0].String()) +} diff --git a/documents/validator.go b/documents/validator.go index 293d4fde5..c0e3366fd 100644 --- a/documents/validator.go +++ b/documents/validator.go @@ -100,6 +100,15 @@ func versionIDsValidator() Validator { }) } +// CreateVersionValidator validates if the new core document is properly derived from old one +func CreateVersionValidator(repo anchors.AnchorRepository) Validator { + return ValidatorGroup{ + baseValidator(), + currentVersionValidator(repo), + LatestVersionValidator(repo), + } +} + // UpdateVersionValidator validates if the new core document is properly derived from old one func UpdateVersionValidator(repo anchors.AnchorRepository) Validator { return ValidatorGroup{ diff --git a/documents/validator_test.go b/documents/validator_test.go index efe35e757..812c32c58 100644 --- a/documents/validator_test.go +++ b/documents/validator_test.go @@ -169,6 +169,11 @@ func TestUpdateVersionValidator(t *testing.T) { assert.Len(t, uvv, 3) } +func TestCreateVersionValidator(t *testing.T) { + uvv := CreateVersionValidator(nil) + assert.Len(t, uvv, 3) +} + func TestValidator_baseValidator(t *testing.T) { bv := baseValidator() @@ -397,8 +402,7 @@ func TestValidator_SignatureValidator(t *testing.T) { sv = SignatureValidator(idService, repo) s, err = account.SignMsg(sr) assert.NoError(t, err) - acID, err := account.GetIdentityID() - assert.NoError(t, err) + acID := account.GetIdentityID() did1, err = identity.NewDIDFromBytes(acID) assert.NoError(t, err) model = new(mockModel) diff --git a/documents/write_acls_test.go b/documents/write_acls_test.go index f88b5bb3b..876ab93e3 100644 --- a/documents/write_acls_test.go +++ b/documents/write_acls_test.go @@ -315,7 +315,7 @@ func getTree(t *testing.T, doc proto.Message, prefix string, compact []byte) *pr Compact: compact, } } - tr := proofs.NewDocumentTree(proofs.TreeOptions{ + tr, err := proofs.NewDocumentTree(proofs.TreeOptions{ ParentPrefix: prop, CompactProperties: true, EnableHashSorting: true, @@ -324,7 +324,7 @@ func getTree(t *testing.T, doc proto.Message, prefix string, compact []byte) *pr return utils.RandomSlice(32), nil }, }) - + assert.NoError(t, err) tree := &tr assert.NoError(t, tree.AddLeavesFromDocument(doc)) assert.NoError(t, tree.Generate()) diff --git a/ethereum/transaction_status_task_integration_test.go b/ethereum/transaction_status_task_integration_test.go index 5a0b796d7..1203ead66 100644 --- a/ethereum/transaction_status_task_integration_test.go +++ b/ethereum/transaction_status_task_integration_test.go @@ -15,7 +15,7 @@ import ( "github.com/stretchr/testify/assert" ) -func enqueueJob(t *testing.T, txHash string) (jobs.Manager, identity.DID, jobs.JobID, chan bool) { +func enqueueJob(t *testing.T, txHash string) (jobs.Manager, identity.DID, jobs.JobID, chan error) { queueSrv := ctx[bootstrap.BootstrappedQueueServer].(*queue.Server) jobManager := ctx[jobs.BootstrappedService].(jobs.Manager) @@ -45,7 +45,7 @@ func TestTransactionStatusTask_successful(t *testing.T) { txManager, cid, tx, result := enqueueJob(t, "0x1") r := <-result - assert.True(t, r) + assert.NoError(t, r) trans, err := txManager.GetJob(cid, tx) assert.Nil(t, err, "a transaction should be returned") assert.Equal(t, string(jobs.Success), string(trans.Status), "transaction should be successful") @@ -56,7 +56,7 @@ func TestTransactionStatusTask_failed(t *testing.T) { txManager, cid, tx, result := enqueueJob(t, "0x2") r := <-result - assert.True(t, r) + assert.Error(t, r) trans, err := txManager.GetJob(cid, tx) assert.Nil(t, err, "a centrifuge transaction should be returned") assert.Equal(t, string(jobs.Failed), string(trans.Status), "transaction should fail") diff --git a/extensions/attribute_utils.go b/extensions/attribute_utils.go index 32849d693..dd313705d 100644 --- a/extensions/attribute_utils.go +++ b/extensions/attribute_utils.go @@ -66,7 +66,7 @@ func IncrementArrayAttrIDX(model documents.Model, typeLabel string) (attr docume } if !model.AttributeExists(key) { - return documents.NewAttribute(typeLabel, documents.AttrInt256, "0") + return documents.NewStringAttribute(typeLabel, documents.AttrInt256, "0") } idx, err := GetArrayLatestIDX(model, typeLabel) @@ -76,12 +76,11 @@ func IncrementArrayAttrIDX(model documents.Model, typeLabel string) (attr docume // increment idx newIdx, err := idx.Inc() - if err != nil { return attr, err } - return documents.NewAttribute(typeLabel, documents.AttrInt256, newIdx.String()) + return documents.NewStringAttribute(typeLabel, documents.AttrInt256, newIdx.String()) } // FillAttributeList fills an attributes list from the JSON object @@ -96,7 +95,7 @@ func FillAttributeList(data interface{}, idx, fieldKey string) ([]documents.Attr label := LabelFromJSONTag(idx, jsonKey, fieldKey) attrType := types.Field(i).Tag.Get("attr") - attr, err := documents.NewAttribute(label, documents.AttributeType(attrType), value) + attr, err := documents.NewStringAttribute(label, documents.AttributeType(attrType), value) if err != nil { return nil, err } @@ -124,9 +123,7 @@ func CreateAttributesList(current documents.Model, data interface{}, fieldKey, t // add updated idx attributes = append(attributes, idx) - return attributes, nil - } // DeleteAttributesSet deletes attributes that already exist on a given model for the addition of new attributes to the set @@ -193,21 +190,21 @@ func FindAttributeSetIDX(model documents.Model, attributeSetID, typeLabel, idLab } // ToMapAttributes converts an array of documents.Attributes to a map -func ToMapAttributes(attrs []documents.Attribute) (map[documents.AttrKey]documents.Attribute, error) { +func ToMapAttributes(attrs []documents.Attribute) map[documents.AttrKey]documents.Attribute { if len(attrs) < 1 { - return nil, nil + return nil } m := make(map[documents.AttrKey]documents.Attribute) for _, v := range attrs { m[v.Key] = documents.Attribute{ - KeyLabel: v.Key.String(), + KeyLabel: v.KeyLabel, Key: v.Key, Value: v.Value, } } - return m, nil + return m } // TODO: placeholder below for generic finding and deriving data from attribute sets diff --git a/extensions/attribute_utils_test.go b/extensions/attribute_utils_test.go index 6e88dfa08..444b2ff10 100644 --- a/extensions/attribute_utils_test.go +++ b/extensions/attribute_utils_test.go @@ -5,6 +5,8 @@ package extensions import ( "testing" + "github.com/centrifuge/go-centrifuge/documents" + "github.com/centrifuge/go-centrifuge/utils" "github.com/stretchr/testify/assert" ) @@ -24,3 +26,25 @@ func TestLabelFromJSONTag(t *testing.T) { assert.Equal(t, "testing_test", LabelFromJSONTag(idxKey, "test", "testing_")) assert.Equal(t, "test_agreement[{IDX}].json", LabelFromJSONTag(idxKey, testJSONKey, testFieldKey)) } + +func TestToMapAttributes(t *testing.T) { + attrs := []documents.Attribute{ + { + KeyLabel: "test_details[0].something", + Key: utils.RandomByte32(), + Value: documents.AttrVal{Str: "whatever"}, + }, + { + KeyLabel: "test_details[0].else", + Key: utils.RandomByte32(), + Value: documents.AttrVal{Str: "nope"}, + }, + } + mapAttr := ToMapAttributes(nil) + assert.Empty(t, mapAttr) + mapAttr = ToMapAttributes(attrs) + assert.NotEmpty(t, mapAttr[attrs[0].Key]) + assert.Equal(t, attrs[0].KeyLabel, mapAttr[attrs[0].Key].KeyLabel) + assert.NotEmpty(t, mapAttr[attrs[1].Key]) + assert.Equal(t, attrs[1].KeyLabel, mapAttr[attrs[1].Key].KeyLabel) +} diff --git a/extensions/funding/bootstrapper.go b/extensions/funding/bootstrapper.go index 2aeb6a06a..2b10097d9 100644 --- a/extensions/funding/bootstrapper.go +++ b/extensions/funding/bootstrapper.go @@ -2,14 +2,13 @@ package funding import ( "github.com/centrifuge/go-centrifuge/bootstrap" - "github.com/centrifuge/go-centrifuge/config" "github.com/centrifuge/go-centrifuge/documents" "github.com/centrifuge/go-centrifuge/errors" ) const ( - // BootstrappedFundingAPIHandler is the key for the api handler in Context - BootstrappedFundingAPIHandler = "Funding API Handler" + // BootstrappedFundingService is the key for Funding service in Context. + BootstrappedFundingService = "Funding Service" ) // Bootstrapper implements Bootstrapper Interface @@ -23,11 +22,6 @@ func (Bootstrapper) Bootstrap(ctx map[string]interface{}) (err error) { } }() - cfgSrv, ok := ctx[config.BootstrappedConfigStorage].(config.Service) - if !ok { - return errors.New("config service not initialised") - } - docSrv, ok := ctx[documents.BootstrappedDocumentService].(documents.Service) if !ok { return errors.New("document service not initialised") @@ -39,7 +33,6 @@ func (Bootstrapper) Bootstrap(ctx map[string]interface{}) (err error) { } srv := DefaultService(docSrv, tokenRegistry) - handler := GRPCHandler(cfgSrv, srv) - ctx[BootstrappedFundingAPIHandler] = handler + ctx[BootstrappedFundingService] = srv return nil } diff --git a/extensions/funding/bootstrapper_test.go b/extensions/funding/bootstrapper_test.go index 6452d87d4..6ab1a84d1 100644 --- a/extensions/funding/bootstrapper_test.go +++ b/extensions/funding/bootstrapper_test.go @@ -6,8 +6,6 @@ import ( "testing" "github.com/centrifuge/go-centrifuge/bootstrap" - "github.com/centrifuge/go-centrifuge/config" - "github.com/centrifuge/go-centrifuge/config/configstore" "github.com/centrifuge/go-centrifuge/documents" "github.com/centrifuge/go-centrifuge/testingutils/documents" "github.com/stretchr/testify/assert" @@ -17,19 +15,13 @@ func TestBootstrapper_Bootstrap(t *testing.T) { ctx := make(map[string]interface{}) b := Bootstrapper{} - // missing config service - err := b.Bootstrap(ctx) - assert.Error(t, err) - assert.Contains(t, err.Error(), "funding bootstrapper: config service not initialised") - // missing doc service - ctx[config.BootstrappedConfigStorage] = new(configstore.MockService) - err = b.Bootstrap(ctx) + err := b.Bootstrap(ctx) assert.Error(t, err) assert.Contains(t, err.Error(), "document service not initialised") - // missing identity service - ctx[documents.BootstrappedDocumentService] = new(mockService) + // missing token service + ctx[documents.BootstrappedDocumentService] = new(testingdocuments.MockService) err = b.Bootstrap(ctx) assert.Error(t, err) assert.Contains(t, err.Error(), "token registry not initialisation") @@ -38,5 +30,5 @@ func TestBootstrapper_Bootstrap(t *testing.T) { ctx[bootstrap.BootstrappedInvoiceUnpaid] = new(testingdocuments.MockRegistry) err = b.Bootstrap(ctx) assert.NoError(t, err) - assert.NotNil(t, ctx[BootstrappedFundingAPIHandler]) + assert.NotNil(t, ctx[BootstrappedFundingService]) } diff --git a/extensions/funding/converters.go b/extensions/funding/converters.go deleted file mode 100644 index 7d9bff2cc..000000000 --- a/extensions/funding/converters.go +++ /dev/null @@ -1,35 +0,0 @@ -package funding - -import ( - "reflect" - - "github.com/centrifuge/go-centrifuge/protobufs/gen/go/funding" -) - -// TODO: generic? Create something like ServiceRegistry for Custom Attributes... -func (f *Data) initFundingFromData(data *funpb.FundingData) { - types := reflect.TypeOf(*f) - values := reflect.ValueOf(*data) - for i := 0; i < types.NumField(); i++ { - n := types.Field(i).Name - v := values.FieldByName(n).Interface().(string) - // converter assumes string struct fields - reflect.ValueOf(f).Elem().FieldByName(n).SetString(v) - - } -} - -// TODO: generic? -func (f *Data) getClientData() *funpb.FundingData { - clientData := new(funpb.FundingData) - types := reflect.TypeOf(*f) - values := reflect.ValueOf(*f) - for i := 0; i < types.NumField(); i++ { - n := types.Field(i).Name - v := values.FieldByName(n).Interface().(string) - // converter assumes string struct fields - reflect.ValueOf(clientData).Elem().FieldByName(n).SetString(v) - - } - return clientData -} diff --git a/extensions/funding/funding.go b/extensions/funding/funding.go index 2ca168450..5812b0fd1 100644 --- a/extensions/funding/funding.go +++ b/extensions/funding/funding.go @@ -1,11 +1,10 @@ -//nolint package funding -// Data is the default funding extension schema +// Data is the default funding extension schema. type Data struct { - AgreementId string `json:"agreement_id,omitempty" attr:"bytes"` - BorrowerId string `json:"borrower_id,omitempty" attr:"bytes"` - FunderId string `json:"funder_id,omitempty" attr:"bytes"` + AgreementID string `json:"agreement_id,omitempty" attr:"bytes"` + BorrowerID string `json:"borrower_id,omitempty" attr:"bytes"` + FunderID string `json:"funder_id,omitempty" attr:"bytes"` Amount string `json:"amount,omitempty" attr:"decimal"` Apr string `json:"apr,omitempty" attr:"string"` Days string `json:"days,omitempty" attr:"integer"` @@ -14,6 +13,14 @@ type Data struct { RepaymentOccurredDate string `json:"repayment_occurred_date,omitempty" attr:"timestamp"` RepaymentAmount string `json:"repayment_amount,omitempty" attr:"decimal"` Currency string `json:"currency,omitempty" attr:"string"` - NftAddress string `json:"nft_address,omitempty" attr:"bytes"` - PaymentDetailsId string `json:"payment_details_id,omitempty" attr:"bytes"` + NFTAddress string `json:"nft_address,omitempty" attr:"bytes"` + PaymentDetailsID string `json:"payment_details_id,omitempty" attr:"bytes"` +} + +// Signature is the funding agreement Signature. +type Signature struct { + Valid string `json:"valid"` + OutdatedSignature string `json:"outdated_signature"` + Identity string `json:"identity"` + SignedVersion string `json:"signed_version"` } diff --git a/extensions/funding/funding_test.go b/extensions/funding/funding_test.go deleted file mode 100644 index 41bb7d23a..000000000 --- a/extensions/funding/funding_test.go +++ /dev/null @@ -1,18 +0,0 @@ -// +build unit - -package funding - -import ( - "testing" - - clientfunpb "github.com/centrifuge/go-centrifuge/protobufs/gen/go/funding" - "github.com/stretchr/testify/assert" -) - -func TestInitFundingFromData(t *testing.T) { - fdc := &clientfunpb.FundingData{Currency: "eur"} - fd := new(Data) - fd.initFundingFromData(fdc) - assert.Equal(t, fdc.Currency, fd.Currency) - -} diff --git a/extensions/funding/handler.go b/extensions/funding/handler.go deleted file mode 100644 index 1efbe06b6..000000000 --- a/extensions/funding/handler.go +++ /dev/null @@ -1,268 +0,0 @@ -package funding - -import ( - "context" - - "github.com/centrifuge/go-centrifuge/config" - "github.com/centrifuge/go-centrifuge/contextutil" - "github.com/centrifuge/go-centrifuge/documents" - "github.com/centrifuge/go-centrifuge/errors" - "github.com/centrifuge/go-centrifuge/extensions" - clientfunpb "github.com/centrifuge/go-centrifuge/protobufs/gen/go/funding" - "github.com/ethereum/go-ethereum/common/hexutil" - logging "github.com/ipfs/go-log" -) - -var apiLog = logging.Logger("funding-api") - -// grpcHandler handles all the funding extension related actions -type grpcHandler struct { - service Service - config config.Service -} - -// GRPCHandler returns an implementation of entity.DocumentServiceServer -func GRPCHandler(config config.Service, srv Service) clientfunpb.FundingServiceServer { - return &grpcHandler{ - service: srv, - config: config, - } -} - -// Create handles a new funding document extension and adds it to an existing document -func (h *grpcHandler) Create(ctx context.Context, req *clientfunpb.FundingCreatePayload) (*clientfunpb.FundingResponse, error) { - apiLog.Debugf("create funding request %v", req) - ctxHeader, err := contextutil.Context(ctx, h.config) - if err != nil { - apiLog.Error(err) - return nil, err - } - - // create new funding id - if req.Data.AgreementId == "" { - req.Data.AgreementId = extensions.NewAttributeSetID() - } else { - _, err := hexutil.Decode(req.Data.AgreementId) - if err != nil { - apiLog.Error(err) - return nil, extensions.ErrAttributeSetID - } - } - - // returns model with added funding custom fields - model, err := h.service.DeriveFromPayload(ctxHeader, req) - if err != nil { - return nil, errors.NewTypedError(extensions.ErrPayload, err) - } - - model, jobID, _, err := h.service.Update(ctxHeader, model) - if err != nil { - apiLog.Error(err) - return nil, err - } - - resp, err := h.service.DeriveFundingResponse(ctxHeader, model, req.Data.AgreementId) - if err != nil { - apiLog.Error(err) - return nil, errors.NewTypedError(extensions.ErrDeriveAttr, err) - } - - resp.Header.JobId = jobID.String() - return resp, nil -} - -// Get returns a funding agreement from an existing document -func (h *grpcHandler) Get(ctx context.Context, req *clientfunpb.Request) (*clientfunpb.FundingResponse, error) { - apiLog.Debugf("Get request %v", req) - ctxHeader, err := contextutil.Context(ctx, h.config) - if err != nil { - apiLog.Error(err) - return nil, err - } - - identifier, err := hexutil.Decode(req.DocumentId) - if err != nil { - apiLog.Error(err) - return nil, documents.ErrDocumentIdentifier - } - - model, err := h.service.GetCurrentVersion(ctxHeader, identifier) - if err != nil { - apiLog.Error(err) - return nil, documents.ErrDocumentNotFound - } - - resp, err := h.service.DeriveFundingResponse(ctxHeader, model, req.AgreementId) - if err != nil { - apiLog.Error(err) - return nil, extensions.ErrDeriveAttr - } - return resp, nil -} - -// Sign adds a funding signature to a document -func (h *grpcHandler) Sign(ctx context.Context, req *clientfunpb.Request) (*clientfunpb.FundingResponse, error) { - apiLog.Debugf("create funding request %v", req) - ctxHeader, err := contextutil.Context(ctx, h.config) - if err != nil { - apiLog.Error(err) - return nil, err - } - - identifier, err := hexutil.Decode(req.DocumentId) - if err != nil { - apiLog.Error(err) - return nil, documents.ErrDocumentIdentifier - } - - // returns model with a signature - model, err := h.service.Sign(ctxHeader, req.AgreementId, identifier) - if err != nil { - return nil, errors.NewTypedError(extensions.ErrPayload, err) - } - - model, jobID, _, err := h.service.Update(ctxHeader, model) - if err != nil { - apiLog.Error(err) - return nil, err - } - - resp, err := h.service.DeriveFundingResponse(ctxHeader, model, req.AgreementId) - if err != nil { - apiLog.Error(err) - return nil, errors.NewTypedError(extensions.ErrDeriveAttr, err) - } - - resp.Header.JobId = jobID.String() - return resp, nil -} - -// Get returns a funding agreement from an existing document -func (h *grpcHandler) GetVersion(ctx context.Context, req *clientfunpb.GetVersionRequest) (*clientfunpb.FundingResponse, error) { - apiLog.Debugf("Get request %v", req) - ctxHeader, err := contextutil.Context(ctx, h.config) - if err != nil { - apiLog.Error(err) - return nil, err - } - - identifier, err := hexutil.Decode(req.DocumentId) - if err != nil { - apiLog.Error(err) - return nil, documents.ErrDocumentIdentifier - } - - version, err := hexutil.Decode(req.VersionId) - if err != nil { - apiLog.Error(err) - return nil, documents.ErrDocumentVersion - } - - model, err := h.service.GetVersion(ctxHeader, identifier, version) - if err != nil { - apiLog.Error(err) - return nil, documents.ErrDocumentVersionNotFound - } - - resp, err := h.service.DeriveFundingResponse(ctxHeader, model, req.AgreementId) - if err != nil { - apiLog.Error(err) - return nil, extensions.ErrDeriveAttr - } - return resp, nil -} - -// GetList returns all funding agreements of a existing document -func (h *grpcHandler) GetList(ctx context.Context, req *clientfunpb.GetListRequest) (*clientfunpb.FundingListResponse, error) { - apiLog.Debugf("Get request %v", req) - ctxHeader, err := contextutil.Context(ctx, h.config) - if err != nil { - apiLog.Error(err) - return nil, err - } - - identifier, err := hexutil.Decode(req.DocumentId) - if err != nil { - apiLog.Error(err) - return nil, documents.ErrDocumentIdentifier - } - - model, err := h.service.GetCurrentVersion(ctxHeader, identifier) - if err != nil { - apiLog.Error(err) - return nil, documents.ErrDocumentNotFound - } - - resp, err := h.service.DeriveFundingListResponse(ctxHeader, model) - if err != nil { - apiLog.Error(err) - return nil, extensions.ErrDeriveAttr - } - return resp, nil -} - -// GetList returns all funding agreements of a existing document -func (h *grpcHandler) GetListVersion(ctx context.Context, req *clientfunpb.GetListVersionRequest) (*clientfunpb.FundingListResponse, error) { - apiLog.Debugf("Get request %v", req) - ctxHeader, err := contextutil.Context(ctx, h.config) - if err != nil { - apiLog.Error(err) - return nil, err - } - - identifier, err := hexutil.Decode(req.DocumentId) - if err != nil { - apiLog.Error(err) - return nil, documents.ErrDocumentIdentifier - } - - version, err := hexutil.Decode(req.VersionId) - if err != nil { - apiLog.Error(err) - return nil, documents.ErrDocumentVersion - } - - model, err := h.service.GetVersion(ctxHeader, identifier, version) - if err != nil { - apiLog.Error(err) - return nil, documents.ErrDocumentNotFound - } - - resp, err := h.service.DeriveFundingListResponse(ctxHeader, model) - if err != nil { - apiLog.Error(err) - return nil, extensions.ErrDeriveAttr - } - return resp, nil -} - -// Update handles an update over an existing funding document extension -func (h *grpcHandler) Update(ctx context.Context, req *clientfunpb.FundingUpdatePayload) (*clientfunpb.FundingResponse, error) { - apiLog.Debugf("create funding request %v", req) - ctxHeader, err := contextutil.Context(ctx, h.config) - if err != nil { - apiLog.Error(err) - return nil, err - } - - // returns model with updated funding custom fields - model, err := h.service.DeriveFromUpdatePayload(ctxHeader, req) - if err != nil { - return nil, errors.NewTypedError(extensions.ErrPayload, err) - } - - model, jobID, _, err := h.service.Update(ctxHeader, model) - if err != nil { - apiLog.Error(err) - return nil, err - } - - resp, err := h.service.DeriveFundingResponse(ctxHeader, model, req.Data.AgreementId) - if err != nil { - apiLog.Error(err) - return nil, errors.NewTypedError(extensions.ErrPayload, err) - } - - resp.Header.JobId = jobID.String() - return resp, nil -} diff --git a/extensions/funding/handler_test.go b/extensions/funding/handler_test.go deleted file mode 100644 index 115e848cd..000000000 --- a/extensions/funding/handler_test.go +++ /dev/null @@ -1,163 +0,0 @@ -// +build unit - -package funding - -import ( - "context" - "testing" - - "github.com/centrifuge/go-centrifuge/config" - "github.com/centrifuge/go-centrifuge/contextutil" - "github.com/centrifuge/go-centrifuge/documents" - "github.com/centrifuge/go-centrifuge/jobs" - "github.com/centrifuge/go-centrifuge/protobufs/gen/go/document" - clientfunpb "github.com/centrifuge/go-centrifuge/protobufs/gen/go/funding" - "github.com/centrifuge/go-centrifuge/testingutils/config" - "github.com/centrifuge/go-centrifuge/testingutils/documents" - "github.com/centrifuge/go-centrifuge/utils" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - -type mockService struct { - Service - mock.Mock -} - -var configService config.Service - -func (m *mockService) DeriveFromPayload(ctx context.Context, req *clientfunpb.FundingCreatePayload) (documents.Model, error) { - args := m.Called(ctx, req) - model, _ := args.Get(0).(documents.Model) - return model, args.Error(1) -} - -func (m *mockService) DeriveFromUpdatePayload(ctx context.Context, req *clientfunpb.FundingUpdatePayload) (documents.Model, error) { - args := m.Called(ctx, req) - model, _ := args.Get(0).(documents.Model) - return model, args.Error(1) -} - -func (m *mockService) DeriveFundingResponse(ctx context.Context, doc documents.Model, fundingId string) (*clientfunpb.FundingResponse, error) { - args := m.Called(doc) - data, _ := args.Get(0).(*clientfunpb.FundingResponse) - return data, args.Error(1) -} - -func (m *mockService) DeriveFundingListResponse(ctx context.Context, doc documents.Model) (*clientfunpb.FundingListResponse, error) { - args := m.Called(doc) - data, _ := args.Get(0).(*clientfunpb.FundingListResponse) - return data, args.Error(1) -} - -func (m *mockService) Update(ctx context.Context, model documents.Model) (documents.Model, jobs.JobID, chan bool, error) { - args := m.Called(ctx, model) - doc1, _ := args.Get(0).(documents.Model) - return doc1, contextutil.Job(ctx), nil, args.Error(2) -} - -func (m *mockService) GetCurrentVersion(ctx context.Context, identifier []byte) (documents.Model, error) { - args := m.Called(ctx, identifier) - model, _ := args.Get(0).(documents.Model) - return model, args.Error(1) -} - -func (m *mockService) GetVersion(ctx context.Context, identifier, version []byte) (documents.Model, error) { - args := m.Called(ctx, identifier) - model, _ := args.Get(0).(documents.Model) - return model, args.Error(1) -} - -func (m *mockService) Sign(ctx context.Context, fundingID string, identifier []byte) (documents.Model, error) { - args := m.Called(ctx, fundingID, identifier) - model, _ := args.Get(0).(documents.Model) - return model, args.Error(1) -} - -func TestGRPCHandler_Create(t *testing.T) { - srv := &mockService{} - - h := &grpcHandler{service: srv, config: configService} - jobID := jobs.NewJobID() - - // successful - srv.On("DeriveFromPayload", mock.Anything, mock.Anything).Return(documents.ErrDocumentIdentifier, nil) - srv.On("Update", mock.Anything, mock.Anything).Return(nil, jobID, nil).Once() - srv.On("DeriveFundingResponse", mock.Anything, mock.Anything, mock.Anything).Return(&clientfunpb.FundingResponse{Header: new(documentpb.ResponseHeader)}, nil).Once() - response, err := h.Create(testingconfig.HandlerContext(configService), &clientfunpb.FundingCreatePayload{DocumentId: hexutil.Encode(utils.RandomSlice(32)), Data: &clientfunpb.FundingData{Currency: "eur"}}) - assert.NoError(t, err) - assert.NotNil(t, response) -} - -func TestGRPCHandler_Update(t *testing.T) { - srv := &mockService{} - - h := &grpcHandler{service: srv, config: configService} - jobID := jobs.NewJobID() - - // successful - srv.On("DeriveFromUpdatePayload", mock.Anything, mock.Anything).Return(&testingdocuments.MockModel{}, nil) - srv.On("Update", mock.Anything, mock.Anything).Return(nil, jobID, nil).Once() - srv.On("DeriveFundingResponse", mock.Anything, mock.Anything, mock.Anything).Return(&clientfunpb.FundingResponse{Header: new(documentpb.ResponseHeader)}, nil).Once() - - response, err := h.Update(testingconfig.HandlerContext(configService), &clientfunpb.FundingUpdatePayload{DocumentId: hexutil.Encode(utils.RandomSlice(32)), Data: &clientfunpb.FundingData{Currency: "eur"}}) - assert.NoError(t, err) - assert.NotNil(t, response) -} - -func TestGRPCHandler_Sign(t *testing.T) { - srv := &mockService{} - h := &grpcHandler{service: srv, config: configService} - jobID := jobs.NewJobID() - - // successful - srv.On("GetCurrentVersion", mock.Anything, mock.Anything).Return(&testingdocuments.MockModel{}, nil) - srv.On("DeriveFundingResponse", mock.Anything, mock.Anything, mock.Anything).Return(&clientfunpb.FundingResponse{Header: new(documentpb.ResponseHeader)}, nil).Once() - srv.On("Sign", mock.Anything, mock.Anything, mock.Anything).Return(&testingdocuments.MockModel{}, nil).Once() - srv.On("Update", mock.Anything, mock.Anything).Return(nil, jobID, nil).Once() - - response, err := h.Sign(testingconfig.HandlerContext(configService), &clientfunpb.Request{DocumentId: hexutil.Encode(utils.RandomSlice(32)), AgreementId: hexutil.Encode(utils.RandomSlice(32))}) - assert.NoError(t, err) - assert.NotNil(t, response) - - // fail - response, err = h.Sign(testingconfig.HandlerContext(configService), &clientfunpb.Request{AgreementId: hexutil.Encode(utils.RandomSlice(32))}) - assert.Error(t, err) -} - -func TestGRPCHandler_Get(t *testing.T) { - srv := &mockService{} - h := &grpcHandler{service: srv, config: configService} - - srv.On("GetCurrentVersion", mock.Anything, mock.Anything).Return(&testingdocuments.MockModel{}, nil) - srv.On("DeriveFundingResponse", mock.Anything, mock.Anything, mock.Anything).Return(&clientfunpb.FundingResponse{Header: new(documentpb.ResponseHeader)}, nil).Once() - - response, err := h.Get(testingconfig.HandlerContext(configService), &clientfunpb.Request{DocumentId: hexutil.Encode(utils.RandomSlice(32)), AgreementId: hexutil.Encode(utils.RandomSlice(32))}) - assert.NoError(t, err) - assert.NotNil(t, response) -} - -func TestGRPCHandler_GetVersion(t *testing.T) { - srv := &mockService{} - h := &grpcHandler{service: srv, config: configService} - - srv.On("GetVersion", mock.Anything, mock.Anything, mock.Anything).Return(&testingdocuments.MockModel{}, nil) - srv.On("DeriveFundingResponse", mock.Anything, mock.Anything, mock.Anything).Return(&clientfunpb.FundingResponse{Header: new(documentpb.ResponseHeader)}, nil).Once() - - response, err := h.GetVersion(testingconfig.HandlerContext(configService), &clientfunpb.GetVersionRequest{DocumentId: hexutil.Encode(utils.RandomSlice(32)), VersionId: hexutil.Encode(utils.RandomSlice(32)), AgreementId: hexutil.Encode(utils.RandomSlice(32))}) - assert.NoError(t, err) - assert.NotNil(t, response) -} - -func TestGRPCHandler_GetList(t *testing.T) { - srv := &mockService{} - h := &grpcHandler{service: srv, config: configService} - - srv.On("GetVersion", mock.Anything, mock.Anything, mock.Anything).Return(&testingdocuments.MockModel{}, nil) - srv.On("DeriveFundingListResponse", mock.Anything, mock.Anything).Return(&clientfunpb.FundingListResponse{Header: new(documentpb.ResponseHeader)}, nil).Once() - - response, err := h.GetListVersion(testingconfig.HandlerContext(configService), &clientfunpb.GetListVersionRequest{DocumentId: hexutil.Encode(utils.RandomSlice(32)), VersionId: hexutil.Encode(utils.RandomSlice(32))}) - assert.NoError(t, err) - assert.NotNil(t, response) -} diff --git a/extensions/funding/service.go b/extensions/funding/service.go index 99c369c63..ec18c9855 100644 --- a/extensions/funding/service.go +++ b/extensions/funding/service.go @@ -8,39 +8,41 @@ import ( "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/go-centrifuge/extensions" "github.com/centrifuge/go-centrifuge/identity" - clientfunpb "github.com/centrifuge/go-centrifuge/protobufs/gen/go/funding" + "github.com/centrifuge/go-centrifuge/jobs" "github.com/ethereum/go-ethereum/common/hexutil" + logging "github.com/ipfs/go-log" ) // Service defines specific functions for extension funding type Service interface { - documents.Service - // Sign adds a signature to an existing document Sign(ctx context.Context, fundingID string, identifier []byte) (documents.Model, error) - // DeriveFromUpdatePayload derives Funding from clientUpdatePayload - DeriveFromUpdatePayload(ctx context.Context, req *clientfunpb.FundingUpdatePayload) (documents.Model, error) + // CreateFundingAgreement creates a new funding agreement and anchors the document. + CreateFundingAgreement(ctx context.Context, docID []byte, data *Data) (documents.Model, jobs.JobID, error) - // DeriveFromPayload derives Funding from clientPayload - DeriveFromPayload(ctx context.Context, req *clientfunpb.FundingCreatePayload) (documents.Model, error) + // UpdateFundingAgreement updates a given funding agreement with the data passed. + UpdateFundingAgreement(ctx context.Context, docID, fundingID []byte, data *Data) (documents.Model, jobs.JobID, error) - // DeriveFundingResponse returns a funding in client format - DeriveFundingResponse(ctx context.Context, model documents.Model, fundingID string) (*clientfunpb.FundingResponse, error) + // GetDataAndSignatures return the funding Data and Signatures associated with the FundingID or funding index. + GetDataAndSignatures(ctx context.Context, model documents.Model, fundingID string, idx string) (Data, []Signature, error) - // DeriveFundingListResponse returns a funding list in client format - DeriveFundingListResponse(ctx context.Context, model documents.Model) (*clientfunpb.FundingListResponse, error) + // SignFundingAgreement adds the signature to the given funding agreement. + SignFundingAgreement(ctx context.Context, docID, fundingID []byte) (documents.Model, jobs.JobID, error) } // service implements Service and handles all funding related persistence and validations type service struct { - documents.Service + docSrv documents.Service tokenRegistry documents.TokenRegistry idSrv identity.Service } +var log = logging.Logger("funding_agreement") + const ( - fundingLabel = "funding_agreement" + // AttrFundingLabel is the funding agreement label + AttrFundingLabel = "funding_agreement" fundingFieldKey = "funding_agreement[{IDX}]." agreementIDLabel = "agreement_id" fundingSignatures = "signatures" @@ -53,262 +55,183 @@ func DefaultService( tokenRegistry documents.TokenRegistry, ) Service { return service{ - Service: srv, + docSrv: srv, tokenRegistry: tokenRegistry, } } -func deriveDIDs(data *clientfunpb.FundingData) ([]identity.DID, error) { - var c []identity.DID - for _, id := range []string{data.BorrowerId, data.FunderId} { - if id != "" { - did, err := identity.NewDIDFromString(id) +// TODO: Move to attribute utils +func (s service) findFunding(model documents.Model, fundingID string) (data Data, err error) { + idx, err := extensions.FindAttributeSetIDX(model, fundingID, AttrFundingLabel, agreementIDLabel, fundingFieldKey) + if err != nil { + return data, err + } + return s.deriveFundingData(model, idx) +} + +func (s service) deriveFundingData(model documents.Model, idx string) (data Data, err error) { + d := new(Data) + types := reflect.TypeOf(*d) + for i := 0; i < types.NumField(); i++ { + // generate attr key + jsonKey := types.Field(i).Tag.Get("json") + label := extensions.LabelFromJSONTag(idx, jsonKey, fundingFieldKey) + + attrKey, err := documents.AttrKeyFromLabel(label) + if err != nil { + return data, err + } + + if model.AttributeExists(attrKey) { + attr, err := model.GetAttribute(attrKey) if err != nil { - return nil, err + return data, err } - c = append(c, did) - } - } - return c, nil -} + // set field in data + n := types.Field(i).Name -func (s service) DeriveFromPayload(ctx context.Context, req *clientfunpb.FundingCreatePayload) (model documents.Model, err error) { - var fd Data - fd.initFundingFromData(req.Data) + v, err := attr.Value.String() + if err != nil { + return data, err + } - var docID []byte - if req.DocumentId == "" { - return nil, documents.ErrDocumentIdentifier + reflect.ValueOf(d).Elem().FieldByName(n).SetString(v) + } } + return *d, nil +} - docID, err = hexutil.Decode(req.DocumentId) +// CreateFundingAgreement creates a funding agreement and anchors the document update +func (s service) CreateFundingAgreement(ctx context.Context, docID []byte, data *Data) (documents.Model, jobs.JobID, error) { + model, err := s.docSrv.GetCurrentVersion(ctx, docID) if err != nil { - return nil, err + return nil, jobs.NilJobID(), documents.ErrDocumentNotFound } - model, err = s.GetCurrentVersion(ctx, docID) + data.AgreementID = extensions.NewAttributeSetID() + attributes, err := extensions.CreateAttributesList(model, *data, fundingFieldKey, AttrFundingLabel) if err != nil { - apiLog.Error(err) - return nil, documents.ErrDocumentNotFound - } - attributes, err := extensions.CreateAttributesList(model, fd, fundingFieldKey, fundingLabel) - if err != nil { - return nil, err + return nil, jobs.NilJobID(), err } - //TODO: use StringsToDIDS here - c, err := deriveDIDs(req.Data) - if err != nil { - return nil, err + var collabs []identity.DID + for _, id := range []string{data.BorrowerID, data.FunderID} { + did, err := identity.NewDIDFromString(id) + if err != nil { + return nil, jobs.NilJobID(), err + } + + collabs = append(collabs, did) } err = model.AddAttributes( documents.CollaboratorsAccess{ - ReadWriteCollaborators: c, + ReadWriteCollaborators: collabs, }, true, attributes..., ) if err != nil { - return nil, err + return nil, jobs.NilJobID(), err } - validator := CreateValidator() - err = validator.Validate(nil, model) + model, jobID, _, err := s.docSrv.Update(ctx, model) if err != nil { - return nil, errors.NewTypedError(documents.ErrDocumentInvalid, err) + return nil, jobs.NilJobID(), err } - return model, nil + return model, jobID, nil } -// DeriveFromUpdatePayload derives Funding from clientUpdatePayload -func (s service) DeriveFromUpdatePayload(ctx context.Context, req *clientfunpb.FundingUpdatePayload) (model documents.Model, err error) { - var fd Data - fd.initFundingFromData(req.Data) - - var docID []byte - if req.DocumentId == "" { - return nil, documents.ErrDocumentIdentifier - } - - docID, err = hexutil.Decode(req.DocumentId) +// UpdateFundingAgreement updates a given funding agreement with the data passed. +func (s service) UpdateFundingAgreement(ctx context.Context, docID, fundingID []byte, data *Data) (documents.Model, jobs.JobID, error) { + model, err := s.docSrv.GetCurrentVersion(ctx, docID) if err != nil { - return nil, err + log.Error(err) + return nil, jobs.NilJobID(), documents.ErrDocumentNotFound } - model, err = s.GetCurrentVersion(ctx, docID) + data.AgreementID = hexutil.Encode(fundingID) + idx, err := extensions.FindAttributeSetIDX(model, data.AgreementID, AttrFundingLabel, agreementIDLabel, fundingFieldKey) if err != nil { - apiLog.Error(err) - return nil, documents.ErrDocumentNotFound + return nil, jobs.NilJobID(), err } - fd.AgreementId = req.AgreementId - idx, err := extensions.FindAttributeSetIDX(model, fd.AgreementId, fundingLabel, agreementIDLabel, fundingFieldKey) - if err != nil { - return nil, err - } + var collabs []identity.DID + for _, id := range []string{data.BorrowerID, data.FunderID} { + did, err := identity.NewDIDFromString(id) + if err != nil { + return nil, jobs.NilJobID(), err + } - //TODO: use StringsToDIDS here - c, err := deriveDIDs(req.Data) - if err != nil { - return nil, err + collabs = append(collabs, did) } // overwriting is not enough because it is not required that // the funding payload contains all funding attributes model, err = extensions.DeleteAttributesSet(model, Data{}, idx, fundingFieldKey) if err != nil { - return nil, err + return nil, jobs.NilJobID(), err } - attributes, err := extensions.FillAttributeList(fd, idx, fundingFieldKey) + attributes, err := extensions.FillAttributeList(*data, idx, fundingFieldKey) if err != nil { - return nil, err + return nil, jobs.NilJobID(), err } err = model.AddAttributes( documents.CollaboratorsAccess{ - ReadWriteCollaborators: c, + ReadWriteCollaborators: collabs, }, true, attributes..., ) if err != nil { - return nil, err + return nil, jobs.NilJobID(), err } - validator := CreateValidator() - err = validator.Validate(nil, model) + model, jobID, _, err := s.docSrv.Update(ctx, model) if err != nil { - return nil, errors.NewTypedError(documents.ErrDocumentInvalid, err) + return nil, jobs.NilJobID(), err } - return model, nil -} - -// TODO: Move to attribute utils -func (s service) findFunding(model documents.Model, fundingID string) (*Data, error) { - idx, err := extensions.FindAttributeSetIDX(model, fundingID, fundingLabel, agreementIDLabel, fundingFieldKey) - if err != nil { - return nil, err - } - return s.deriveFundingData(model, idx) -} - -// TODO: Move to attribute utils -func (s service) deriveFundingData(model documents.Model, idx string) (*Data, error) { - data := new(Data) - - types := reflect.TypeOf(*data) - for i := 0; i < types.NumField(); i++ { - // generate attr key - jsonKey := types.Field(i).Tag.Get("json") - label := extensions.LabelFromJSONTag(idx, jsonKey, fundingFieldKey) - - attrKey, err := documents.AttrKeyFromLabel(label) - if err != nil { - return nil, err - } - - if model.AttributeExists(attrKey) { - attr, err := model.GetAttribute(attrKey) - if err != nil { - return nil, err - } - - // set field in data - n := types.Field(i).Name - - v, err := attr.Value.String() - if err != nil { - return nil, err - } - - reflect.ValueOf(data).Elem().FieldByName(n).SetString(v) - - } - - } - return data, nil + return model, jobID, nil } -// DeriveFundingResponse returns create response from the added funding -func (s service) DeriveFundingResponse(ctx context.Context, model documents.Model, fundingID string) (*clientfunpb.FundingResponse, error) { - idx, err := extensions.FindAttributeSetIDX(model, fundingID, fundingLabel, agreementIDLabel, fundingFieldKey) +// SignFundingAgreement adds the signature to the given funding agreement. +func (s service) SignFundingAgreement(ctx context.Context, docID, fundingID []byte) (documents.Model, jobs.JobID, error) { + m, err := s.Sign(ctx, hexutil.Encode(fundingID), docID) if err != nil { - return nil, err + return nil, jobs.NilJobID(), err } - h, err := documents.DeriveResponseHeader(s.tokenRegistry, model) + m, jobID, _, err := s.docSrv.Update(ctx, m) if err != nil { - return nil, errors.New("failed to derive response: %v", err) - } - data, err := s.deriveFundingData(model, idx) - if err != nil { - return nil, err + return nil, jobs.NilJobID(), err } - signatures, err := s.deriveFundingSignatures(ctx, model, data, idx) - if err != nil { - return nil, errors.NewTypedError(extensions.ErrAttrSetSignature, err) - } - - return &clientfunpb.FundingResponse{ - Header: h, - Data: &clientfunpb.FundingResponseData{Funding: data.getClientData(), Signatures: signatures}, - }, nil - + return m, jobID, nil } -// DeriveFundingListResponse returns a funding list in client format -func (s service) DeriveFundingListResponse(ctx context.Context, model documents.Model) (*clientfunpb.FundingListResponse, error) { - response := new(clientfunpb.FundingListResponse) - - h, err := documents.DeriveResponseHeader(s.tokenRegistry, model) - if err != nil { - return nil, errors.New("failed to derive response: %v", err) - } - response.Header = h - - fl, err := documents.AttrKeyFromLabel(fundingLabel) - if err != nil { - return nil, err - } - - if !model.AttributeExists(fl) { - return response, nil +// GetDataAndSignatures return the funding Data and Signatures associated with the FundingID. +func (s service) GetDataAndSignatures(ctx context.Context, model documents.Model, fundingID string, idx string) (data Data, sigs []Signature, err error) { + if idx == "" { + idx, err = extensions.FindAttributeSetIDX(model, fundingID, AttrFundingLabel, agreementIDLabel, fundingFieldKey) + if err != nil { + return data, sigs, err + } } - lastIdx, err := extensions.GetArrayLatestIDX(model, fundingLabel) + data, err = s.deriveFundingData(model, idx) if err != nil { - return nil, err + return data, sigs, err } - i, err := documents.NewInt256("0") + sigs, err = s.deriveFundingSignatures(ctx, model, data, idx) if err != nil { - return nil, err + return data, sigs, errors.NewTypedError(extensions.ErrAttrSetSignature, err) } - for i.Cmp(lastIdx) != 1 { - funding, err := s.deriveFundingData(model, i.String()) - if err != nil { - continue - } - - signatures, err := s.deriveFundingSignatures(ctx, model, funding, i.String()) - if err != nil { - return nil, errors.NewTypedError(extensions.ErrAttrSetSignature, err) - } - - response.Data = append(response.Data, &clientfunpb.FundingResponseData{Funding: funding.getClientData(), Signatures: signatures}) - i, err = i.Inc() - - if err != nil { - return nil, err - } - - } - return response, nil + return data, sigs, nil } diff --git a/extensions/funding/service_test.go b/extensions/funding/service_test.go index 50df5fcea..eeaabe4da 100644 --- a/extensions/funding/service_test.go +++ b/extensions/funding/service_test.go @@ -4,7 +4,6 @@ package funding import ( "context" - "fmt" "os" "testing" "time" @@ -16,12 +15,13 @@ import ( "github.com/centrifuge/go-centrifuge/config/configstore" "github.com/centrifuge/go-centrifuge/documents" "github.com/centrifuge/go-centrifuge/documents/invoice" + "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/go-centrifuge/ethereum" "github.com/centrifuge/go-centrifuge/extensions" + "github.com/centrifuge/go-centrifuge/identity" "github.com/centrifuge/go-centrifuge/identity/ideth" "github.com/centrifuge/go-centrifuge/jobs" "github.com/centrifuge/go-centrifuge/p2p" - clientfunpb "github.com/centrifuge/go-centrifuge/protobufs/gen/go/funding" "github.com/centrifuge/go-centrifuge/queue" "github.com/centrifuge/go-centrifuge/storage/leveldb" "github.com/centrifuge/go-centrifuge/testingutils/config" @@ -29,6 +29,7 @@ import ( "github.com/centrifuge/go-centrifuge/testingutils/identity" "github.com/centrifuge/go-centrifuge/testingutils/testingjobs" "github.com/centrifuge/go-centrifuge/utils" + "github.com/centrifuge/go-centrifuge/utils/byteutils" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -47,7 +48,7 @@ func TestMain(m *testing.M) { ctx[ethereum.BootstrappedEthereumClient] = ethClient jobMan := &testingjobs.MockJobManager{} ctx[jobs.BootstrappedService] = jobMan - done := make(chan bool) + done := make(chan error) jobMan.On("ExecuteWithinJob", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(jobs.NilJobID(), done, nil) ctx[bootstrap.BootstrappedInvoiceUnpaid] = new(testingdocuments.MockRegistry) ibootstrappers := []bootstrap.TestBootstrapper{ @@ -66,22 +67,16 @@ func TestMain(m *testing.M) { bootstrap.RunTestBootstrappers(ibootstrappers, ctx) cfg = ctx[bootstrap.BootstrappedConfig].(config.Configuration) cfg.Set("identityId", did.String()) - configService = ctx[config.BootstrappedConfigStorage].(config.Service) result := m.Run() bootstrap.RunTestTeardown(ibootstrappers) os.Exit(result) } func TestAttributesUtils(t *testing.T) { - testingdocuments.CreateInvoicePayload() - inv := new(invoice.Invoice) - err := inv.InitInvoiceInput(testingdocuments.CreateInvoicePayload(), testingidentity.GenerateRandomDID()) - assert.NoError(t, err) + inv, _ := invoice.CreateInvoiceWithEmbedCD(t, nil, testingidentity.GenerateRandomDID(), nil) docSrv := new(testingdocuments.MockService) docSrv.On("GetCurrentVersion", mock.Anything, mock.Anything).Return(inv, nil) - srv := DefaultService(docSrv, nil) - - data := createTestData() + data := CreateData() // Fill attributes list a, err := extensions.FillAttributeList(data, "0", fundingFieldKey) @@ -90,7 +85,7 @@ func TestAttributesUtils(t *testing.T) { assert.Len(t, a, 12) // Creating an attributes list generates the correct attributes and adds an idx as an attribute - attributes, err := extensions.CreateAttributesList(inv, data, fundingFieldKey, fundingLabel) + attributes, err := extensions.CreateAttributesList(inv, data, fundingFieldKey, AttrFundingLabel) assert.NoError(t, err) assert.Len(t, attributes, 13) @@ -118,17 +113,17 @@ func TestAttributesUtils(t *testing.T) { } // wrong attributeSetID - idx, err := extensions.FindAttributeSetIDX(inv, "randomID", fundingLabel, agreementIDLabel, fundingFieldKey) + idx, err := extensions.FindAttributeSetIDX(inv, "randomID", AttrFundingLabel, agreementIDLabel, fundingFieldKey) assert.Error(t, err) // correct - idx, err = extensions.FindAttributeSetIDX(inv, agreementID, fundingLabel, agreementIDLabel, fundingFieldKey) + idx, err = extensions.FindAttributeSetIDX(inv, agreementID, AttrFundingLabel, agreementIDLabel, fundingFieldKey) assert.Equal(t, "0", idx) assert.NoError(t, err) // add second attributeSet - data.AgreementId = extensions.NewAttributeSetID() - a2, err := extensions.CreateAttributesList(inv, data, fundingFieldKey, fundingLabel) + data.AgreementID = extensions.NewAttributeSetID() + a2, err := extensions.CreateAttributesList(inv, data, fundingFieldKey, AttrFundingLabel) assert.NoError(t, err) var aID string @@ -144,10 +139,10 @@ func TestAttributesUtils(t *testing.T) { assert.NoError(t, err) // latest idx - model, err := srv.GetCurrentVersion(context.Background(), inv.Document.DocumentIdentifier) + model, err := docSrv.GetCurrentVersion(context.Background(), inv.Document.DocumentIdentifier) assert.NoError(t, err) - lastIdx, err := extensions.GetArrayLatestIDX(model, fundingLabel) + lastIdx, err := extensions.GetArrayLatestIDX(model, AttrFundingLabel) assert.NoError(t, err) n, err := documents.NewInt256("1") @@ -155,12 +150,12 @@ func TestAttributesUtils(t *testing.T) { assert.Equal(t, lastIdx, n) // index should be 1 - idx, err = extensions.FindAttributeSetIDX(inv, aID, fundingLabel, agreementIDLabel, fundingFieldKey) + idx, err = extensions.FindAttributeSetIDX(inv, aID, AttrFundingLabel, agreementIDLabel, fundingFieldKey) assert.Equal(t, "1", idx) assert.NoError(t, err) // delete the first attribute set - idx, err = extensions.FindAttributeSetIDX(inv, agreementID, fundingLabel, agreementIDLabel, fundingFieldKey) + idx, err = extensions.FindAttributeSetIDX(inv, agreementID, AttrFundingLabel, agreementIDLabel, fundingFieldKey) assert.NoError(t, err) model, err = extensions.DeleteAttributesSet(model, Data{}, idx, fundingFieldKey) @@ -168,11 +163,11 @@ func TestAttributesUtils(t *testing.T) { assert.Len(t, model.GetAttributes(), 13) // error when trying to delete non existing attribute set - idx, err = extensions.FindAttributeSetIDX(inv, agreementID, fundingLabel, agreementIDLabel, fundingFieldKey) + idx, err = extensions.FindAttributeSetIDX(inv, agreementID, AttrFundingLabel, agreementIDLabel, fundingFieldKey) assert.Error(t, err) // check that latest idx is still 1 even though the first set of attributes have been deleted ? - latest, err := extensions.GetArrayLatestIDX(model, fundingLabel) + latest, err := extensions.GetArrayLatestIDX(model, AttrFundingLabel) assert.NoError(t, err) assert.Equal(t, latest, n) @@ -181,195 +176,189 @@ func TestAttributesUtils(t *testing.T) { assert.Error(t, err) // check that we can no longer find the attributes from the first set - idx, err = extensions.FindAttributeSetIDX(inv, agreementID, fundingLabel, agreementIDLabel, fundingFieldKey) + idx, err = extensions.FindAttributeSetIDX(inv, agreementID, AttrFundingLabel, agreementIDLabel, fundingFieldKey) assert.Error(t, err) // test increment array attr idx n, err = documents.NewInt256("2") assert.NoError(t, err) - newIdx, err := extensions.IncrementArrayAttrIDX(model, fundingLabel) + newIdx, err := extensions.IncrementArrayAttrIDX(model, AttrFundingLabel) assert.NoError(t, err) v, err := newIdx.Value.String() assert.NoError(t, err) assert.Equal(t, "2", v) - assert.Equal(t, fundingLabel, newIdx.KeyLabel) + assert.Equal(t, AttrFundingLabel, newIdx.KeyLabel) } -func TestDeriveFromPayload(t *testing.T) { - ctxh := testingconfig.CreateAccountContext(t, cfg) - testingdocuments.CreateInvoicePayload() - inv := new(invoice.Invoice) - err := inv.InitInvoiceInput(testingdocuments.CreateInvoicePayload(), testingidentity.GenerateRandomDID()) - assert.NoError(t, err) - - docSrv := new(testingdocuments.MockService) - docSrv.On("GetCurrentVersion", mock.Anything, mock.Anything).Return(inv, nil) - srv := DefaultService(docSrv, nil) - - payload := createTestPayload() - payload.DocumentId = hexutil.Encode(inv.Document.DocumentIdentifier) - - for i := 0; i < 10; i++ { - model, err := srv.DeriveFromPayload(ctxh, payload) - assert.NoError(t, err) - label := fmt.Sprintf("funding_agreement[%d].currency", i) - key, err := documents.AttrKeyFromLabel(label) - assert.NoError(t, err) - - attr, err := model.GetAttribute(key) - assert.NoError(t, err) - assert.Equal(t, "eur", attr.Value.Str) +func invalidData() Data { + return Data{ + Currency: "eur", + Days: "90", + Amount: "1000", + RepaymentAmount: "1200.12", + Fee: "10", + BorrowerID: "", + FunderID: testingidentity.GenerateRandomDID().String(), + NFTAddress: hexutil.Encode(utils.RandomSlice(32)), + RepaymentDueDate: time.Now().UTC().Format(time.RFC3339), + RepaymentOccurredDate: time.Now().UTC().Format(time.RFC3339), + PaymentDetailsID: hexutil.Encode(utils.RandomSlice(32)), } - - payload.DocumentId = "" - _, err = srv.DeriveFromPayload(ctxh, payload) - assert.Error(t, err) } -func TestDeriveFundingResponse(t *testing.T) { - testingdocuments.CreateInvoicePayload() - inv := new(invoice.Invoice) - err := inv.InitInvoiceInput(testingdocuments.CreateInvoicePayload(), testingidentity.GenerateRandomDID()) - assert.NoError(t, err) - +func TestService_CreateFundingAgreement(t *testing.T) { + // missing document. docSrv := new(testingdocuments.MockService) - docSrv.On("GetCurrentVersion", mock.Anything, mock.Anything).Return(inv, nil) + docSrv.On("GetCurrentVersion", mock.Anything).Return(nil, errors.New("failed to get document")).Once() srv := DefaultService(docSrv, nil) + docID := utils.RandomSlice(32) + ctx := context.Background() + _, _, err := srv.CreateFundingAgreement(ctx, docID, new(Data)) + assert.Error(t, err) + assert.True(t, errors.IsOfType(documents.ErrDocumentNotFound, err)) + + // failed to create attribute + m := new(testingdocuments.MockModel) + docSrv.On("GetCurrentVersion", mock.Anything).Return(m, nil) + m.On("AttributeExists", mock.Anything).Return(true).Once() + m.On("GetAttribute", mock.Anything).Return(documents.Attribute{}, errors.New("attribute not found")).Once() + _, _, err = srv.CreateFundingAgreement(ctx, docID, new(Data)) + assert.Error(t, err) + assert.Contains(t, err.Error(), "attribute not found") - ctxh := testingconfig.CreateAccountContext(t, cfg) - - for i := 0; i < 10; i++ { - payload := createTestPayload() - payload.DocumentId = hexutil.Encode(inv.Document.DocumentIdentifier) - model, err := srv.DeriveFromPayload(context.Background(), payload) - assert.NoError(t, err) + // invalid dids + data := invalidData() + m.On("AttributeExists", mock.Anything).Return(false) + _, _, err = srv.CreateFundingAgreement(ctx, docID, &data) + assert.Error(t, err) + assert.True(t, errors.IsOfType(identity.ErrMalformedAddress, err)) - response, err := srv.DeriveFundingResponse(ctxh, model, payload.Data.AgreementId) - assert.NoError(t, err) - checkResponse(t, payload, response.Data.Funding) - } + // failed to add attributes + data = CreateData() + m.On("AddAttributes", mock.Anything, mock.Anything, mock.Anything).Return(errors.New("failed to add attrs")).Once() + m.On("AttributeExists", mock.Anything).Return(false) + _, _, err = srv.CreateFundingAgreement(ctx, docID, &data) + assert.Error(t, err) + assert.Contains(t, err.Error(), "failed to add attrs") -} + // failed to update document + m.On("AddAttributes", mock.Anything, mock.Anything, mock.Anything).Return(nil) + docSrv.On("Update", ctx, m).Return(nil, jobs.NilJobID(), errors.New("failed to update")).Once() + _, _, err = srv.CreateFundingAgreement(ctx, docID, &data) + assert.Error(t, err) + assert.Contains(t, err.Error(), "failed to update") -func TestDeriveFundingListResponse(t *testing.T) { - testingdocuments.CreateInvoicePayload() - inv := new(invoice.Invoice) - err := inv.InitInvoiceInput(testingdocuments.CreateInvoicePayload(), testingidentity.GenerateRandomDID()) + // success + docSrv.On("Update", ctx, m).Return(m, jobs.NewJobID(), nil) + d, _, err := srv.CreateFundingAgreement(ctx, docID, &data) assert.NoError(t, err) + assert.Equal(t, d, m) + docSrv.AssertExpectations(t) + m.AssertExpectations(t) +} +func TestService_UpdateFundingAgreement(t *testing.T) { + // missing document. docSrv := new(testingdocuments.MockService) - docSrv.On("GetCurrentVersion", mock.Anything, mock.Anything).Return(inv, nil) + docSrv.On("GetCurrentVersion", mock.Anything).Return(nil, errors.New("failed to get document")).Once() srv := DefaultService(docSrv, nil) + docID := utils.RandomSlice(32) + fundingID := utils.RandomSlice(32) + ctx := testingconfig.CreateAccountContext(t, cfg) + _, _, err := srv.UpdateFundingAgreement(ctx, docID, fundingID, new(Data)) + assert.Error(t, err) + assert.True(t, errors.IsOfType(documents.ErrDocumentNotFound, err)) - var model documents.Model - var payloads []*clientfunpb.FundingCreatePayload - for i := 0; i < 10; i++ { - p := createTestPayload() - p.DocumentId = hexutil.Encode(inv.Document.DocumentIdentifier) - payloads = append(payloads, p) - model, err = srv.DeriveFromPayload(context.Background(), p) - assert.NoError(t, err) - } + // missing attribute + inv, _ := invoice.CreateInvoiceWithEmbedCD(t, ctx, did, nil) + docID = inv.ID() + docSrv.On("GetCurrentVersion", mock.Anything).Return(inv, nil) + _, _, err = srv.UpdateFundingAgreement(ctx, docID, fundingID, new(Data)) + assert.Error(t, err) - response, err := srv.DeriveFundingListResponse(context.Background(), model) + // invalid identities + data := CreateData() + fundingID, err = hexutil.Decode(data.AgreementID) assert.NoError(t, err) - assert.Equal(t, 10, len(response.Data)) - - for i := 0; i < 10; i++ { - checkResponse(t, payloads[i], response.Data[i].Funding) - } -} - -func TestService_DeriveFromUpdatePayload(t *testing.T) { - testingdocuments.CreateInvoicePayload() - inv := new(invoice.Invoice) - err := inv.InitInvoiceInput(testingdocuments.CreateInvoicePayload(), testingidentity.GenerateRandomDID()) + attrs, err := extensions.CreateAttributesList(inv, data, fundingFieldKey, AttrFundingLabel) assert.NoError(t, err) - - docSrv := new(testingdocuments.MockService) - docSrv.On("GetCurrentVersion", mock.Anything, mock.Anything).Return(inv, nil) - srv := DefaultService(docSrv, nil) - - var model documents.Model - p := createTestPayload() - p.DocumentId = hexutil.Encode(inv.Document.DocumentIdentifier) - model, err = srv.DeriveFromPayload(context.Background(), p) + err = inv.AddAttributes(documents.CollaboratorsAccess{}, false, attrs...) assert.NoError(t, err) + _, _, err = srv.UpdateFundingAgreement(ctx, docID, fundingID, new(Data)) + assert.Error(t, err) - // update - docSrv.On("GetCurrentVersion", mock.Anything, mock.Anything).Return(model, nil) - p2 := &clientfunpb.FundingUpdatePayload{Data: createTestClientData(), DocumentId: hexutil.Encode(utils.RandomSlice(32)), AgreementId: p.Data.AgreementId} - p2.Data.Currency = "" - p2.Data.Fee = "13.37" - - model, err = srv.DeriveFromUpdatePayload(context.Background(), p2) + // update fails + err = inv.AddAttributes(documents.CollaboratorsAccess{}, false, attrs...) assert.NoError(t, err) + docSrv.On("Update", mock.Anything, mock.Anything).Return(nil, jobs.NilJobID(), errors.New("update failed")).Once() + _, _, err = srv.UpdateFundingAgreement(ctx, docID, fundingID, &data) + assert.Error(t, err) + assert.Contains(t, err.Error(), "update failed") - response, err := srv.DeriveFundingListResponse(context.Background(), model) + // success + err = inv.AddAttributes(documents.CollaboratorsAccess{}, false, attrs...) assert.NoError(t, err) - assert.Equal(t, 1, len(response.Data)) - assert.Equal(t, p2.Data.Fee, response.Data[0].Funding.Fee) - - // fee was not set in the update old fee field should not exist - assert.NotEqual(t, p.Data.Fee, response.Data[0].Funding.Fee) + docSrv.On("Update", mock.Anything, mock.Anything).Return(inv, jobs.NewJobID(), nil).Once() + m, _, err := srv.UpdateFundingAgreement(ctx, docID, fundingID, &data) + assert.NoError(t, err) + assert.Equal(t, m, inv) + docSrv.AssertExpectations(t) +} - // non existing funding id - p3 := &clientfunpb.FundingUpdatePayload{Data: createTestClientData(), DocumentId: hexutil.Encode(utils.RandomSlice(32)), AgreementId: hexutil.Encode(utils.RandomSlice(32))} - model, err = srv.DeriveFromUpdatePayload(context.Background(), p3) +func TestService_SignFundingAgreement(t *testing.T) { + // missing agreement + ctx := testingconfig.CreateAccountContext(t, cfg) + inv, _ := invoice.CreateInvoiceWithEmbedCD(t, ctx, did, nil) + docSrv := new(testingdocuments.MockService) + s := DefaultService(docSrv, nil) + docID := inv.ID() + docSrv.On("GetCurrentVersion", docID).Return(inv, nil) + _, _, err := s.SignFundingAgreement(ctx, docID, utils.RandomSlice(32)) assert.Error(t, err) - assert.Contains(t, err, extensions.ErrAttributeSetNotFound) + assert.True(t, errors.IsOfType(extensions.ErrAttributeSetNotFound, err)) - p2.DocumentId = "" - _, err = srv.DeriveFromUpdatePayload(context.Background(), p2) + // failed update + data := CreateData() + attrs, err := extensions.CreateAttributesList(inv, data, fundingFieldKey, AttrFundingLabel) + assert.NoError(t, err) + err = inv.AddAttributes(documents.CollaboratorsAccess{}, false, attrs...) + assert.NoError(t, err) + fundingID, err := hexutil.Decode(data.AgreementID) + assert.NoError(t, err) + docSrv.On("Update", ctx, inv).Return(nil, nil, errors.New("failed to update")).Once() + _, _, err = s.SignFundingAgreement(ctx, docID, fundingID) assert.Error(t, err) -} + assert.Contains(t, err.Error(), "failed to update") -func createTestClientData() *clientfunpb.FundingData { - fundingId := extensions.NewAttributeSetID() - return &clientfunpb.FundingData{ - AgreementId: fundingId, - Currency: "eur", - Days: "90", - Amount: "1000", - RepaymentAmount: "1200.12", - Fee: "10", - BorrowerId: testingidentity.GenerateRandomDID().String(), - FunderId: testingidentity.GenerateRandomDID().String(), - NftAddress: hexutil.Encode(utils.RandomSlice(32)), - RepaymentDueDate: time.Now().UTC().Format(time.RFC3339), - RepaymentOccurredDate: time.Now().UTC().Format(time.RFC3339), - PaymentDetailsId: hexutil.Encode(utils.RandomSlice(32)), - } + // success + docSrv.On("Update", ctx, inv).Return(inv, jobs.NewJobID(), nil) + m, _, err := s.SignFundingAgreement(ctx, docID, fundingID) + assert.NoError(t, err) + assert.Equal(t, inv, m) + docSrv.AssertExpectations(t) } -func createTestData() Data { - fundingId := extensions.NewAttributeSetID() - return Data{ - AgreementId: fundingId, - Currency: "eur", - Days: "90", - Amount: "1000", - RepaymentAmount: "1200.12", - Fee: "10", - BorrowerId: testingidentity.GenerateRandomDID().String(), - FunderId: testingidentity.GenerateRandomDID().String(), - NftAddress: hexutil.Encode(utils.RandomSlice(32)), - RepaymentDueDate: time.Now().UTC().Format(time.RFC3339), - RepaymentOccurredDate: time.Now().UTC().Format(time.RFC3339), - PaymentDetailsId: hexutil.Encode(utils.RandomSlice(32)), - } -} +func TestService_GetDataAndSignatures(t *testing.T) { + ctx := testingconfig.CreateAccountContext(t, cfg) + inv, _ := invoice.CreateInvoiceWithEmbedCD(t, ctx, did, nil) + docSrv := new(testingdocuments.MockService) + srv := DefaultService(docSrv, nil) -func createTestPayload() *clientfunpb.FundingCreatePayload { - return &clientfunpb.FundingCreatePayload{Data: createTestClientData()} -} + // missing funding id + fundingID := byteutils.HexBytes(utils.RandomSlice(32)).String() + _, _, err := srv.GetDataAndSignatures(ctx, inv, fundingID, "") + assert.Error(t, err) -func checkResponse(t *testing.T, payload *clientfunpb.FundingCreatePayload, response *clientfunpb.FundingData) { - assert.Equal(t, payload.Data.AgreementId, response.AgreementId) - assert.Equal(t, payload.Data.Currency, response.Currency) - assert.Equal(t, payload.Data.Days, response.Days) - assert.Equal(t, payload.Data.Amount, response.Amount) - assert.Equal(t, payload.Data.RepaymentDueDate, response.RepaymentDueDate) + // success + data := CreateData() + attrs, err := extensions.CreateAttributesList(inv, data, fundingFieldKey, AttrFundingLabel) + assert.NoError(t, err) + err = inv.AddAttributes(documents.CollaboratorsAccess{}, false, attrs...) + assert.NoError(t, err) + data1, sigs, err := srv.GetDataAndSignatures(ctx, inv, data.AgreementID, "") + assert.NoError(t, err) + assert.Equal(t, data, data1) + assert.Len(t, sigs, 0) } diff --git a/extensions/funding/signatures.go b/extensions/funding/signatures.go index 063525a3b..1d0ec0c94 100644 --- a/extensions/funding/signatures.go +++ b/extensions/funding/signatures.go @@ -10,7 +10,6 @@ import ( "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/go-centrifuge/extensions" "github.com/centrifuge/go-centrifuge/identity" - clientfunpb "github.com/centrifuge/go-centrifuge/protobufs/gen/go/funding" "github.com/centrifuge/go-centrifuge/utils" "github.com/ethereum/go-ethereum/common/hexutil" ) @@ -60,12 +59,12 @@ func (s service) Sign(ctx context.Context, agreementID string, identifier []byte return nil, errors.NewTypedError(documents.ErrDocumentConfigAccountID, err) } - model, err := s.Service.GetCurrentVersion(ctx, identifier) + model, err := s.docSrv.GetCurrentVersion(ctx, identifier) if err != nil { return nil, documents.ErrDocumentNotFound } - idxFunding, err := extensions.FindAttributeSetIDX(model, agreementID, fundingLabel, agreementIDLabel, fundingFieldKey) + idxFunding, err := extensions.FindAttributeSetIDX(model, agreementID, AttrFundingLabel, agreementIDLabel, fundingFieldKey) if err != nil { return nil, extensions.ErrAttributeSetNotFound } @@ -83,7 +82,7 @@ func (s service) Sign(ctx context.Context, agreementID string, identifier []byte return model, nil } -func (s service) validateValueOfSignAttr(funding *Data, signAttr documents.Attribute) (bool, error) { +func (s service) validateValueOfSignAttr(funding Data, signAttr documents.Attribute) (bool, error) { value, err := json.Marshal(funding) if err != nil { return false, extensions.ErrJSON @@ -91,52 +90,52 @@ func (s service) validateValueOfSignAttr(funding *Data, signAttr documents.Attri return utils.IsSameByteSlice(value, signAttr.Value.Signed.Value), nil } -func (s service) validateSignedFundingVersion(ctx context.Context, identifier []byte, fundingID string, signAttr documents.Attribute) (*clientfunpb.FundingSignature, error) { +func (s service) validateSignedFundingVersion(ctx context.Context, identifier []byte, fundingID string, signAttr documents.Attribute) (sig Signature, err error) { did := signAttr.Value.Signed.Identity - signedDocVersion, err := s.Service.GetVersion(ctx, identifier, signAttr.Value.Signed.DocumentVersion) + signedDocVersion, err := s.docSrv.GetVersion(ctx, identifier, signAttr.Value.Signed.DocumentVersion) if err != nil { - return nil, documents.ErrDocumentNotFound + return sig, documents.ErrDocumentNotFound } signedFunding, err := s.findFunding(signedDocVersion, fundingID) if err != nil { - return nil, extensions.ErrAttributeSetNotFound + return sig, extensions.ErrAttributeSetNotFound } valid, err := s.validateValueOfSignAttr(signedFunding, signAttr) if err != nil { - return nil, err + return sig, err } if valid { // the value of the older funding version signature is correct - return &clientfunpb.FundingSignature{Valid: "true", SignedVersion: hexutil.Encode(identifier), Identity: did.String(), OutdatedSignature: "true"}, nil + return Signature{Valid: "true", SignedVersion: hexutil.Encode(identifier), Identity: did.String(), OutdatedSignature: "true"}, nil } - return &clientfunpb.FundingSignature{Valid: "false", SignedVersion: hexutil.Encode(identifier), Identity: did.String(), OutdatedSignature: "true"}, nil + return Signature{Valid: "false", SignedVersion: hexutil.Encode(identifier), Identity: did.String(), OutdatedSignature: "true"}, nil } -func (s service) signAttrToClientData(ctx context.Context, current documents.Model, funding *Data, signAttr documents.Attribute) (*clientfunpb.FundingSignature, error) { +func (s service) signAttrToClientData(ctx context.Context, current documents.Model, funding Data, signAttr documents.Attribute) (sig Signature, err error) { if signAttr.Value.Type != documents.AttrSigned { - return nil, extensions.ErrAttrSetSignature + return sig, extensions.ErrAttrSetSignature } did := signAttr.Value.Signed.Identity valid, err := s.validateValueOfSignAttr(funding, signAttr) if err != nil { - return nil, err + return sig, err } // value correct (funding data didn't change since signing) if valid { - return &clientfunpb.FundingSignature{Valid: "true", SignedVersion: hexutil.Encode(current.ID()), Identity: did.String(), OutdatedSignature: "false"}, nil + return Signature{Valid: "true", SignedVersion: hexutil.Encode(current.ID()), Identity: did.String(), OutdatedSignature: "false"}, nil } - return s.validateSignedFundingVersion(ctx, current.ID(), funding.AgreementId, signAttr) + return s.validateSignedFundingVersion(ctx, current.ID(), funding.AgreementID, signAttr) } -func (s service) deriveFundingSignatures(ctx context.Context, model documents.Model, funding *Data, idxFunding string) ([]*clientfunpb.FundingSignature, error) { - var signatures []*clientfunpb.FundingSignature +func (s service) deriveFundingSignatures(ctx context.Context, model documents.Model, funding Data, idxFunding string) ([]Signature, error) { + var signatures []Signature sLabel := extensions.GenerateLabel(fundingFieldKey, idxFunding, fundingSignatures) key, err := documents.AttrKeyFromLabel(sLabel) if err != nil { diff --git a/extensions/funding/signatures_test.go b/extensions/funding/signatures_test.go index 93a4969c3..8bde5e53e 100644 --- a/extensions/funding/signatures_test.go +++ b/extensions/funding/signatures_test.go @@ -12,8 +12,9 @@ import ( "github.com/centrifuge/go-centrifuge/contextutil" "github.com/centrifuge/go-centrifuge/documents" "github.com/centrifuge/go-centrifuge/documents/invoice" - clientfunpb "github.com/centrifuge/go-centrifuge/protobufs/gen/go/funding" - "github.com/centrifuge/go-centrifuge/testingutils/documents" + "github.com/centrifuge/go-centrifuge/extensions" + "github.com/centrifuge/go-centrifuge/jobs" + testingdocuments "github.com/centrifuge/go-centrifuge/testingutils/documents" "github.com/centrifuge/go-centrifuge/testingutils/identity" "github.com/centrifuge/go-centrifuge/utils" "github.com/ethereum/go-ethereum/common/hexutil" @@ -32,38 +33,30 @@ func (m *mockAccount) SignMsg(msg []byte) (*coredocumentpb.Signature, error) { return sig, args.Error(1) } -func (m *mockAccount) GetIdentityID() ([]byte, error) { +func (m *mockAccount) GetIdentityID() []byte { args := m.Called() sig, _ := args.Get(0).([]byte) - return sig, args.Error(1) + return sig } func setupFundingForTesting(t *testing.T, fundingAmount int) (Service, *testingdocuments.MockService, documents.Model, string) { - testingdocuments.CreateInvoicePayload() - inv := new(invoice.Invoice) - err := inv.InitInvoiceInput(testingdocuments.CreateInvoicePayload(), testingidentity.GenerateRandomDID()) - assert.NoError(t, err) - + inv, _ := invoice.CreateInvoiceWithEmbedCD(t, nil, testingidentity.GenerateRandomDID(), nil) docSrv := new(testingdocuments.MockService) docSrv.On("GetCurrentVersion", mock.Anything, mock.Anything).Return(inv, nil) - srv := DefaultService(docSrv, nil) - - var model documents.Model - var payloads []*clientfunpb.FundingCreatePayload var lastFundingId string // create a list of fundings for i := 0; i < fundingAmount; i++ { - p := createTestPayload() - p.DocumentId = hexutil.Encode(inv.Document.DocumentIdentifier) - payloads = append(payloads, p) - model, err = srv.DeriveFromPayload(context.Background(), p) + data := CreateData() + attrs, err := extensions.CreateAttributesList(inv, data, fundingFieldKey, AttrFundingLabel) + assert.NoError(t, err) + err = inv.AddAttributes(documents.CollaboratorsAccess{}, false, attrs...) assert.NoError(t, err) - lastFundingId = p.Data.AgreementId + lastFundingId = data.AgreementID } - return srv, docSrv, model, lastFundingId + return srv, docSrv, inv, lastFundingId } func TestService_Sign(t *testing.T) { @@ -124,10 +117,10 @@ func TestService_SignVerify(t *testing.T) { assert.NoError(t, err) // funding current version: valid - response, err := srv.DeriveFundingResponse(ctx, model, fundingID) + data, signatures, err := srv.GetDataAndSignatures(ctx, model, fundingID, "") assert.NoError(t, err) - assert.Equal(t, "true", response.Data.Signatures[0].Valid) - assert.Equal(t, "false", response.Data.Signatures[0].OutdatedSignature) + assert.Equal(t, "true", signatures[0].Valid) + assert.Equal(t, "false", signatures[0].OutdatedSignature) // update funding after signature oldCD, err := model.PackCoreDocument() @@ -136,18 +129,20 @@ func TestService_SignVerify(t *testing.T) { err = oldInv.UnpackCoreDocument(oldCD) assert.NoError(t, err) - p2 := &clientfunpb.FundingUpdatePayload{Data: createTestClientData(), DocumentId: hexutil.Encode(utils.RandomSlice(32)), AgreementId: fundingID} - p2.Data.Currency = "" - p2.Data.Fee = "13.37" - updatedModel, err := srv.DeriveFromUpdatePayload(context.Background(), p2) + data.Currency = "" + data.Fee = "13.37" + fundIDBytes, err := hexutil.Decode(fundingID) + assert.NoError(t, err) + docSrv.On("Update", mock.Anything, model).Return(model, jobs.NewJobID(), nil) + updatedModel, _, err := srv.UpdateFundingAgreement(context.Background(), utils.RandomSlice(32), fundIDBytes, &data) assert.NoError(t, err) // older funding version signed: valid docSrv.On("GetVersion", mock.Anything, mock.Anything).Return(oldInv, nil).Once() - response, err = srv.DeriveFundingResponse(ctx, updatedModel, fundingID) + _, signatures, err = srv.GetDataAndSignatures(ctx, updatedModel, fundingID, "") assert.NoError(t, err) - assert.Equal(t, "true", response.Data.Signatures[0].Valid) - assert.Equal(t, "true", response.Data.Signatures[0].OutdatedSignature) + assert.Equal(t, "true", signatures[0].Valid) + assert.Equal(t, "true", signatures[0].OutdatedSignature) // older funding version signed: invalid invalidValue, err := hexutil.Decode("0x1234") @@ -158,8 +153,8 @@ func TestService_SignVerify(t *testing.T) { assert.NoError(t, err) docSrv.On("GetVersion", mock.Anything, mock.Anything).Return(oldInv, nil) - response, err = srv.DeriveFundingResponse(ctx, oldInv, fundingID) + _, signatures, err = srv.GetDataAndSignatures(ctx, oldInv, fundingID, "") assert.NoError(t, err) - assert.Equal(t, "false", response.Data.Signatures[0].Valid) - assert.Equal(t, "true", response.Data.Signatures[0].OutdatedSignature) + assert.Equal(t, "false", signatures[0].Valid) + assert.Equal(t, "true", signatures[0].OutdatedSignature) } diff --git a/extensions/funding/test_funding.go b/extensions/funding/test_funding.go new file mode 100644 index 000000000..0e5beb5e7 --- /dev/null +++ b/extensions/funding/test_funding.go @@ -0,0 +1,96 @@ +// +build unit integration testworld + +package funding + +import ( + "context" + "strings" + "testing" + "time" + + "github.com/centrifuge/go-centrifuge/documents" + "github.com/centrifuge/go-centrifuge/documents/invoice" + "github.com/centrifuge/go-centrifuge/extensions" + "github.com/centrifuge/go-centrifuge/identity" + "github.com/centrifuge/go-centrifuge/jobs" + testingidentity "github.com/centrifuge/go-centrifuge/testingutils/identity" + "github.com/centrifuge/go-centrifuge/utils" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func (b Bootstrapper) TestBootstrap(context map[string]interface{}) error { + return b.Bootstrap(context) +} + +func (Bootstrapper) TestTearDown() error { + return nil +} + +type MockService struct { + Service + mock.Mock +} + +func (m *MockService) Sign(ctx context.Context, fundingID string, identifier []byte) (documents.Model, error) { + args := m.Called(ctx, fundingID, identifier) + model, _ := args.Get(0).(documents.Model) + return model, args.Error(1) +} + +func (m *MockService) CreateFundingAgreement(ctx context.Context, docID []byte, data *Data) (documents.Model, jobs.JobID, error) { + args := m.Called(ctx, docID, data) + model, _ := args.Get(0).(documents.Model) + jobID, _ := args.Get(1).(jobs.JobID) + return model, jobID, args.Error(2) +} + +func (m *MockService) UpdateFundingAgreement(ctx context.Context, docID, fundingID []byte, data *Data) (documents.Model, jobs.JobID, error) { + args := m.Called(ctx, docID, fundingID, data) + model, _ := args.Get(0).(documents.Model) + jobID, _ := args.Get(1).(jobs.JobID) + return model, jobID, args.Error(2) +} + +func (m *MockService) SignFundingAgreement(ctx context.Context, docID, fundingID []byte) (documents.Model, jobs.JobID, error) { + args := m.Called(ctx, docID, fundingID) + model, _ := args.Get(0).(documents.Model) + jobID, _ := args.Get(1).(jobs.JobID) + return model, jobID, args.Error(2) +} + +func (m *MockService) GetDataAndSignatures(ctx context.Context, model documents.Model, fundingID, idx string) (Data, []Signature, error) { + args := m.Called(ctx, model, fundingID) + d, _ := args.Get(0).(Data) + sigs, _ := args.Get(1).([]Signature) + return d, sigs, args.Error(2) +} + +func CreateData() Data { + fundingId := extensions.NewAttributeSetID() + return Data{ + AgreementID: fundingId, + Currency: "eur", + Days: "90", + Amount: "1000", + RepaymentAmount: "1200.12", + Fee: "10", + BorrowerID: strings.ToLower(testingidentity.GenerateRandomDID().String()), + FunderID: strings.ToLower(testingidentity.GenerateRandomDID().String()), + NFTAddress: hexutil.Encode(utils.RandomSlice(32)), + RepaymentDueDate: time.Now().UTC().Format(time.RFC3339), + RepaymentOccurredDate: time.Now().UTC().Format(time.RFC3339), + PaymentDetailsID: hexutil.Encode(utils.RandomSlice(32)), + } +} + +func CreateInvoiceWithFunding(t *testing.T, ctx context.Context, did identity.DID) (*invoice.Invoice, string) { + data := CreateData() + inv, _ := invoice.CreateInvoiceWithEmbedCD(t, ctx, did, nil) + attrs, err := extensions.CreateAttributesList(inv, data, fundingFieldKey, AttrFundingLabel) + assert.NoError(t, err) + err = inv.AddAttributes(documents.CollaboratorsAccess{}, false, attrs...) + assert.NoError(t, err) + return inv, data.AgreementID +} diff --git a/extensions/funding/validator.go b/extensions/funding/validator.go deleted file mode 100644 index 74e95b94c..000000000 --- a/extensions/funding/validator.go +++ /dev/null @@ -1,20 +0,0 @@ -package funding - -import ( - "github.com/centrifuge/go-centrifuge/documents" -) - -// fieldValidateFunc validates the fields of the funding extension -func fieldValidator() documents.Validator { - return documents.ValidatorFunc(func(_, new documents.Model) error { - //todo implement validator - return nil - }) -} - -// CreateValidator returns a validator group that should be run before adding the funding extension -func CreateValidator() documents.ValidatorGroup { - return documents.ValidatorGroup{ - fieldValidator(), - } -} diff --git a/extensions/transferdetails/bootstrapper.go b/extensions/transferdetails/bootstrapper.go index d181ff7f4..b57591648 100644 --- a/extensions/transferdetails/bootstrapper.go +++ b/extensions/transferdetails/bootstrapper.go @@ -4,6 +4,7 @@ import ( "github.com/centrifuge/go-centrifuge/bootstrap" "github.com/centrifuge/go-centrifuge/documents" "github.com/centrifuge/go-centrifuge/errors" + "github.com/centrifuge/go-centrifuge/httpapi/coreapi" ) const ( @@ -22,9 +23,9 @@ func (Bootstrapper) Bootstrap(ctx map[string]interface{}) (err error) { } }() - docSrv, ok := ctx[documents.BootstrappedDocumentService].(documents.Service) + coreAPISrv, ok := ctx[coreapi.BootstrappedCoreAPIService].(coreapi.Service) if !ok { - return errors.New("document service not initialised") + return errors.New("core-api service not initialised") } tokenRegistry, ok := ctx[bootstrap.BootstrappedInvoiceUnpaid].(documents.TokenRegistry) @@ -32,7 +33,7 @@ func (Bootstrapper) Bootstrap(ctx map[string]interface{}) (err error) { return errors.New("token registry not initialisation") } - srv := DefaultService(docSrv, tokenRegistry) + srv := DefaultService(coreAPISrv, tokenRegistry) ctx[BootstrappedTransferDetailService] = srv return nil } diff --git a/extensions/transferdetails/bootstrapper_test.go b/extensions/transferdetails/bootstrapper_test.go index 7e72b7611..6c1e17e0b 100644 --- a/extensions/transferdetails/bootstrapper_test.go +++ b/extensions/transferdetails/bootstrapper_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/centrifuge/go-centrifuge/bootstrap" - "github.com/centrifuge/go-centrifuge/documents" + "github.com/centrifuge/go-centrifuge/httpapi/coreapi" "github.com/centrifuge/go-centrifuge/testingutils/documents" "github.com/stretchr/testify/assert" ) @@ -18,10 +18,10 @@ func TestBootstrapper_Bootstrap(t *testing.T) { // missing doc service err := b.Bootstrap(ctx) assert.Error(t, err) - assert.Contains(t, err.Error(), "document service not initialised") + assert.Contains(t, err.Error(), "core-api service not initialised") // missing token registry - ctx[documents.BootstrappedDocumentService] = new(testingdocuments.MockService) + ctx[coreapi.BootstrappedCoreAPIService] = coreapi.Service{} err = b.Bootstrap(ctx) assert.Error(t, err) assert.Contains(t, err.Error(), "token registry not initialisation") diff --git a/extensions/transferdetails/service.go b/extensions/transferdetails/service.go index 4fc43a77c..b4dd57af0 100644 --- a/extensions/transferdetails/service.go +++ b/extensions/transferdetails/service.go @@ -8,6 +8,7 @@ import ( "github.com/centrifuge/go-centrifuge/documents" "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/go-centrifuge/extensions" + "github.com/centrifuge/go-centrifuge/httpapi/coreapi" "github.com/centrifuge/go-centrifuge/identity" "github.com/centrifuge/go-centrifuge/jobs" "github.com/ethereum/go-ethereum/common/hexutil" @@ -32,7 +33,7 @@ type Service interface { // service implements Service and handles all funding related persistence and validations type service struct { - srv documents.Service + coreAPISrv coreapi.Service tokenRegistry documents.TokenRegistry } @@ -44,11 +45,11 @@ const ( // DefaultService returns the default implementation of the service. func DefaultService( - srv documents.Service, + srv coreapi.Service, tokenRegistry documents.TokenRegistry, ) Service { return service{ - srv: srv, + coreAPISrv: srv, tokenRegistry: tokenRegistry, } } @@ -56,7 +57,7 @@ func DefaultService( var log = logging.Logger("transferdetail-api") // TODO: get rid of this or make generic -func deriveDIDs(data *TransferDetailData) ([]identity.DID, error) { +func deriveDIDs(data *Data) ([]identity.DID, error) { var c []identity.DID for _, id := range []string{data.SenderID, data.RecipientID} { if id != "" { @@ -77,17 +78,14 @@ func (s service) updateModel(ctx context.Context, model documents.Model) (docume return nil, jobs.NilJobID(), err } - a := model.GetAttributes() - attr, err := extensions.ToMapAttributes(a) - if err != nil { - return nil, jobs.NilJobID(), err - } - d, err := json.Marshal(model.GetData()) if err != nil { return nil, jobs.NilJobID(), err } + a := model.GetAttributes() + attr := extensions.ToMapAttributes(a) + payload := documents.UpdatePayload{ DocumentID: model.ID(), CreatePayload: documents.CreatePayload{ @@ -98,9 +96,7 @@ func (s service) updateModel(ctx context.Context, model documents.Model) (docume }, } - // TODO: use coreapi.UpdateDocument - //updated, jobID, err := coreapi.Service.UpdateDocument(ctx, payload) - updated, jobID, err := s.srv.UpdateModel(ctx, payload) + updated, jobID, err := s.coreAPISrv.UpdateDocument(ctx, payload) if err != nil { return nil, jobID, err } @@ -134,7 +130,7 @@ func (s service) deriveFromPayload(ctx context.Context, req CreateTransferDetail return nil, err } - model, err = s.srv.GetCurrentVersion(ctx, docID) + model, err = s.coreAPISrv.GetDocument(ctx, docID) if err != nil { log.Error(err) return nil, documents.ErrDocumentNotFound @@ -197,7 +193,7 @@ func (s service) deriveFromUpdatePayload(ctx context.Context, req UpdateTransfer return nil, err } - model, err = s.srv.GetCurrentVersion(ctx, docID) + model, err = s.coreAPISrv.GetDocument(ctx, docID) if err != nil { log.Error(err) return nil, documents.ErrDocumentNotFound @@ -216,7 +212,7 @@ func (s service) deriveFromUpdatePayload(ctx context.Context, req UpdateTransfer // overwriting is not enough because it is not required that // the TransferDetail payload contains all TransferDetail attributes - model, err = extensions.DeleteAttributesSet(model, TransferDetailData{}, idx, transfersFieldKey) + model, err = extensions.DeleteAttributesSet(model, Data{}, idx, transfersFieldKey) if err != nil { return nil, err } @@ -247,7 +243,7 @@ func (s service) deriveFromUpdatePayload(ctx context.Context, req UpdateTransfer } // TODO: move to generic function in attribute utils -func (s service) findTransfer(model documents.Model, transferID string) (*TransferDetailData, error) { +func (s service) findTransfer(model documents.Model, transferID string) (*Data, error) { idx, err := extensions.FindAttributeSetIDX(model, transferID, transfersLabel, transferIDLabel, transfersFieldKey) if err != nil { return nil, err @@ -256,8 +252,8 @@ func (s service) findTransfer(model documents.Model, transferID string) (*Transf } // TODO: move to generic function in attribute utils -func (s service) deriveTransferData(model documents.Model, idx string) (*TransferDetailData, error) { - data := new(TransferDetailData) +func (s service) deriveTransferData(model documents.Model, idx string) (*Data, error) { + data := new(Data) types := reflect.TypeOf(*data) for i := 0; i < types.NumField(); i++ { @@ -320,7 +316,7 @@ func (s service) DeriveTransferList(ctx context.Context, model documents.Model) if !model.AttributeExists(fl) { return &TransferDetailList{ Data: nil, - }, nil, nil + }, model, nil } lastIdx, err := extensions.GetArrayLatestIDX(model, transfersLabel) diff --git a/extensions/transferdetails/service_test.go b/extensions/transferdetails/service_test.go index f36c6877b..544028a5a 100644 --- a/extensions/transferdetails/service_test.go +++ b/extensions/transferdetails/service_test.go @@ -18,6 +18,7 @@ import ( "github.com/centrifuge/go-centrifuge/documents/invoice" "github.com/centrifuge/go-centrifuge/ethereum" "github.com/centrifuge/go-centrifuge/extensions" + "github.com/centrifuge/go-centrifuge/httpapi/coreapi" "github.com/centrifuge/go-centrifuge/identity/ideth" "github.com/centrifuge/go-centrifuge/jobs" "github.com/centrifuge/go-centrifuge/p2p" @@ -35,12 +36,11 @@ import ( var ctx = map[string]interface{}{} var cfg config.Configuration +var did = testingidentity.GenerateRandomDID() -var ( - did = testingidentity.GenerateRandomDID() -) - -var configService config.Service +func newCoreAPIService(docSrv documents.Service) coreapi.Service { + return coreapi.NewService(docSrv, nil, nil, nil) +} func TestMain(m *testing.M) { ethClient := new(ethereum.MockEthClient) @@ -48,7 +48,7 @@ func TestMain(m *testing.M) { ctx[ethereum.BootstrappedEthereumClient] = ethClient jobMan := &testingjobs.MockJobManager{} ctx[jobs.BootstrappedService] = jobMan - done := make(chan bool) + done := make(chan error) jobMan.On("ExecuteWithinJob", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(jobs.NilJobID(), done, nil) ctx[bootstrap.BootstrappedInvoiceUnpaid] = new(testingdocuments.MockRegistry) ibootstrappers := []bootstrap.TestBootstrapper{ @@ -68,7 +68,6 @@ func TestMain(m *testing.M) { bootstrap.RunTestBootstrappers(ibootstrappers, ctx) cfg = ctx[bootstrap.BootstrappedConfig].(config.Configuration) cfg.Set("identityId", did.String()) - configService = ctx[config.BootstrappedConfigStorage].(config.Service) result := m.Run() bootstrap.RunTestTeardown(ibootstrappers) os.Exit(result) @@ -76,15 +75,12 @@ func TestMain(m *testing.M) { func TestDeriveFromPayload(t *testing.T) { ctxh := testingconfig.CreateAccountContext(t, cfg) - testingdocuments.CreateInvoicePayload() - inv := new(invoice.Invoice) - err := inv.InitInvoiceInput(testingdocuments.CreateInvoicePayload(), testingidentity.GenerateRandomDID()) - assert.NoError(t, err) + inv, _ := invoice.CreateInvoiceWithEmbedCD(t, nil, testingidentity.GenerateRandomDID(), nil) docSrv := new(testingdocuments.MockService) docSrv.On("GetCurrentVersion", mock.Anything, mock.Anything).Return(inv, nil) docSrv.On("UpdateModel", mock.Anything, mock.Anything).Return(inv, nil, nil) - srv := DefaultService(docSrv, nil) + srv := DefaultService(newCoreAPIService(docSrv), nil) payload := createTestPayload() payload.DocumentID = hexutil.Encode(inv.Document.DocumentIdentifier) @@ -102,18 +98,13 @@ func TestDeriveFromPayload(t *testing.T) { } func TestDeriveTransferResponse(t *testing.T) { - testingdocuments.CreateInvoicePayload() - inv := new(invoice.Invoice) - err := inv.InitInvoiceInput(testingdocuments.CreateInvoicePayload(), testingidentity.GenerateRandomDID()) - assert.NoError(t, err) + inv, _ := invoice.CreateInvoiceWithEmbedCD(t, nil, testingidentity.GenerateRandomDID(), nil) docSrv := new(testingdocuments.MockService) docSrv.On("GetCurrentVersion", mock.Anything, mock.Anything).Return(inv, nil) docSrv.On("UpdateModel", mock.Anything, mock.Anything).Return(inv, nil, nil) - srv := DefaultService(docSrv, nil) - + srv := DefaultService(newCoreAPIService(docSrv), nil) ctxh := testingconfig.CreateAccountContext(t, cfg) - for i := 0; i < 10; i++ { payload := createTestPayload() payload.DocumentID = hexutil.Encode(inv.Document.DocumentIdentifier) @@ -129,18 +120,26 @@ func TestDeriveTransferResponse(t *testing.T) { } -func TestDeriveTransferListResponse(t *testing.T) { - testingdocuments.CreateInvoicePayload() - inv := new(invoice.Invoice) - err := inv.InitInvoiceInput(testingdocuments.CreateInvoicePayload(), testingidentity.GenerateRandomDID()) +func TestService_DeriveTransferListWithNoAttributes(t *testing.T) { + inv, _ := invoice.CreateInvoiceWithEmbedCD(t, nil, testingidentity.GenerateRandomDID(), nil) + docSrv := new(testingdocuments.MockService) + srv := DefaultService(newCoreAPIService(docSrv), nil) + response, m, err := srv.DeriveTransferList(context.Background(), inv) + assert.NotNil(t, response) + assert.NotNil(t, m) assert.NoError(t, err) +} + +func TestDeriveTransferListResponse(t *testing.T) { + inv, _ := invoice.CreateInvoiceWithEmbedCD(t, nil, testingidentity.GenerateRandomDID(), nil) docSrv := new(testingdocuments.MockService) docSrv.On("GetCurrentVersion", mock.Anything, mock.Anything).Return(inv, nil) docSrv.On("UpdateModel", mock.Anything, mock.Anything).Return(inv, nil, nil) - srv := DefaultService(docSrv, nil) + srv := DefaultService(newCoreAPIService(docSrv), nil) var model documents.Model + var err error var payloads []CreateTransferDetailRequest for i := 0; i < 10; i++ { p := createTestPayload() @@ -161,20 +160,17 @@ func TestDeriveTransferListResponse(t *testing.T) { } func TestService_DeriveFromUpdatePayload(t *testing.T) { - testingdocuments.CreateInvoicePayload() - inv := new(invoice.Invoice) - err := inv.InitInvoiceInput(testingdocuments.CreateInvoicePayload(), testingidentity.GenerateRandomDID()) - assert.NoError(t, err) + inv, _ := invoice.CreateInvoiceWithEmbedCD(t, nil, testingidentity.GenerateRandomDID(), nil) docSrv := new(testingdocuments.MockService) docSrv.On("GetCurrentVersion", mock.Anything, mock.Anything).Return(inv, nil) docSrv.On("UpdateModel", mock.Anything, mock.Anything).Return(inv, nil, nil) - srv := DefaultService(docSrv, nil) + srv := DefaultService(newCoreAPIService(docSrv), nil) var model documents.Model p := createTestPayload() p.DocumentID = hexutil.Encode(inv.Document.DocumentIdentifier) - model, _, err = srv.CreateTransferDetail(context.Background(), p) + model, _, err := srv.CreateTransferDetail(context.Background(), p) assert.NoError(t, err) // update @@ -202,9 +198,9 @@ func TestService_DeriveFromUpdatePayload(t *testing.T) { assert.Contains(t, err, extensions.ErrAttributeSetNotFound) } -func createTestData() TransferDetailData { +func createTestData() Data { transferID := extensions.NewAttributeSetID() - return TransferDetailData{ + return Data{ TransferID: transferID, SenderID: testingidentity.GenerateRandomDID().String(), RecipientID: testingidentity.GenerateRandomDID().String(), @@ -224,7 +220,7 @@ func createTestPayload() CreateTransferDetailRequest { return CreateTransferDetailRequest{Data: createTestData()} } -func checkResponse(t *testing.T, payload CreateTransferDetailRequest, response *TransferDetailData) { +func checkResponse(t *testing.T, payload CreateTransferDetailRequest, response *Data) { assert.Equal(t, payload.Data.TransferID, response.TransferID) assert.Equal(t, payload.Data.Currency, response.Currency) assert.Equal(t, payload.Data.Status, response.Status) diff --git a/extensions/transferdetails/transferdetails.go b/extensions/transferdetails/transferdetails.go index 5b309f05d..12efce4c1 100644 --- a/extensions/transferdetails/transferdetails.go +++ b/extensions/transferdetails/transferdetails.go @@ -1,7 +1,7 @@ package transferdetails -// TransferDetailData is the default transfer details extension schema -type TransferDetailData struct { +// Data is the default transfer details extension schema +type Data struct { TransferID string `json:"transfer_id,omitempty" attr:"bytes"` SenderID string `json:"sender_id,omitempty" attr:"bytes"` RecipientID string `json:"recipient_id,omitempty" attr:"bytes"` @@ -20,22 +20,22 @@ type TransferDetailData struct { // CreateTransferDetailRequest holds the required fields to create a new transfer agreement type CreateTransferDetailRequest struct { DocumentID string - Data TransferDetailData + Data Data } // UpdateTransferDetailRequest holds the required fields to update a transfer agreement type UpdateTransferDetailRequest struct { DocumentID string TransferID string - Data TransferDetailData + Data Data } // TransferDetail holds a TransferDetail response type TransferDetail struct { - Data TransferDetailData + Data Data } // TransferDetailList holds a list of TransferDetails type TransferDetailList struct { - Data []TransferDetailData + Data []Data } diff --git a/httpapi/coreapi/accounts_api.go b/httpapi/coreapi/accounts_api.go index 4d8b33846..c59bd1770 100644 --- a/httpapi/coreapi/accounts_api.go +++ b/httpapi/coreapi/accounts_api.go @@ -12,8 +12,13 @@ import ( "github.com/go-chi/render" ) -// ErrAccountIDInvalid is a sentinel error for invalid account IDs. -const ErrAccountIDInvalid = errors.Error("account ID is invalid") +const ( + // ErrAccountIDInvalid is a sentinel error for invalid account IDs. + ErrAccountIDInvalid = errors.Error("account ID is invalid") + + // ErrAccountNotFound is a sentinel error for when account is missing. + ErrAccountNotFound = errors.Error("account not found") +) // SignPayload signs the payload and returns the signature. // @summary Signs and returns the signature of the Payload. @@ -70,3 +75,199 @@ func (h handler) SignPayload(w http.ResponseWriter, r *http.Request) { SignerID: sig.SignerId, }) } + +// GetAccount returns the account associated with accountID. +// @summary Returns the account associated with accountID. +// @description Returns the account associated with accountID. +// @id get_account +// @tags Accounts +// @param account_id path string true "Account ID" +// @produce json +// @Failure 400 {object} httputils.HTTPError +// @Failure 404 {object} httputils.HTTPError +// @success 200 {object} coreapi.Account +// @router /v1/accounts/{account_id} [get] +func (h handler) GetAccount(w http.ResponseWriter, r *http.Request) { + var err error + var code int + defer httputils.RespondIfError(&code, &err, w, r) + + accID, err := hexutil.Decode(chi.URLParam(r, accountIDParam)) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + err = ErrAccountIDInvalid + return + } + + acc, err := h.srv.GetAccount(accID) + if err != nil { + code = http.StatusNotFound + log.Error(err) + err = ErrAccountNotFound + return + } + + render.Status(r, http.StatusOK) + render.JSON(w, r, toClientAccount(acc)) +} + +// GenerateAccount generates a new account with defaults. +// @summary Generates a new account with defaults. +// @description Generates a new account with defaults. +// @id generate_account +// @tags Accounts +// @produce json +// @Failure 500 {object} httputils.HTTPError +// @success 200 {object} coreapi.Account +// @router /v1/accounts/generate [post] +func (h handler) GenerateAccount(w http.ResponseWriter, r *http.Request) { + var err error + var code int + defer httputils.RespondIfError(&code, &err, w, r) + + acc, err := h.srv.GenerateAccount() + if err != nil { + code = http.StatusInternalServerError + log.Error(err) + return + } + + render.Status(r, http.StatusOK) + render.JSON(w, r, toClientAccount(acc)) +} + +// GetAccounts returns all the accounts in the node. +// @summary Returns all the accounts in the node. +// @description Returns all the accounts in the node. +// @id get_accounts +// @tags Accounts +// @produce json +// @Failure 500 {object} httputils.HTTPError +// @success 200 {object} coreapi.Accounts +// @router /v1/accounts [get] +func (h handler) GetAccounts(w http.ResponseWriter, r *http.Request) { + var err error + var code int + defer httputils.RespondIfError(&code, &err, w, r) + + accs, err := h.srv.GetAccounts() + if err != nil { + code = http.StatusInternalServerError + log.Error(err) + return + } + + render.Status(r, http.StatusOK) + render.JSON(w, r, toClientAccounts(accs)) +} + +// CreateAccount creates a new account. +// @summary Creates a new account without any default configurations. +// @description Creates a new account without any default configurations. +// @id create_account +// @tags Accounts +// @produce json +// @param body body coreapi.Account true "Account Create request" +// @Failure 400 {object} httputils.HTTPError +// @Failure 500 {object} httputils.HTTPError +// @success 200 {object} coreapi.Account +// @router /v1/accounts [post] +func (h handler) CreateAccount(w http.ResponseWriter, r *http.Request) { + var err error + var code int + defer httputils.RespondIfError(&code, &err, w, r) + + data, err := ioutil.ReadAll(r.Body) + if err != nil { + code = http.StatusInternalServerError + log.Error(err) + return + } + + var cacc Account + err = json.Unmarshal(data, &cacc) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + return + } + + acc, err := fromClientAccount(cacc) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + return + } + + acc, err = h.srv.CreateAccount(acc) + if err != nil { + code = http.StatusInternalServerError + log.Error(err) + return + } + + render.Status(r, http.StatusOK) + render.JSON(w, r, toClientAccount(acc)) +} + +// UpdateAccount updates an existing account. +// @summary Updates an existing account. +// @description Updates an existing account. +// @id update_account +// @tags Accounts +// @produce json +// @param account_id path string true "Account ID" +// @param body body coreapi.Account true "Account Update request" +// @Failure 400 {object} httputils.HTTPError +// @Failure 404 {object} httputils.HTTPError +// @Failure 500 {object} httputils.HTTPError +// @success 200 {object} coreapi.Account +// @router /v1/accounts/{account_id} [put] +func (h handler) UpdateAccount(w http.ResponseWriter, r *http.Request) { + var err error + var code int + defer httputils.RespondIfError(&code, &err, w, r) + + accID, err := hexutil.Decode(chi.URLParam(r, accountIDParam)) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + err = ErrAccountIDInvalid + return + } + + data, err := ioutil.ReadAll(r.Body) + if err != nil { + code = http.StatusInternalServerError + log.Error(err) + return + } + + var cacc Account + err = json.Unmarshal(data, &cacc) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + return + } + + cacc.IdentityID = accID + acc, err := fromClientAccount(cacc) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + return + } + + acc, err = h.srv.UpdateAccount(acc) + if err != nil { + code = http.StatusNotFound + log.Error(err) + err = ErrAccountNotFound + return + } + + render.Status(r, http.StatusOK) + render.JSON(w, r, toClientAccount(acc)) +} diff --git a/httpapi/coreapi/accounts_api_test.go b/httpapi/coreapi/accounts_api_test.go index 7525aea7c..5eecac1d7 100644 --- a/httpapi/coreapi/accounts_api_test.go +++ b/httpapi/coreapi/accounts_api_test.go @@ -10,14 +10,19 @@ import ( "net/http" "net/http/httptest" "testing" + "time" coredocumentpb "github.com/centrifuge/centrifuge-protobufs/gen/go/coredocument" + "github.com/centrifuge/go-centrifuge/config" "github.com/centrifuge/go-centrifuge/config/configstore" "github.com/centrifuge/go-centrifuge/errors" + testingconfig "github.com/centrifuge/go-centrifuge/testingutils/config" "github.com/centrifuge/go-centrifuge/utils" + "github.com/centrifuge/go-centrifuge/utils/byteutils" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/go-chi/chi" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" ) func TestHandler_SignPayload(t *testing.T) { @@ -61,7 +66,7 @@ func TestHandler_SignPayload(t *testing.T) { assert.NoError(t, err) srv := new(configstore.MockService) srv.On("Sign", accountID, payload).Return(nil, errors.New("failed to sign payload")).Once() - h.srv.accountsService = srv + h.srv.accountsSrv = srv w, r = getHTTPReqAndResp(ctx, bytes.NewReader(d)) h.SignPayload(w, r) assert.Equal(t, w.Code, http.StatusBadRequest) @@ -77,7 +82,7 @@ func TestHandler_SignPayload(t *testing.T) { Signature: signature, PublicKey: pk, }, nil).Once() - h.srv.accountsService = srv + h.srv.accountsSrv = srv w, r = getHTTPReqAndResp(ctx, bytes.NewReader(d)) h.SignPayload(w, r) assert.Equal(t, w.Code, http.StatusOK) @@ -87,3 +92,330 @@ func TestHandler_SignPayload(t *testing.T) { assert.Contains(t, w.Body.String(), hexutil.Encode(accountID)) srv.AssertExpectations(t) } + +func TestHandler_GetAccount(t *testing.T) { + getHTTPReqAndResp := func(ctx context.Context) (*httptest.ResponseRecorder, *http.Request) { + return httptest.NewRecorder(), httptest.NewRequest("GET", "/accounts/{account_id}", nil).WithContext(ctx) + } + // empty account_id + rctx := chi.NewRouteContext() + rctx.URLParams.Keys = make([]string, 1, 1) + rctx.URLParams.Values = make([]string, 1, 1) + rctx.URLParams.Keys[0] = accountIDParam + rctx.URLParams.Values[0] = "" + ctx := context.WithValue(context.Background(), chi.RouteCtxKey, rctx) + w, r := getHTTPReqAndResp(ctx) + h := handler{} + h.GetAccount(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), ErrAccountIDInvalid.Error()) + + // invalid account id + rctx.URLParams.Values[0] = "invalid value" + w, r = getHTTPReqAndResp(ctx) + h.GetAccount(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), ErrAccountIDInvalid.Error()) + + // missing account + accountID := utils.RandomSlice(20) + rctx.URLParams.Values[0] = hexutil.Encode(accountID) + srv := new(configstore.MockService) + srv.On("GetAccount", accountID).Return(nil, errors.New("failed to get account")).Once() + h.srv.accountsSrv = srv + w, r = getHTTPReqAndResp(ctx) + h.GetAccount(w, r) + srv.AssertExpectations(t) + assert.Equal(t, w.Code, http.StatusNotFound) + assert.Contains(t, w.Body.String(), ErrAccountNotFound.Error()) + + // success + cfg := new(testingconfig.MockConfig) + cfg.On("GetEthereumAccount", "name").Return(&config.AccountConfig{}, nil).Once() + cfg.On("GetEthereumDefaultAccountName").Return("dummyAcc").Once() + cfg.On("GetReceiveEventNotificationEndpoint").Return("dummyNotifier").Once() + cfg.On("GetIdentityID").Return(accountID, nil).Once() + cfg.On("GetP2PKeyPair").Return("pub", "priv").Once() + cfg.On("GetSigningKeyPair").Return("pub", "priv").Once() + cfg.On("GetEthereumContextWaitTimeout").Return(time.Second).Once() + cfg.On("GetPrecommitEnabled").Return(true).Once() + acc, err := configstore.NewAccount("name", cfg) + assert.NoError(t, err) + srv = new(configstore.MockService) + srv.On("GetAccount", accountID).Return(acc, nil).Once() + h.srv.accountsSrv = srv + w, r = getHTTPReqAndResp(ctx) + h.GetAccount(w, r) + srv.AssertExpectations(t) + cfg.AssertExpectations(t) + assert.Equal(t, w.Code, http.StatusOK) + assert.Contains(t, w.Body.String(), hexutil.Encode(accountID)) +} + +func TestHandler_GenerateAccount(t *testing.T) { + getHTTPReqAndResp := func(ctx context.Context) (*httptest.ResponseRecorder, *http.Request) { + return httptest.NewRecorder(), httptest.NewRequest("POST", "/accounts/generate", nil).WithContext(ctx) + } + + // failed generation + rctx := chi.NewRouteContext() + ctx := context.WithValue(context.Background(), chi.RouteCtxKey, rctx) + h := handler{} + srv := new(configstore.MockService) + srv.On("GenerateAccount").Return(nil, errors.New("failed to generate account")).Once() + h.srv.accountsSrv = srv + w, r := getHTTPReqAndResp(ctx) + h.GenerateAccount(w, r) + assert.Equal(t, w.Code, http.StatusInternalServerError) + assert.Contains(t, w.Body.String(), "failed to generate account") + srv.AssertExpectations(t) + + // success + accountID := utils.RandomSlice(20) + cfg := new(testingconfig.MockConfig) + cfg.On("GetEthereumAccount", "name").Return(&config.AccountConfig{}, nil).Once() + cfg.On("GetEthereumDefaultAccountName").Return("dummyAcc").Once() + cfg.On("GetReceiveEventNotificationEndpoint").Return("dummyNotifier").Once() + cfg.On("GetIdentityID").Return(accountID, nil).Once() + cfg.On("GetP2PKeyPair").Return("pub", "priv").Once() + cfg.On("GetSigningKeyPair").Return("pub", "priv").Once() + cfg.On("GetEthereumContextWaitTimeout").Return(time.Second).Once() + cfg.On("GetPrecommitEnabled").Return(true).Once() + acc, err := configstore.NewAccount("name", cfg) + assert.NoError(t, err) + srv = new(configstore.MockService) + srv.On("GenerateAccount").Return(acc, nil).Once() + h.srv.accountsSrv = srv + w, r = getHTTPReqAndResp(ctx) + h.GenerateAccount(w, r) + srv.AssertExpectations(t) + cfg.AssertExpectations(t) + assert.Equal(t, w.Code, http.StatusOK) + assert.Contains(t, w.Body.String(), hexutil.Encode(accountID)) +} + +func TestHandler_GetAccounts(t *testing.T) { + getHTTPReqAndResp := func(ctx context.Context) (*httptest.ResponseRecorder, *http.Request) { + return httptest.NewRecorder(), httptest.NewRequest("GET", "/accounts", nil).WithContext(ctx) + } + + // failed generation + rctx := chi.NewRouteContext() + ctx := context.WithValue(context.Background(), chi.RouteCtxKey, rctx) + h := handler{} + srv := new(configstore.MockService) + srv.On("GetAccounts").Return(nil, errors.New("failed to get accounts")).Once() + h.srv.accountsSrv = srv + w, r := getHTTPReqAndResp(ctx) + h.GetAccounts(w, r) + assert.Equal(t, w.Code, http.StatusInternalServerError) + assert.Contains(t, w.Body.String(), "failed to get accounts") + srv.AssertExpectations(t) + + // success + accountID := utils.RandomSlice(20) + cfg := new(testingconfig.MockConfig) + cfg.On("GetEthereumAccount", "name").Return(&config.AccountConfig{}, nil).Once() + cfg.On("GetEthereumDefaultAccountName").Return("dummyAcc").Once() + cfg.On("GetReceiveEventNotificationEndpoint").Return("dummyNotifier").Once() + cfg.On("GetIdentityID").Return(accountID, nil).Once() + cfg.On("GetP2PKeyPair").Return("pub", "priv").Once() + cfg.On("GetSigningKeyPair").Return("pub", "priv").Once() + cfg.On("GetEthereumContextWaitTimeout").Return(time.Second).Once() + cfg.On("GetPrecommitEnabled").Return(true).Once() + acc, err := configstore.NewAccount("name", cfg) + assert.NoError(t, err) + srv = new(configstore.MockService) + srv.On("GetAccounts").Return([]config.Account{acc}, nil).Once() + h.srv.accountsSrv = srv + w, r = getHTTPReqAndResp(ctx) + h.GetAccounts(w, r) + srv.AssertExpectations(t) + cfg.AssertExpectations(t) + assert.Equal(t, w.Code, http.StatusOK) + assert.Contains(t, w.Body.String(), hexutil.Encode(accountID)) +} + +func marshall(t *testing.T, data map[string]interface{}) []byte { + d, err := json.Marshal(data) + assert.NoError(t, err) + return d +} + +func TestHandler_CreateAccount(t *testing.T) { + getHTTPReqAndResp := func(ctx context.Context, body io.Reader) (*httptest.ResponseRecorder, *http.Request) { + return httptest.NewRecorder(), httptest.NewRequest("POST", "/accounts", body).WithContext(ctx) + } + + // empty body + rctx := chi.NewRouteContext() + ctx := context.WithValue(context.Background(), chi.RouteCtxKey, rctx) + h := handler{} + w, r := getHTTPReqAndResp(ctx, nil) + h.CreateAccount(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), "unexpected end of JSON input") + + // missing ethereum key and address + data := map[string]interface{}{ + "eth_account": map[string]string{}, + } + d := marshall(t, data) + w, r = getHTTPReqAndResp(ctx, bytes.NewReader(d)) + h.CreateAccount(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), "ethereum address/key cannot be empty") + + // invalid p2p key pair + addr := byteutils.HexBytes(utils.RandomSlice(20)) + key := byteutils.HexBytes(utils.RandomSlice(32)) + data["eth_account"] = map[string]string{ + "address": addr.String(), + "key": key.String(), + } + w, r = getHTTPReqAndResp(ctx, bytes.NewReader(marshall(t, data))) + h.CreateAccount(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), "p2p key pair is invalid") + + randomKP := func() KeyPair { + return KeyPair{Pub: "pub", Pvt: "prv"} + } + + // invalid signing key pair + data["p2p_key_pair"] = randomKP() + w, r = getHTTPReqAndResp(ctx, bytes.NewReader(marshall(t, data))) + h.CreateAccount(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), "signing key pair is invalid") + + // invalid identity id + data["signing_key_pair"] = randomKP() + w, r = getHTTPReqAndResp(ctx, bytes.NewReader(marshall(t, data))) + h.CreateAccount(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), "Identity ID cannot be empty") + + // create account failed + id := hexutil.Bytes(utils.RandomSlice(20)) + data["identity_id"] = id.String() + srv := new(configstore.MockService) + srv.On("CreateAccount", mock.Anything).Return(nil, errors.New("failed to create account")).Once() + h.srv.accountsSrv = srv + w, r = getHTTPReqAndResp(ctx, bytes.NewReader(marshall(t, data))) + h.CreateAccount(w, r) + assert.Equal(t, w.Code, http.StatusInternalServerError) + assert.Contains(t, w.Body.String(), "failed to create account") + srv.AssertExpectations(t) + + // success + cfg := new(testingconfig.MockConfig) + cfg.On("GetEthereumAccount", "name").Return(&config.AccountConfig{Address: addr.String(), Key: key.String()}, nil).Once() + cfg.On("GetEthereumDefaultAccountName").Return("dummyAcc").Once() + cfg.On("GetReceiveEventNotificationEndpoint").Return("dummyNotifier").Once() + cfg.On("GetIdentityID").Return([]byte(id), nil).Once() + cfg.On("GetP2PKeyPair").Return("pub", "prv").Once() + cfg.On("GetSigningKeyPair").Return("pub", "prv").Once() + cfg.On("GetEthereumContextWaitTimeout").Return(time.Second).Once() + cfg.On("GetPrecommitEnabled").Return(true).Once() + acc, err := configstore.NewAccount("name", cfg) + assert.NoError(t, err) + srv = new(configstore.MockService) + srv.On("CreateAccount", mock.Anything).Return(acc, nil).Once() + h.srv.accountsSrv = srv + w, r = getHTTPReqAndResp(ctx, bytes.NewReader(marshall(t, data))) + h.CreateAccount(w, r) + assert.Equal(t, w.Code, http.StatusOK) + assert.Contains(t, w.Body.String(), id.String()) +} + +func TestHandler_UpdateAccount(t *testing.T) { + getHTTPReqAndResp := func(ctx context.Context, body io.Reader) (*httptest.ResponseRecorder, *http.Request) { + return httptest.NewRecorder(), httptest.NewRequest("PUT", "/accounts/{account_id}", body).WithContext(ctx) + } + + // invalid account ID + rctx := chi.NewRouteContext() + rctx.URLParams.Keys = make([]string, 1, 1) + rctx.URLParams.Values = make([]string, 1, 1) + rctx.URLParams.Keys[0] = accountIDParam + rctx.URLParams.Values[0] = "" + ctx := context.WithValue(context.Background(), chi.RouteCtxKey, rctx) + h := handler{} + w, r := getHTTPReqAndResp(ctx, nil) + h.UpdateAccount(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), ErrAccountIDInvalid.Error()) + + // empty body + id := hexutil.Bytes(utils.RandomSlice(20)) + rctx.URLParams.Values[0] = id.String() + w, r = getHTTPReqAndResp(ctx, nil) + h.UpdateAccount(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), "unexpected end of JSON input") + + // missing ethereum key and address + data := map[string]interface{}{ + "eth_account": map[string]string{}, + } + d := marshall(t, data) + w, r = getHTTPReqAndResp(ctx, bytes.NewReader(d)) + h.UpdateAccount(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), "ethereum address/key cannot be empty") + + // invalid p2p key pair + addr := byteutils.HexBytes(utils.RandomSlice(20)) + key := byteutils.HexBytes(utils.RandomSlice(32)) + data["eth_account"] = map[string]string{ + "address": addr.String(), + "key": key.String(), + } + w, r = getHTTPReqAndResp(ctx, bytes.NewReader(marshall(t, data))) + h.UpdateAccount(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), "p2p key pair is invalid") + + randomKP := func() KeyPair { + return KeyPair{Pub: "pub", Pvt: "prv"} + } + + // invalid signing key pair + data["p2p_key_pair"] = randomKP() + w, r = getHTTPReqAndResp(ctx, bytes.NewReader(marshall(t, data))) + h.UpdateAccount(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), "signing key pair is invalid") + + // update account failed + data["signing_key_pair"] = randomKP() + srv := new(configstore.MockService) + srv.On("UpdateAccount", mock.Anything).Return(nil, errors.New("failed to update account")).Once() + h.srv.accountsSrv = srv + w, r = getHTTPReqAndResp(ctx, bytes.NewReader(marshall(t, data))) + h.UpdateAccount(w, r) + assert.Equal(t, w.Code, http.StatusNotFound) + assert.Contains(t, w.Body.String(), ErrAccountNotFound.Error()) + srv.AssertExpectations(t) + + // success + cfg := new(testingconfig.MockConfig) + cfg.On("GetEthereumAccount", "name").Return(&config.AccountConfig{Address: addr.String(), Key: key.String()}, nil).Once() + cfg.On("GetEthereumDefaultAccountName").Return("dummyAcc").Once() + cfg.On("GetReceiveEventNotificationEndpoint").Return("dummyNotifier").Once() + cfg.On("GetIdentityID").Return([]byte(id), nil).Once() + cfg.On("GetP2PKeyPair").Return("pub", "prv").Once() + cfg.On("GetSigningKeyPair").Return("pub", "prv").Once() + cfg.On("GetEthereumContextWaitTimeout").Return(time.Second).Once() + cfg.On("GetPrecommitEnabled").Return(true).Once() + acc, err := configstore.NewAccount("name", cfg) + assert.NoError(t, err) + srv = new(configstore.MockService) + srv.On("UpdateAccount", mock.Anything).Return(acc, nil).Once() + h.srv.accountsSrv = srv + w, r = getHTTPReqAndResp(ctx, bytes.NewReader(marshall(t, data))) + h.UpdateAccount(w, r) + assert.Equal(t, w.Code, http.StatusOK) + assert.Contains(t, w.Body.String(), id.String()) +} diff --git a/httpapi/coreapi/bootstrapper.go b/httpapi/coreapi/bootstrapper.go new file mode 100644 index 000000000..6a95e5283 --- /dev/null +++ b/httpapi/coreapi/bootstrapper.go @@ -0,0 +1,47 @@ +package coreapi + +import ( + "github.com/centrifuge/go-centrifuge/bootstrap" + "github.com/centrifuge/go-centrifuge/config" + "github.com/centrifuge/go-centrifuge/documents" + "github.com/centrifuge/go-centrifuge/errors" + "github.com/centrifuge/go-centrifuge/jobs" + "github.com/centrifuge/go-centrifuge/nft" +) + +// BootstrappedCoreAPIService key maps to the Service implementation in Bootstrap context. +const BootstrappedCoreAPIService = "CoreAPI Service" + +// Bootstrapper implements bootstrap.Bootstrapper. +type Bootstrapper struct{} + +// Bootstrap adds transaction.Repository into context. +func (b Bootstrapper) Bootstrap(ctx map[string]interface{}) error { + docSrv, ok := ctx[documents.BootstrappedDocumentService].(documents.Service) + if !ok { + return errors.New("failed to get %s", documents.BootstrappedDocumentService) + } + + jobsMan, ok := ctx[jobs.BootstrappedService].(jobs.Manager) + if !ok { + return errors.New("failed to get %s", jobs.BootstrappedService) + } + + nftSrv, ok := ctx[bootstrap.BootstrappedInvoiceUnpaid].(nft.Service) + if !ok { + return errors.New("failed to get %s", bootstrap.BootstrappedInvoiceUnpaid) + } + + accountSrv, ok := ctx[config.BootstrappedConfigStorage].(config.Service) + if !ok { + return errors.New("failed to get %s", config.BootstrappedConfigStorage) + } + + ctx[BootstrappedCoreAPIService] = Service{ + docSrv: docSrv, + jobsSrv: jobsMan, + nftSrv: nftSrv, + accountsSrv: accountSrv, + } + return nil +} diff --git a/httpapi/coreapi/bootstrapper_test.go b/httpapi/coreapi/bootstrapper_test.go new file mode 100644 index 000000000..44a69fc20 --- /dev/null +++ b/httpapi/coreapi/bootstrapper_test.go @@ -0,0 +1,49 @@ +// +build unit + +package coreapi + +import ( + "testing" + + "github.com/centrifuge/go-centrifuge/bootstrap" + "github.com/centrifuge/go-centrifuge/config" + "github.com/centrifuge/go-centrifuge/config/configstore" + "github.com/centrifuge/go-centrifuge/documents" + "github.com/centrifuge/go-centrifuge/jobs" + testingdocuments "github.com/centrifuge/go-centrifuge/testingutils/documents" + testingnfts "github.com/centrifuge/go-centrifuge/testingutils/nfts" + "github.com/centrifuge/go-centrifuge/testingutils/testingjobs" + "github.com/stretchr/testify/assert" +) + +func TestBootstrapper_Bootstrap(t *testing.T) { + ctx := make(map[string]interface{}) + b := Bootstrapper{} + + // missing doc service + err := b.Bootstrap(ctx) + assert.Error(t, err) + assert.Contains(t, err.Error(), documents.BootstrappedDocumentService) + + // missing jobs service + ctx[documents.BootstrappedDocumentService] = new(testingdocuments.MockService) + err = b.Bootstrap(ctx) + assert.Error(t, err) + assert.Contains(t, err.Error(), jobs.BootstrappedService) + + // missing nft service + ctx[jobs.BootstrappedService] = new(testingjobs.MockJobManager) + err = b.Bootstrap(ctx) + assert.Error(t, err) + assert.Contains(t, err.Error(), bootstrap.BootstrappedInvoiceUnpaid) + + // missing accounts service + ctx[bootstrap.BootstrappedInvoiceUnpaid] = new(testingnfts.MockNFTService) + err = b.Bootstrap(ctx) + assert.Error(t, err) + assert.Contains(t, err.Error(), config.BootstrappedConfigStorage) + + // success + ctx[config.BootstrappedConfigStorage] = new(configstore.MockService) + assert.NoError(t, b.Bootstrap(ctx)) +} diff --git a/httpapi/coreapi/coreapi.go b/httpapi/coreapi/coreapi.go index feb84a363..4090f4000 100644 --- a/httpapi/coreapi/coreapi.go +++ b/httpapi/coreapi/coreapi.go @@ -1,16 +1,18 @@ package coreapi import ( - "github.com/centrifuge/go-centrifuge/config" + "github.com/centrifuge/go-centrifuge/bootstrap" "github.com/centrifuge/go-centrifuge/documents" - "github.com/centrifuge/go-centrifuge/jobs" - "github.com/centrifuge/go-centrifuge/nft" "github.com/go-chi/chi" ) const ( - documentIDParam = "document_id" - versionIDParam = "version_id" + // DocumentIDParam for document_id in api path. + DocumentIDParam = "document_id" + + // VersionIDParam for version_id in api path. + VersionIDParam = "version_id" + jobIDParam = "job_id" tokenIDParam = "token_id" registryAddressParam = "registry_address" @@ -18,24 +20,28 @@ const ( ) // Register registers the core apis to the router. -func Register(r chi.Router, - nftSrv nft.Service, - accountSrv config.Service, - docSrv documents.Service, - jobsSrv jobs.Manager) { +func Register(ctx map[string]interface{}, r chi.Router) { + coreAPISrv := ctx[BootstrappedCoreAPIService].(Service) + tokenRegistry := ctx[bootstrap.BootstrappedInvoiceUnpaid].(documents.TokenRegistry) h := handler{ - srv: Service{docService: docSrv, jobsService: jobsSrv, nftService: nftSrv, accountsService: accountSrv}, - tokenRegistry: nftSrv.(documents.TokenRegistry), + srv: coreAPISrv, + tokenRegistry: tokenRegistry, } + r.Post("/documents", h.CreateDocument) - r.Put("/documents/{"+documentIDParam+"}", h.UpdateDocument) - r.Get("/documents/{"+documentIDParam+"}", h.GetDocument) - r.Get("/documents/{"+documentIDParam+"}/versions/{"+versionIDParam+"}", h.GetDocumentVersion) - r.Post("/documents/{"+documentIDParam+"}/proofs", h.GenerateProofs) - r.Post("/documents/{"+documentIDParam+"}/versions/{"+versionIDParam+"}/proofs", h.GenerateProofsForVersion) + r.Put("/documents/{"+DocumentIDParam+"}", h.UpdateDocument) + r.Get("/documents/{"+DocumentIDParam+"}", h.GetDocument) + r.Get("/documents/{"+DocumentIDParam+"}/versions/{"+VersionIDParam+"}", h.GetDocumentVersion) + r.Post("/documents/{"+DocumentIDParam+"}/proofs", h.GenerateProofs) + r.Post("/documents/{"+DocumentIDParam+"}/versions/{"+VersionIDParam+"}/proofs", h.GenerateProofsForVersion) r.Get("/jobs/{"+jobIDParam+"}", h.GetJobStatus) r.Post("/nfts/registries/{"+registryAddressParam+"}/mint", h.MintNFT) r.Post("/nfts/registries/{"+registryAddressParam+"}/tokens/{"+tokenIDParam+"}/transfer", h.TransferNFT) r.Get("/nfts/registries/{"+registryAddressParam+"}/tokens/{"+tokenIDParam+"}/owner", h.OwnerOfNFT) r.Post("/accounts/{"+accountIDParam+"}/sign", h.SignPayload) + r.Post("/accounts/generate", h.GenerateAccount) + r.Get("/accounts/{"+accountIDParam+"}", h.GetAccount) + r.Get("/accounts", h.GetAccounts) + r.Post("/accounts", h.CreateAccount) + r.Put("/accounts/{"+accountIDParam+"}", h.UpdateAccount) } diff --git a/httpapi/coreapi/coreapi_test.go b/httpapi/coreapi/coreapi_test.go index 17e87be2a..c1a67c7cd 100644 --- a/httpapi/coreapi/coreapi_test.go +++ b/httpapi/coreapi/coreapi_test.go @@ -5,6 +5,7 @@ package coreapi import ( "testing" + "github.com/centrifuge/go-centrifuge/bootstrap" testingnfts "github.com/centrifuge/go-centrifuge/testingutils/nfts" "github.com/go-chi/chi" "github.com/stretchr/testify/assert" @@ -12,28 +13,41 @@ import ( func TestRegister(t *testing.T) { r := chi.NewRouter() - Register(r, new(testingnfts.MockNFTService), nil, nil, nil) - assert.Len(t, r.Routes(), 10) - assert.Equal(t, r.Routes()[0].Pattern, "/accounts/{account_id}/sign") + ctx := map[string]interface{}{ + BootstrappedCoreAPIService: Service{}, + bootstrap.BootstrappedInvoiceUnpaid: new(testingnfts.MockNFTService), + } + Register(ctx, r) + assert.Len(t, r.Routes(), 13) + assert.Equal(t, r.Routes()[0].Pattern, "/accounts") + assert.Len(t, r.Routes()[0].Handlers, 2) + assert.NotNil(t, r.Routes()[0].Handlers["GET"]) assert.NotNil(t, r.Routes()[0].Handlers["POST"]) - assert.Equal(t, r.Routes()[1].Pattern, "/documents") + assert.Equal(t, r.Routes()[1].Pattern, "/accounts/generate") assert.NotNil(t, r.Routes()[1].Handlers["POST"]) - assert.Equal(t, r.Routes()[2].Pattern, "/documents/{document_id}") - assert.Len(t, r.Routes()[2].Handlers, 2) + assert.Equal(t, r.Routes()[2].Pattern, "/accounts/{account_id}") assert.NotNil(t, r.Routes()[2].Handlers["GET"]) assert.NotNil(t, r.Routes()[2].Handlers["PUT"]) - assert.Equal(t, r.Routes()[3].Pattern, "/documents/{document_id}/proofs") + assert.Equal(t, r.Routes()[3].Pattern, "/accounts/{account_id}/sign") assert.NotNil(t, r.Routes()[3].Handlers["POST"]) - assert.Equal(t, r.Routes()[4].Pattern, "/documents/{document_id}/versions/{version_id}") - assert.NotNil(t, r.Routes()[4].Handlers["GET"]) - assert.Equal(t, r.Routes()[5].Pattern, "/documents/{document_id}/versions/{version_id}/proofs") - assert.NotNil(t, r.Routes()[5].Handlers["POST"]) - assert.Equal(t, r.Routes()[6].Pattern, "/jobs/{job_id}") - assert.NotNil(t, r.Routes()[6].Handlers["GET"]) - assert.Equal(t, r.Routes()[7].Pattern, "/nfts/registries/{registry_address}/mint") - assert.NotNil(t, r.Routes()[7].Handlers["POST"]) - assert.Equal(t, r.Routes()[8].Pattern, "/nfts/registries/{registry_address}/tokens/{token_id}/owner") - assert.NotNil(t, r.Routes()[8].Handlers["GET"]) - assert.Equal(t, r.Routes()[9].Pattern, "/nfts/registries/{registry_address}/tokens/{token_id}/transfer") - assert.NotNil(t, r.Routes()[9].Handlers["POST"]) + assert.Equal(t, r.Routes()[4].Pattern, "/documents") + assert.NotNil(t, r.Routes()[4].Handlers["POST"]) + assert.Equal(t, r.Routes()[5].Pattern, "/documents/{document_id}") + assert.Len(t, r.Routes()[5].Handlers, 2) + assert.NotNil(t, r.Routes()[5].Handlers["GET"]) + assert.NotNil(t, r.Routes()[5].Handlers["PUT"]) + assert.Equal(t, r.Routes()[6].Pattern, "/documents/{document_id}/proofs") + assert.NotNil(t, r.Routes()[6].Handlers["POST"]) + assert.Equal(t, r.Routes()[7].Pattern, "/documents/{document_id}/versions/{version_id}") + assert.NotNil(t, r.Routes()[7].Handlers["GET"]) + assert.Equal(t, r.Routes()[8].Pattern, "/documents/{document_id}/versions/{version_id}/proofs") + assert.NotNil(t, r.Routes()[8].Handlers["POST"]) + assert.Equal(t, r.Routes()[9].Pattern, "/jobs/{job_id}") + assert.NotNil(t, r.Routes()[9].Handlers["GET"]) + assert.Equal(t, r.Routes()[10].Pattern, "/nfts/registries/{registry_address}/mint") + assert.NotNil(t, r.Routes()[10].Handlers["POST"]) + assert.Equal(t, r.Routes()[11].Pattern, "/nfts/registries/{registry_address}/tokens/{token_id}/owner") + assert.NotNil(t, r.Routes()[11].Handlers["GET"]) + assert.Equal(t, r.Routes()[12].Pattern, "/nfts/registries/{registry_address}/tokens/{token_id}/transfer") + assert.NotNil(t, r.Routes()[12].Handlers["POST"]) } diff --git a/httpapi/coreapi/documents_api.go b/httpapi/coreapi/documents_api.go index 803870bc7..901d417ca 100644 --- a/httpapi/coreapi/documents_api.go +++ b/httpapi/coreapi/documents_api.go @@ -33,7 +33,7 @@ type handler struct { // @Failure 500 {object} httputils.HTTPError // @Failure 400 {object} httputils.HTTPError // @Failure 403 {object} httputils.HTTPError -// @success 201 {object} coreapi.DocumentResponse +// @success 202 {object} coreapi.DocumentResponse // @router /v1/documents [post] func (h handler) CreateDocument(w http.ResponseWriter, r *http.Request) { var err error @@ -56,7 +56,7 @@ func (h handler) CreateDocument(w http.ResponseWriter, r *http.Request) { return } - payload, err := toDocumentsCreatePayload(request) + payload, err := ToDocumentsCreatePayload(request) if err != nil { code = http.StatusBadRequest log.Error(err) @@ -70,14 +70,14 @@ func (h handler) CreateDocument(w http.ResponseWriter, r *http.Request) { return } - resp, err := getDocumentResponse(model, h.tokenRegistry, jobID) + resp, err := GetDocumentResponse(model, h.tokenRegistry, jobID) if err != nil { code = http.StatusInternalServerError log.Error(err) return } - render.Status(r, http.StatusCreated) + render.Status(r, http.StatusAccepted) render.JSON(w, r, resp) } @@ -94,14 +94,14 @@ func (h handler) CreateDocument(w http.ResponseWriter, r *http.Request) { // @Failure 500 {object} httputils.HTTPError // @Failure 400 {object} httputils.HTTPError // @Failure 403 {object} httputils.HTTPError -// @success 201 {object} coreapi.DocumentResponse +// @success 202 {object} coreapi.DocumentResponse // @router /v1/documents/{document_id} [put] func (h handler) UpdateDocument(w http.ResponseWriter, r *http.Request) { var err error var code int defer httputils.RespondIfError(&code, &err, w, r) - docID, err := hexutil.Decode(chi.URLParam(r, documentIDParam)) + docID, err := hexutil.Decode(chi.URLParam(r, DocumentIDParam)) if err != nil { code = http.StatusBadRequest log.Error(err) @@ -125,7 +125,7 @@ func (h handler) UpdateDocument(w http.ResponseWriter, r *http.Request) { return } - payload, err := toDocumentsCreatePayload(request) + payload, err := ToDocumentsCreatePayload(request) if err != nil { code = http.StatusBadRequest log.Error(err) @@ -139,14 +139,14 @@ func (h handler) UpdateDocument(w http.ResponseWriter, r *http.Request) { return } - resp, err := getDocumentResponse(model, h.tokenRegistry, jobID) + resp, err := GetDocumentResponse(model, h.tokenRegistry, jobID) if err != nil { code = http.StatusInternalServerError log.Error(err) return } - render.Status(r, http.StatusCreated) + render.Status(r, http.StatusAccepted) render.JSON(w, r, resp) } @@ -158,6 +158,7 @@ func (h handler) UpdateDocument(w http.ResponseWriter, r *http.Request) { // @param authorization header string true "Hex encoded centrifuge ID of the account for the intended API action" // @param document_id path string true "Document Identifier" // @produce json +// @Failure 403 {object} httputils.HTTPError // @Failure 400 {object} httputils.HTTPError // @Failure 404 {object} httputils.HTTPError // @Failure 500 {object} httputils.HTTPError @@ -168,7 +169,7 @@ func (h handler) GetDocument(w http.ResponseWriter, r *http.Request) { var code int defer httputils.RespondIfError(&code, &err, w, r) - docID, err := hexutil.Decode(chi.URLParam(r, documentIDParam)) + docID, err := hexutil.Decode(chi.URLParam(r, DocumentIDParam)) if err != nil { code = http.StatusBadRequest log.Error(err) @@ -184,7 +185,7 @@ func (h handler) GetDocument(w http.ResponseWriter, r *http.Request) { return } - resp, err := getDocumentResponse(model, h.tokenRegistry, jobs.NilJobID()) + resp, err := GetDocumentResponse(model, h.tokenRegistry, jobs.NilJobID()) if err != nil { code = http.StatusInternalServerError log.Error(err) @@ -204,6 +205,7 @@ func (h handler) GetDocument(w http.ResponseWriter, r *http.Request) { // @param document_id path string true "Document Identifier" // @param version_id path string true "Document Version Identifier" // @produce json +// @Failure 403 {object} httputils.HTTPError // @Failure 400 {object} httputils.HTTPError // @Failure 404 {object} httputils.HTTPError // @Failure 500 {object} httputils.HTTPError @@ -215,7 +217,7 @@ func (h handler) GetDocumentVersion(w http.ResponseWriter, r *http.Request) { defer httputils.RespondIfError(&code, &err, w, r) ids := make([][]byte, 2, 2) - for i, idStr := range []string{chi.URLParam(r, documentIDParam), chi.URLParam(r, versionIDParam)} { + for i, idStr := range []string{chi.URLParam(r, DocumentIDParam), chi.URLParam(r, VersionIDParam)} { var id []byte id, err = hexutil.Decode(idStr) if err != nil { @@ -236,7 +238,7 @@ func (h handler) GetDocumentVersion(w http.ResponseWriter, r *http.Request) { return } - resp, err := getDocumentResponse(model, h.tokenRegistry, jobs.NilJobID()) + resp, err := GetDocumentResponse(model, h.tokenRegistry, jobs.NilJobID()) if err != nil { code = http.StatusInternalServerError log.Error(err) @@ -256,6 +258,7 @@ func (h handler) GetDocumentVersion(w http.ResponseWriter, r *http.Request) { // @param document_id path string true "Document Identifier" // @param body body coreapi.ProofsRequest true "Document proof request" // @produce json +// @Failure 403 {object} httputils.HTTPError // @Failure 400 {object} httputils.HTTPError // @Failure 500 {object} httputils.HTTPError // @success 200 {object} coreapi.ProofsResponse @@ -265,7 +268,7 @@ func (h handler) GenerateProofs(w http.ResponseWriter, r *http.Request) { var code int defer httputils.RespondIfError(&code, &err, w, r) - docID, err := hexutil.Decode(chi.URLParam(r, documentIDParam)) + docID, err := hexutil.Decode(chi.URLParam(r, DocumentIDParam)) if err != nil { code = http.StatusBadRequest log.Error(err) @@ -309,6 +312,7 @@ func (h handler) GenerateProofs(w http.ResponseWriter, r *http.Request) { // @param version_id path string true "Document Version Identifier" // @param body body coreapi.ProofsRequest true "Document proof request" // @produce json +// @Failure 403 {object} httputils.HTTPError // @Failure 400 {object} httputils.HTTPError // @Failure 500 {object} httputils.HTTPError // @success 200 {object} coreapi.ProofsResponse @@ -319,7 +323,7 @@ func (h handler) GenerateProofsForVersion(w http.ResponseWriter, r *http.Request defer httputils.RespondIfError(&code, &err, w, r) ids := make([][]byte, 2, 2) - for i, idStr := range []string{chi.URLParam(r, documentIDParam), chi.URLParam(r, versionIDParam)} { + for i, idStr := range []string{chi.URLParam(r, DocumentIDParam), chi.URLParam(r, VersionIDParam)} { var id []byte id, err = hexutil.Decode(idStr) if err != nil { diff --git a/httpapi/coreapi/documents_api_test.go b/httpapi/coreapi/documents_api_test.go index 3d65e4146..c8b8bc062 100644 --- a/httpapi/coreapi/documents_api_test.go +++ b/httpapi/coreapi/documents_api_test.go @@ -59,7 +59,7 @@ func TestHandler_CreateDocument(t *testing.T) { d, err = json.Marshal(data) assert.NoError(t, err) docSrv := new(testingdocuments.MockService) - srv := Service{docService: docSrv} + srv := Service{docSrv: docSrv} h = handler{srv: srv} docSrv.On("CreateModel", mock.Anything, mock.Anything).Return(nil, jobs.NilJobID(), errors.New("failed to create model")) r = httptest.NewRequest("POST", "/documents", bytes.NewReader(d)) @@ -75,7 +75,7 @@ func TestHandler_CreateDocument(t *testing.T) { m.On("GetAttributes").Return(nil) m.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, errors.New("failed to get collaborators")) docSrv = new(testingdocuments.MockService) - srv = Service{docService: docSrv} + srv = Service{docSrv: docSrv} h = handler{srv: srv} docSrv.On("CreateModel", mock.Anything, mock.Anything).Return(m, jobs.NewJobID(), nil) r = httptest.NewRequest("POST", "/documents", bytes.NewReader(d)) @@ -97,13 +97,13 @@ func TestHandler_CreateDocument(t *testing.T) { m.On("NFTs").Return(nil) m.On("GetAttributes").Return(nil) docSrv = new(testingdocuments.MockService) - srv = Service{docService: docSrv} + srv = Service{docSrv: docSrv} h = handler{srv: srv} docSrv.On("CreateModel", mock.Anything, mock.Anything).Return(m, jobs.NewJobID(), nil) r = httptest.NewRequest("POST", "/documents", bytes.NewReader(d)) w = httptest.NewRecorder() h.CreateDocument(w, r) - assert.Equal(t, w.Code, http.StatusCreated) + assert.Equal(t, w.Code, http.StatusAccepted) m.AssertExpectations(t) docSrv.AssertExpectations(t) } @@ -165,7 +165,7 @@ func TestHandler_UpdateDocument(t *testing.T) { d, err = json.Marshal(data) assert.NoError(t, err) docSrv := new(testingdocuments.MockService) - srv := Service{docService: docSrv} + srv := Service{docSrv: docSrv} h = handler{srv: srv} docSrv.On("UpdateModel", mock.Anything, mock.Anything).Return(nil, jobs.NilJobID(), errors.New("failed to update model")) w, r = getHTTPReqAndResp(ctx, bytes.NewReader(d)) @@ -180,7 +180,7 @@ func TestHandler_UpdateDocument(t *testing.T) { m.On("GetAttributes").Return(nil) m.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, errors.New("failed to get collaborators")) docSrv = new(testingdocuments.MockService) - srv = Service{docService: docSrv} + srv = Service{docSrv: docSrv} h = handler{srv: srv} docSrv.On("UpdateModel", mock.Anything, mock.Anything).Return(m, jobs.NewJobID(), nil) w, r = getHTTPReqAndResp(ctx, bytes.NewReader(d)) @@ -201,12 +201,12 @@ func TestHandler_UpdateDocument(t *testing.T) { m.On("NFTs").Return(nil) m.On("GetAttributes").Return(nil) docSrv = new(testingdocuments.MockService) - srv = Service{docService: docSrv} + srv = Service{docSrv: docSrv} h = handler{srv: srv} docSrv.On("UpdateModel", mock.Anything, mock.Anything).Return(m, jobs.NewJobID(), nil) w, r = getHTTPReqAndResp(ctx, bytes.NewReader(d)) h.UpdateDocument(w, r) - assert.Equal(t, w.Code, http.StatusCreated) + assert.Equal(t, w.Code, http.StatusAccepted) m.AssertExpectations(t) docSrv.AssertExpectations(t) } @@ -237,7 +237,7 @@ func TestHandler_GetDocument(t *testing.T) { rctx.URLParams.Values[0] = hexutil.Encode(id) docSrv := new(testingdocuments.MockService) docSrv.On("GetCurrentVersion", id).Return(nil, errors.New("failed")) - h = handler{srv: Service{docService: docSrv}} + h = handler{srv: Service{docSrv: docSrv}} w, r := getHTTPReqAndResp(ctx) h.GetDocument(w, r) assert.Equal(t, w.Code, http.StatusNotFound) @@ -263,7 +263,7 @@ func TestHandler_GetDocument(t *testing.T) { m.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, errors.New("failed to get collaborators")) docSrv = new(testingdocuments.MockService) docSrv.On("GetCurrentVersion", id).Return(m, nil) - h = handler{srv: Service{docService: docSrv}} + h = handler{srv: Service{docSrv: docSrv}} w, r = getHTTPReqAndResp(ctx) h.GetDocument(w, r) assert.Equal(t, w.Code, http.StatusInternalServerError) @@ -284,7 +284,7 @@ func TestHandler_GetDocument(t *testing.T) { m.On("GetAttributes").Return(nil) docSrv = new(testingdocuments.MockService) docSrv.On("GetCurrentVersion", id).Return(m, nil) - h = handler{srv: Service{docService: docSrv}} + h = handler{srv: Service{docSrv: docSrv}} w, r = getHTTPReqAndResp(ctx) h.GetDocument(w, r) assert.Equal(t, w.Code, http.StatusOK) @@ -322,7 +322,7 @@ func TestHandler_GetDocumentVersion(t *testing.T) { rctx.URLParams.Values[1] = hexutil.Encode(vid) docSrv := new(testingdocuments.MockService) docSrv.On("GetVersion", id, vid).Return(nil, errors.New("failed")) - h = handler{srv: Service{docService: docSrv}} + h = handler{srv: Service{docSrv: docSrv}} w, r := getHTTPReqAndResp(ctx) h.GetDocumentVersion(w, r) assert.Equal(t, w.Code, http.StatusNotFound) @@ -348,7 +348,7 @@ func TestHandler_GetDocumentVersion(t *testing.T) { m.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, errors.New("failed to get collaborators")) docSrv = new(testingdocuments.MockService) docSrv.On("GetVersion", id, vid).Return(m, nil) - h = handler{srv: Service{docService: docSrv}} + h = handler{srv: Service{docSrv: docSrv}} w, r = getHTTPReqAndResp(ctx) h.GetDocumentVersion(w, r) assert.Equal(t, w.Code, http.StatusInternalServerError) @@ -369,7 +369,7 @@ func TestHandler_GetDocumentVersion(t *testing.T) { m.On("GetAttributes").Return(nil) docSrv = new(testingdocuments.MockService) docSrv.On("GetVersion", id, vid).Return(m, nil) - h = handler{srv: Service{docService: docSrv}} + h = handler{srv: Service{docSrv: docSrv}} w, r = getHTTPReqAndResp(ctx) h.GetDocumentVersion(w, r) assert.Equal(t, w.Code, http.StatusOK) @@ -414,7 +414,7 @@ func TestHandler_GenerateProofs(t *testing.T) { buf := bytes.NewReader(d) docSrv := new(testingdocuments.MockService) docSrv.On("CreateProofs", mock.Anything, id, request.Fields).Return(nil, errors.New("failed to generate proofs")) - h = handler{srv: Service{docService: docSrv}} + h = handler{srv: Service{docSrv: docSrv}} w, r = getHTTPReqAndResp(ctx, buf) h.GenerateProofs(w, r) assert.Equal(t, w.Code, http.StatusInternalServerError) @@ -445,7 +445,7 @@ func TestHandler_GenerateProofs(t *testing.T) { } docSrv = new(testingdocuments.MockService) docSrv.On("CreateProofs", mock.Anything, id, request.Fields).Return(proof, nil) - h = handler{srv: Service{docService: docSrv}} + h = handler{srv: Service{docSrv: docSrv}} w, r = getHTTPReqAndResp(ctx, buf) h.GenerateProofs(w, r) assert.Equal(t, w.Code, http.StatusOK) @@ -494,7 +494,7 @@ func TestHandler_GenerateProofsForVersion(t *testing.T) { buf := bytes.NewReader(d) docSrv := new(testingdocuments.MockService) docSrv.On("CreateProofsForVersion", mock.Anything, id, vid, request.Fields).Return(nil, errors.New("failed to generate proofs")) - h = handler{srv: Service{docService: docSrv}} + h = handler{srv: Service{docSrv: docSrv}} w, r = getHTTPReqAndResp(ctx, buf) h.GenerateProofsForVersion(w, r) assert.Equal(t, w.Code, http.StatusInternalServerError) @@ -525,7 +525,7 @@ func TestHandler_GenerateProofsForVersion(t *testing.T) { }, } docSrv.On("CreateProofsForVersion", mock.Anything, id, vid, request.Fields).Return(proof, nil) - h = handler{srv: Service{docService: docSrv}} + h = handler{srv: Service{docSrv: docSrv}} w, r = getHTTPReqAndResp(ctx, buf) h.GenerateProofsForVersion(w, r) assert.Equal(t, w.Code, http.StatusOK) diff --git a/httpapi/coreapi/jobs_api.go b/httpapi/coreapi/jobs_api.go index bab1d2bb6..1854b0585 100644 --- a/httpapi/coreapi/jobs_api.go +++ b/httpapi/coreapi/jobs_api.go @@ -27,6 +27,7 @@ const ( // @param authorization header string true "Hex encoded centrifuge ID of the account for the intended API action" // @param job_id path string true "Job ID" // @produce json +// @Failure 403 {object} httputils.HTTPError // @Failure 400 {object} httputils.HTTPError // @Failure 404 {object} httputils.HTTPError // @success 200 {object} jobs.StatusResponse diff --git a/httpapi/coreapi/jobs_api_test.go b/httpapi/coreapi/jobs_api_test.go index 50f75a0e0..dd9781ce9 100644 --- a/httpapi/coreapi/jobs_api_test.go +++ b/httpapi/coreapi/jobs_api_test.go @@ -58,7 +58,7 @@ func TestService_GetJobStatus(t *testing.T) { w, r = getHTTPReqAndResp(ctx) jobMan := testingjobs.MockJobManager{} jobMan.On("GetJobStatus", did, jobID).Return(nil, errors.New("missing job")) - h = handler{srv: Service{jobsService: jobMan}} + h = handler{srv: Service{jobsSrv: jobMan}} h.GetJobStatus(w, r) assert.Equal(t, w.Code, http.StatusNotFound) assert.Contains(t, w.Body.String(), ErrJobNotFound.Error()) @@ -69,7 +69,7 @@ func TestService_GetJobStatus(t *testing.T) { jobMan = testingjobs.MockJobManager{} tt := time.Now().UTC() jobMan.On("GetJobStatus", did, jobID).Return(jobs.StatusResponse{JobID: jobID.String(), LastUpdated: tt}, nil) - h = handler{srv: Service{jobsService: jobMan}} + h = handler{srv: Service{jobsSrv: jobMan}} h.GetJobStatus(w, r) assert.Equal(t, w.Code, http.StatusOK) assert.Contains(t, w.Body.String(), jobID.String()) diff --git a/httpapi/coreapi/nfts_api.go b/httpapi/coreapi/nfts_api.go index 600e55a71..d71d9fd22 100644 --- a/httpapi/coreapi/nfts_api.go +++ b/httpapi/coreapi/nfts_api.go @@ -25,15 +25,16 @@ const ( // @summary Mints an NFT against a document. // @description Mints an NFT against a document. // @id mint_nft -// @tags NFT +// @tags NFTs // @accept json // @param authorization header string true "Hex encoded centrifuge ID of the account for the intended API action" // @param registry_address path string true "NFT registry address in hex" // @param body body coreapi.MintNFTRequest true "Mint NFT request" // @produce json +// @Failure 403 {object} httputils.HTTPError // @Failure 500 {object} httputils.HTTPError // @Failure 400 {object} httputils.HTTPError -// @success 201 {object} coreapi.MintNFTResponse +// @success 202 {object} coreapi.MintNFTResponse // @router /v1/nfts/registries/{registry_address}/mint [post] func (h handler) MintNFT(w http.ResponseWriter, r *http.Request) { var err error @@ -78,7 +79,7 @@ func (h handler) MintNFT(w http.ResponseWriter, r *http.Request) { DocumentID: req.DocumentID, TokenID: resp.TokenID, } - render.Status(r, http.StatusCreated) + render.Status(r, http.StatusAccepted) render.JSON(w, r, nftResp) } @@ -86,13 +87,14 @@ func (h handler) MintNFT(w http.ResponseWriter, r *http.Request) { // @summary Transfers given NFT to provide address. // @description Transfers given NFT to provide address. // @id transfer_nft -// @tags NFT +// @tags NFTs // @accept json // @param authorization header string true "Hex encoded centrifuge ID of the account for the intended API action" // @param registry_address path string true "NFT registry address in hex" // @param token_id path string true "NFT token ID in hex" // @param body body coreapi.TransferNFTRequest true "Mint NFT request" // @produce json +// @Failure 403 {object} httputils.HTTPError // @Failure 500 {object} httputils.HTTPError // @Failure 400 {object} httputils.HTTPError // @success 200 {object} coreapi.TransferNFTResponse @@ -154,11 +156,12 @@ func (h handler) TransferNFT(w http.ResponseWriter, r *http.Request) { // @summary Returns the Owner of the given NFT. // @description Returns the Owner of the given NFT. // @id owner_of_nft -// @tags NFT +// @tags NFTs // @param authorization header string true "Hex encoded centrifuge ID of the account for the intended API action" // @param token_id path string true "NFT token ID in hex" // @param registry_address path string true "Registry address in hex" // @produce json +// @Failure 403 {object} httputils.HTTPError // @Failure 400 {object} httputils.HTTPError // @success 200 {object} coreapi.NFTOwnerResponse // @router /v1/nfts/registries/{registry_address}/tokens/{token_id}/owner [get] diff --git a/httpapi/coreapi/nfts_api_test.go b/httpapi/coreapi/nfts_api_test.go index 04088dd51..a6838fb1d 100644 --- a/httpapi/coreapi/nfts_api_test.go +++ b/httpapi/coreapi/nfts_api_test.go @@ -96,7 +96,7 @@ func TestHandler_MintNFT(t *testing.T) { w, r = getHTTPReqAndResp(ctx, bytes.NewReader(d)) srv := new(testingnfts.MockNFTService) srv.On("MintNFT", ctx, mock.Anything).Return(nil, nil, errors.New("failed to mint nft")).Once() - h.srv.nftService = srv + h.srv.nftSrv = srv h.MintNFT(w, r) assert.Equal(t, http.StatusBadRequest, w.Code) assert.Contains(t, w.Body.String(), "failed to mint nft") @@ -111,9 +111,9 @@ func TestHandler_MintNFT(t *testing.T) { TokenID: tokenID, JobID: jobs.NewJobID().String(), }, nil, nil).Once() - h.srv.nftService = srv + h.srv.nftSrv = srv h.MintNFT(w, r) - assert.Equal(t, http.StatusCreated, w.Code) + assert.Equal(t, http.StatusAccepted, w.Code) assert.Contains(t, w.Body.String(), tokenID) srv.AssertExpectations(t) } @@ -145,7 +145,7 @@ func TestHandler_TransferNFT(t *testing.T) { assert.Equal(t, w.Code, http.StatusBadRequest) assert.Contains(t, w.Body.String(), "unexpected end of JSON input") - // service fail + // Service fail to := hexutil.Encode(utils.RandomSlice(20)) body := map[string]interface{}{ "to": to, @@ -154,7 +154,7 @@ func TestHandler_TransferNFT(t *testing.T) { assert.NoError(t, err) srv := new(testingnfts.MockNFTService) srv.On("TransferFrom", ctx, mock.Anything, mock.Anything, mock.Anything).Return(nil, nil, errors.New("failed to transfer")).Once() - h.srv.nftService = srv + h.srv.nftSrv = srv b = bytes.NewReader(d) w, r = getHTTPReqAndResp(ctx) h.TransferNFT(w, r) @@ -168,7 +168,7 @@ func TestHandler_TransferNFT(t *testing.T) { TokenID: tokenID.String(), JobID: jobs.NewJobID().String(), }, nil, nil).Once() - h.srv.nftService = srv + h.srv.nftSrv = srv b = bytes.NewReader(d) w, r = getHTTPReqAndResp(ctx) h.TransferNFT(w, r) @@ -197,7 +197,7 @@ func TestHandler_OwnerOfNFT(t *testing.T) { // owner failed srv := new(testingnfts.MockNFTService) srv.On("OwnerOf", mock.Anything, mock.Anything).Return(nil, errors.New("failed to get owner")).Once() - h.srv.nftService = srv + h.srv.nftSrv = srv w, r := getHTTPReqAndResp(ctx) h.OwnerOfNFT(w, r) assert.Equal(t, w.Code, http.StatusBadRequest) @@ -208,7 +208,7 @@ func TestHandler_OwnerOfNFT(t *testing.T) { owner := common.BytesToAddress(utils.RandomSlice(20)) srv = new(testingnfts.MockNFTService) srv.On("OwnerOf", mock.Anything, mock.Anything).Return(owner, nil).Once() - h.srv.nftService = srv + h.srv.nftSrv = srv w, r = getHTTPReqAndResp(ctx) h.OwnerOfNFT(w, r) assert.Equal(t, w.Code, http.StatusOK) diff --git a/httpapi/coreapi/service.go b/httpapi/coreapi/service.go index 58275a87b..d0492036a 100644 --- a/httpapi/coreapi/service.go +++ b/httpapi/coreapi/service.go @@ -12,67 +12,102 @@ import ( "github.com/ethereum/go-ethereum/common" ) -// Service provides functionality for Core APIs. +// NewService returns the new CoreAPI Service. +func NewService(docSrv documents.Service, jobsSrv jobs.Manager, nftSrv nft.Service, accountsSrv config.Service) Service { + return Service{ + docSrv: docSrv, + jobsSrv: jobsSrv, + nftSrv: nftSrv, + accountsSrv: accountsSrv, + } +} + +// Service defines the functionality for the CoreAPI service. type Service struct { - docService documents.Service - jobsService jobs.Manager - nftService nft.Service - accountsService config.Service + docSrv documents.Service + jobsSrv jobs.Manager + nftSrv nft.Service + accountsSrv config.Service } // CreateDocument creates the document from the payload and anchors it. func (s Service) CreateDocument(ctx context.Context, payload documents.CreatePayload) (documents.Model, jobs.JobID, error) { - return s.docService.CreateModel(ctx, payload) + return s.docSrv.CreateModel(ctx, payload) } // UpdateDocument updates the document from the payload and anchors the next version. func (s Service) UpdateDocument(ctx context.Context, payload documents.UpdatePayload) (documents.Model, jobs.JobID, error) { - return s.docService.UpdateModel(ctx, payload) + return s.docSrv.UpdateModel(ctx, payload) } // GetJobStatus returns the job status. func (s Service) GetJobStatus(account identity.DID, id jobs.JobID) (jobs.StatusResponse, error) { - return s.jobsService.GetJobStatus(account, id) + return s.jobsSrv.GetJobStatus(account, id) } // GetDocument returns the latest version of the document. func (s Service) GetDocument(ctx context.Context, docID []byte) (documents.Model, error) { - return s.docService.GetCurrentVersion(ctx, docID) + return s.docSrv.GetCurrentVersion(ctx, docID) } // GetDocumentVersion returns the specific version of the document func (s Service) GetDocumentVersion(ctx context.Context, docID, versionID []byte) (documents.Model, error) { - return s.docService.GetVersion(ctx, docID, versionID) + return s.docSrv.GetVersion(ctx, docID, versionID) } // GenerateProofs returns the proofs for the latest version of the document. func (s Service) GenerateProofs(ctx context.Context, docID []byte, fields []string) (*documents.DocumentProof, error) { - return s.docService.CreateProofs(ctx, docID, fields) + return s.docSrv.CreateProofs(ctx, docID, fields) } // GenerateProofsForVersion returns the proofs for the specific version of the document. func (s Service) GenerateProofsForVersion(ctx context.Context, docID, versionID []byte, fields []string) (*documents.DocumentProof, error) { - return s.docService.CreateProofsForVersion(ctx, docID, versionID, fields) + return s.docSrv.CreateProofsForVersion(ctx, docID, versionID, fields) } // MintNFT mints an NFT. func (s Service) MintNFT(ctx context.Context, request nft.MintNFTRequest) (*nft.TokenResponse, error) { - resp, _, err := s.nftService.MintNFT(ctx, request) + resp, _, err := s.nftSrv.MintNFT(ctx, request) return resp, err } // TransferNFT transfers NFT with tokenID in a given registry to `to` address. func (s Service) TransferNFT(ctx context.Context, to, registry common.Address, tokenID nft.TokenID) (*nft.TokenResponse, error) { - resp, _, err := s.nftService.TransferFrom(ctx, registry, to, tokenID) + resp, _, err := s.nftSrv.TransferFrom(ctx, registry, to, tokenID) return resp, err } // OwnerOfNFT returns the owner of the NFT. func (s Service) OwnerOfNFT(registry common.Address, tokenID nft.TokenID) (common.Address, error) { - return s.nftService.OwnerOf(registry, tokenID[:]) + return s.nftSrv.OwnerOf(registry, tokenID[:]) } // SignPayload uses the accountID's secret key to sign the payload and returns the signature func (s Service) SignPayload(accountID, payload []byte) (*coredocumentpb.Signature, error) { - return s.accountsService.Sign(accountID, payload) + return s.accountsSrv.Sign(accountID, payload) +} + +// GetAccount returns the Account associated with accountID +func (s Service) GetAccount(accountID []byte) (config.Account, error) { + return s.accountsSrv.GetAccount(accountID) +} + +// GenerateAccount generates an account with defaults. +func (s Service) GenerateAccount() (config.Account, error) { + return s.accountsSrv.GenerateAccount() +} + +// GetAccounts returns all the accounts. +func (s Service) GetAccounts() ([]config.Account, error) { + return s.accountsSrv.GetAccounts() +} + +// CreateAccount creates a new account from the data provided. +func (s Service) CreateAccount(acc config.Account) (config.Account, error) { + return s.accountsSrv.CreateAccount(acc) +} + +// UpdateAccount updates the existing account with the data provided. +func (s Service) UpdateAccount(acc config.Account) (config.Account, error) { + return s.accountsSrv.UpdateAccount(acc) } diff --git a/httpapi/coreapi/service_test.go b/httpapi/coreapi/service_test.go index 86a10e5a0..3dd734a47 100644 --- a/httpapi/coreapi/service_test.go +++ b/httpapi/coreapi/service_test.go @@ -15,7 +15,7 @@ import ( func TestService_CreateDocument(t *testing.T) { docSrv := new(testingdocuments.MockService) - srv := Service{docService: docSrv} + srv := Service{docSrv: docSrv} m := new(testingdocuments.MockModel) docSrv.On("CreateModel", mock.Anything, mock.Anything).Return(m, jobs.NewJobID(), nil) nm, _, err := srv.CreateDocument(context.Background(), documents.CreatePayload{}) @@ -25,7 +25,7 @@ func TestService_CreateDocument(t *testing.T) { func TestService_UpdateDocument(t *testing.T) { docSrv := new(testingdocuments.MockService) - srv := Service{docService: docSrv} + srv := Service{docSrv: docSrv} m := new(testingdocuments.MockModel) docSrv.On("UpdateModel", mock.Anything, mock.Anything).Return(m, jobs.NewJobID(), nil) nm, _, err := srv.UpdateDocument(context.Background(), documents.UpdatePayload{}) diff --git a/extensions/funding/test_bootstrapper.go b/httpapi/coreapi/test_boostrapper.go similarity index 92% rename from extensions/funding/test_bootstrapper.go rename to httpapi/coreapi/test_boostrapper.go index bd33847d0..209f4d596 100644 --- a/extensions/funding/test_bootstrapper.go +++ b/httpapi/coreapi/test_boostrapper.go @@ -1,6 +1,6 @@ // +build unit integration -package funding +package coreapi func (b Bootstrapper) TestBootstrap(context map[string]interface{}) error { return b.Bootstrap(context) diff --git a/httpapi/coreapi/types.go b/httpapi/coreapi/types.go index ef1b0aca5..b6e680c37 100644 --- a/httpapi/coreapi/types.go +++ b/httpapi/coreapi/types.go @@ -2,9 +2,13 @@ package coreapi import ( "encoding/json" + "math/big" + "strings" "time" "github.com/centrifuge/centrifuge-protobufs/gen/go/coredocument" + "github.com/centrifuge/go-centrifuge/config" + "github.com/centrifuge/go-centrifuge/config/configstore" "github.com/centrifuge/go-centrifuge/documents" "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/go-centrifuge/identity" @@ -15,24 +19,47 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" ) -// AttributeMap defines a map of attributes with attribute key as key -type AttributeMap map[string]Attribute +// MonetaryValue defines user format to represent currency type +// Value string representation of decimal number +// ChainID hex bytes representing the chain where the currency is relevant +// ID string representing the Currency (USD|ETH|0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2(DAI)...) +type MonetaryValue struct { + Value *documents.Decimal `json:"value" swaggertype:"primitive,string"` + ChainID byteutils.HexBytes `json:"chain_id" swaggertype:"primitive,string"` + ID string `json:"id"` +} + +// AttributeMapRequest defines a map of attributes with attribute key as key +type AttributeMapRequest map[string]AttributeRequest // CreateDocumentRequest defines the payload for creating documents. type CreateDocumentRequest struct { - Scheme string `json:"scheme" enums:"generic,invoice,purchase_order,entity"` - ReadAccess []common.Address `json:"read_access" swaggertype:"array,string"` - WriteAccess []common.Address `json:"write_access" swaggertype:"array,string"` - Data interface{} `json:"data"` - Attributes AttributeMap `json:"attributes"` + Scheme string `json:"scheme" enums:"generic,invoice,entity"` + ReadAccess []identity.DID `json:"read_access" swaggertype:"array,string"` + WriteAccess []identity.DID `json:"write_access" swaggertype:"array,string"` + Data interface{} `json:"data"` + Attributes AttributeMapRequest `json:"attributes"` +} + +// AttributeRequest defines a single attribute. +// Type type of the attribute +// Value simple value of the attribute +// MonetaryValue value for only monetary attribute +type AttributeRequest struct { + Type string `json:"type" enums:"integer,decimal,string,bytes,timestamp,monetary"` + Value string `json:"value"` + MonetaryValue *MonetaryValue `json:"monetary_value,omitempty"` } -// Attribute defines a single attribute. -type Attribute struct { - Type string `json:"type" enums:"integer,decimal,string,bytes,timestamp"` - Value string `json:"value"` +// AttributeResponse adds key to the attribute. +type AttributeResponse struct { + AttributeRequest + Key byteutils.HexBytes `json:"key" swaggertype:"primitive,string"` } +// AttributeMapResponse maps attribute label to AttributeResponse +type AttributeMapResponse map[string]AttributeResponse + // NFT defines a single NFT. type NFT struct { Registry string `json:"registry"` @@ -43,30 +70,44 @@ type NFT struct { // ResponseHeader holds the common response header fields type ResponseHeader struct { - DocumentID string `json:"document_id"` - VersionID string `json:"version_id"` - Author string `json:"author"` - CreatedAt string `json:"created_at"` - ReadAccess []common.Address `json:"read_access" swaggertype:"array,string"` - WriteAccess []common.Address `json:"write_access" swaggertype:"array,string"` - JobID string `json:"job_id,omitempty"` - NFTs []NFT `json:"nfts"` + DocumentID string `json:"document_id"` + VersionID string `json:"version_id"` + Author string `json:"author"` + CreatedAt string `json:"created_at"` + ReadAccess []identity.DID `json:"read_access" swaggertype:"array,string"` + WriteAccess []identity.DID `json:"write_access" swaggertype:"array,string"` + JobID string `json:"job_id,omitempty"` + NFTs []NFT `json:"nfts"` + Status string `json:"status,omitempty"` } // DocumentResponse is the common response for Document APIs. type DocumentResponse struct { - Header ResponseHeader `json:"header"` - Scheme string `json:"scheme" enums:"generic,invoice,purchase_order,entity"` - Data interface{} `json:"data"` - Attributes AttributeMap `json:"attributes"` + Header ResponseHeader `json:"header"` + Scheme string `json:"scheme" enums:"generic,invoice,entity"` + Data interface{} `json:"data"` + Attributes AttributeMapResponse `json:"attributes"` } -func toDocumentAttributes(cattrs map[string]Attribute) (map[documents.AttrKey]documents.Attribute, error) { +func toDocumentAttributes(cattrs map[string]AttributeRequest) (map[documents.AttrKey]documents.Attribute, error) { attrs := make(map[documents.AttrKey]documents.Attribute) for k, v := range cattrs { - attr, err := documents.NewAttribute(k, documents.AttributeType(v.Type), v.Value) - if err != nil { - return nil, err + var attr documents.Attribute + var err error + switch documents.AttributeType(v.Type) { + case documents.AttrMonetary: + if v.MonetaryValue == nil { + return nil, errors.NewTypedError(documents.ErrWrongAttrFormat, errors.New("empty value field")) + } + attr, err = documents.NewMonetaryAttribute(k, v.MonetaryValue.Value, v.MonetaryValue.ChainID.Bytes(), v.MonetaryValue.ID) + if err != nil { + return nil, err + } + default: + attr, err = documents.NewStringAttribute(k, documents.AttributeType(v.Type), v.Value) + if err != nil { + return nil, err + } } attrs[attr.Key] = attr @@ -75,12 +116,13 @@ func toDocumentAttributes(cattrs map[string]Attribute) (map[documents.AttrKey]do return attrs, nil } -func toDocumentsCreatePayload(request CreateDocumentRequest) (documents.CreatePayload, error) { +// ToDocumentsCreatePayload converts CoreAPI create payload to documents payload. +func ToDocumentsCreatePayload(request CreateDocumentRequest) (documents.CreatePayload, error) { payload := documents.CreatePayload{ Scheme: request.Scheme, Collaborators: documents.CollaboratorsAccess{ - ReadCollaborators: identity.AddressToDIDs(request.ReadAccess...), - ReadWriteCollaborators: identity.AddressToDIDs(request.WriteAccess...), + ReadCollaborators: request.ReadAccess, + ReadWriteCollaborators: request.WriteAccess, }, } @@ -104,8 +146,9 @@ func convertNFTs(tokenRegistry documents.TokenRegistry, nfts []*coredocumentpb.N regAddress := common.BytesToAddress(n.RegistryId[:common.AddressLength]) i, errn := tokenRegistry.CurrentIndexOfToken(regAddress, n.TokenId) if errn != nil || i == nil { - err = errors.AppendError(err, errors.New("token index received is nil or other error: %v", errn)) - continue + // Optional value to be part of the document response + log.Debug(errors.New("token index received is nil or other error: %v", errn)) + i = new(big.Int) } o, errn := tokenRegistry.OwnerOf(regAddress, n.TokenId) @@ -124,17 +167,38 @@ func convertNFTs(tokenRegistry documents.TokenRegistry, nfts []*coredocumentpb.N return nnfts, err } -func convertAttributes(attrs []documents.Attribute) (AttributeMap, error) { - m := make(AttributeMap) +func toAttributeMapResponse(attrs []documents.Attribute) (AttributeMapResponse, error) { + m := make(AttributeMapResponse) for _, v := range attrs { - val, err := v.Value.String() - if err != nil { - return nil, err + var attrReq AttributeRequest + switch v.Value.Type { + case documents.AttrMonetary: + id := string(v.Value.Monetary.ID) + if v.Value.Monetary.Type == documents.MonetaryToken { + id = hexutil.Encode(v.Value.Monetary.ID) + } + attrReq = AttributeRequest{ + Type: v.Value.Type.String(), + MonetaryValue: &MonetaryValue{ + Value: v.Value.Monetary.Value, + ChainID: v.Value.Monetary.ChainID, + ID: id, + }, + } + default: + val, err := v.Value.String() + if err != nil { + return nil, err + } + attrReq = AttributeRequest{ + Type: v.Value.Type.String(), + Value: val, + } } - m[v.KeyLabel] = Attribute{ - Type: v.Value.Type.String(), - Value: val, + m[v.KeyLabel] = AttributeResponse{ + AttributeRequest: attrReq, + Key: v.Key[:], } } @@ -170,17 +234,18 @@ func DeriveResponseHeader(tokenRegistry documents.TokenRegistry, model documents VersionID: hexutil.Encode(model.CurrentVersion()), Author: author.String(), CreatedAt: ts, - ReadAccess: identity.DIDsToAddress(cs.ReadCollaborators...), - WriteAccess: identity.DIDsToAddress(cs.ReadWriteCollaborators...), + ReadAccess: cs.ReadCollaborators, + WriteAccess: cs.ReadWriteCollaborators, NFTs: cnfts, JobID: id.String(), }, nil } -func getDocumentResponse(model documents.Model, tokenRegistry documents.TokenRegistry, jobID jobs.JobID) (resp DocumentResponse, err error) { +// GetDocumentResponse converts model to a client api format. +func GetDocumentResponse(model documents.Model, tokenRegistry documents.TokenRegistry, jobID jobs.JobID) (resp DocumentResponse, err error) { docData := model.GetData() scheme := model.Scheme() - attrMap, err := convertAttributes(model.GetAttributes()) + attrMap, err := toAttributeMapResponse(model.GetAttributes()) if err != nil { return resp, err } @@ -205,51 +270,21 @@ type ProofResponseHeader struct { State string `json:"state"` } -// Proof represents a single proof -type Proof struct { - Property byteutils.HexBytes `json:"property" swaggertype:"primitive,string"` - Value byteutils.HexBytes `json:"value" swaggertype:"primitive,string"` - Salt byteutils.HexBytes `json:"salt" swaggertype:"primitive,string"` - Hash byteutils.HexBytes `json:"hash" swaggertype:"primitive,string"` - SortedHashes []byteutils.HexBytes `json:"sorted_hashes" swaggertype:"array,string"` -} - // ProofsResponse holds the proofs for the fields given for a document. type ProofsResponse struct { Header ProofResponseHeader `json:"header"` - FieldProofs []Proof `json:"field_proofs"` + FieldProofs []documents.Proof `json:"field_proofs"` } func convertProofs(proof *documents.DocumentProof) ProofsResponse { - resp := ProofsResponse{ + return ProofsResponse{ Header: ProofResponseHeader{ DocumentID: proof.DocumentID, VersionID: proof.VersionID, State: proof.State, }, + FieldProofs: documents.ConvertProofs(proof.FieldProofs), } - - var proofs []Proof - for _, pf := range proof.FieldProofs { - pff := Proof{ - Value: pf.Value, - Hash: pf.Hash, - Salt: pf.Salt, - Property: pf.GetCompactName(), - } - - var hashes []byteutils.HexBytes - for _, h := range pf.SortedHashes { - h := h - hashes = append(hashes, h) - } - - pff.SortedHashes = hashes - proofs = append(proofs, pff) - } - - resp.FieldProofs = proofs - return resp } // MintNFTRequest holds required fields for minting NFT @@ -320,3 +355,90 @@ type SignResponse struct { PublicKey byteutils.HexBytes `json:"public_key" swaggertype:"primitive,string"` SignerID byteutils.HexBytes `json:"signer_id" swaggertype:"primitive,string"` } + +// KeyPair represents the public and private key. +type KeyPair struct { + Pub string `json:"pub"` + Pvt string `json:"pvt"` +} + +// EthAccount holds address of the account. +type EthAccount struct { + Address string `json:"address"` + Key string `json:"key,omitempty"` + Password string `json:"password,omitempty"` +} + +// Account holds the single account details. +type Account struct { + EthereumAccount EthAccount `json:"eth_account"` + EthereumDefaultAccountName string `json:"eth_default_account_name"` + ReceiveEventNotificationEndpoint string `json:"receive_event_notification_endpoint"` + IdentityID byteutils.HexBytes `json:"identity_id" swaggertype:"primitive,string"` + SigningKeyPair KeyPair `json:"signing_key_pair"` + P2PKeyPair KeyPair `json:"p2p_key_pair"` +} + +// Accounts holds a list of accounts +type Accounts struct { + Data []Account `json:"data"` +} + +func toClientAccount(acc config.Account) Account { + var p2pkp, signingkp KeyPair + p2pkp.Pub, p2pkp.Pvt = acc.GetP2PKeyPair() + signingkp.Pub, signingkp.Pvt = acc.GetSigningKeyPair() + + return Account{ + EthereumAccount: EthAccount{ + Address: acc.GetEthereumAccount().Address, + }, + IdentityID: acc.GetIdentityID(), + ReceiveEventNotificationEndpoint: acc.GetReceiveEventNotificationEndpoint(), + EthereumDefaultAccountName: acc.GetEthereumDefaultAccountName(), + P2PKeyPair: p2pkp, + SigningKeyPair: signingkp, + } +} + +func toClientAccounts(accs []config.Account) Accounts { + var caccs Accounts + for _, acc := range accs { + caccs.Data = append(caccs.Data, toClientAccount(acc)) + } + + return caccs +} + +func isKeyPairEmpty(kp *KeyPair) bool { + kp.Pvt, kp.Pub = strings.TrimSpace(kp.Pvt), strings.TrimSpace(kp.Pub) + return kp.Pvt == "" || kp.Pub == "" +} + +func fromClientAccount(cacc Account) (config.Account, error) { + acc := new(configstore.Account) + if cacc.EthereumAccount.Address == "" || cacc.EthereumAccount.Key == "" { + return nil, errors.New("ethereum address/key cannot be empty") + } + ca := config.AccountConfig(cacc.EthereumAccount) + acc.EthereumAccount = &ca + acc.EthereumDefaultAccountName = cacc.EthereumDefaultAccountName + + if isKeyPairEmpty(&cacc.P2PKeyPair) { + return nil, errors.New("p2p key pair is invalid") + } + acc.P2PKeyPair = configstore.KeyPair(cacc.P2PKeyPair) + + if isKeyPairEmpty(&cacc.SigningKeyPair) { + return nil, errors.New("signing key pair is invalid") + } + acc.SigningKeyPair = configstore.KeyPair(cacc.SigningKeyPair) + + if len(cacc.IdentityID) < 1 { + return nil, errors.New("Identity ID cannot be empty") + } + + acc.IdentityID = cacc.IdentityID + acc.ReceiveEventNotificationEndpoint = cacc.ReceiveEventNotificationEndpoint + return acc, nil +} diff --git a/httpapi/coreapi/types_test.go b/httpapi/coreapi/types_test.go index 6abf53ec7..7d9f18e08 100644 --- a/httpapi/coreapi/types_test.go +++ b/httpapi/coreapi/types_test.go @@ -12,7 +12,6 @@ import ( "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/go-centrifuge/identity" "github.com/centrifuge/go-centrifuge/jobs" - documentpb "github.com/centrifuge/go-centrifuge/protobufs/gen/go/document" testingdocuments "github.com/centrifuge/go-centrifuge/testingutils/documents" testingidentity "github.com/centrifuge/go-centrifuge/testingutils/identity" "github.com/centrifuge/go-centrifuge/utils" @@ -22,8 +21,11 @@ import ( "github.com/stretchr/testify/mock" ) -func TestTypes_convertAttributes(t *testing.T) { - attrs := AttributeMap{ +func TestTypes_toAttributeMapResponse(t *testing.T) { + dec, err := documents.NewDecimal("100001.002") + assert.NoError(t, err) + + attrs := AttributeMapRequest{ "string_test": { Type: "string", Value: "hello, world!", @@ -33,26 +35,48 @@ func TestTypes_convertAttributes(t *testing.T) { Type: "decimal", Value: "100001.001", }, + + "monetary_test": { + Type: "monetary", + MonetaryValue: &MonetaryValue{ + ID: "USD", + Value: dec, + ChainID: []byte{1}, + }, + }, } atts, err := toDocumentAttributes(attrs) assert.NoError(t, err) - assert.Len(t, atts, 2) + assert.Len(t, atts, 3) var attrList []documents.Attribute for _, v := range atts { attrList = append(attrList, v) } - cattrs, err := convertAttributes(attrList) + cattrs, err := toAttributeMapResponse(attrList) assert.NoError(t, err) - assert.Equal(t, attrs, cattrs) + assert.Len(t, cattrs, len(attrs)) + assert.Equal(t, cattrs["string_test"].Value, attrs["string_test"].Value) + assert.Equal(t, cattrs["decimal_test"].Value, attrs["decimal_test"].Value) + assert.Equal(t, cattrs["monetary_test"].MonetaryValue, attrs["monetary_test"].MonetaryValue) + + attrs["monetary_test_empty"] = AttributeRequest{Type: "monetary"} + _, err = toDocumentAttributes(attrs) + assert.Error(t, err) + delete(attrs, "monetary_test_empty") + + attrs["monetary_test_dec_empty"] = AttributeRequest{Type: "monetary", MonetaryValue: &MonetaryValue{ID: "USD", ChainID: []byte{1}}} + _, err = toDocumentAttributes(attrs) + assert.Error(t, err) + delete(attrs, "monetary_test_dec_empty") - attrs["invalid"] = Attribute{Type: "unknown", Value: "some value"} + attrs["invalid"] = AttributeRequest{Type: "unknown", Value: "some value"} _, err = toDocumentAttributes(attrs) assert.Error(t, err) attrList = append(attrList, documents.Attribute{Value: documents.AttrVal{Type: "invalid"}}) - _, err = convertAttributes(attrList) + _, err = toAttributeMapResponse(attrList) assert.Error(t, err) } @@ -115,17 +139,17 @@ func TestTypes_toDocumentCreatePayload(t *testing.T) { request.Data = invoiceData() // success - payload, err := toDocumentsCreatePayload(request) + payload, err := ToDocumentsCreatePayload(request) assert.NoError(t, err) assert.Equal(t, payload.Scheme, "invoice") assert.NotNil(t, payload.Data) // failure - request.Attributes = map[string]Attribute{ + request.Attributes = map[string]AttributeRequest{ "invalid": {Type: "unknown", Value: "some value"}, } - _, err = toDocumentsCreatePayload(request) + _, err = ToDocumentsCreatePayload(request) assert.Error(t, err) } @@ -154,7 +178,7 @@ func TestTypes_convertNFTs(t *testing.T) { errLen int errMsg string nftLen int - expectedNFTs []*documentpb.NFT + expectedNFTs []NFT }{ { name: "1 nft, no error", @@ -174,11 +198,11 @@ func TestTypes_convertNFTs(t *testing.T) { }, isErr: false, nftLen: 1, - expectedNFTs: []*documentpb.NFT{ + expectedNFTs: []NFT{ { Registry: hexutil.Encode(regIDs[0][:20]), Owner: addrs[0].Hex(), - TokenId: hexutil.Encode(tokIDs[0]), + TokenID: hexutil.Encode(tokIDs[0]), TokenIndex: hexutil.Encode(tokIDx[0].Bytes()), }, }, @@ -207,17 +231,17 @@ func TestTypes_convertNFTs(t *testing.T) { }, isErr: false, nftLen: 2, - expectedNFTs: []*documentpb.NFT{ + expectedNFTs: []NFT{ { Registry: hexutil.Encode(regIDs[0][:20]), Owner: addrs[0].Hex(), - TokenId: hexutil.Encode(tokIDs[0]), + TokenID: hexutil.Encode(tokIDs[0]), TokenIndex: hexutil.Encode(tokIDx[0].Bytes()), }, { Registry: hexutil.Encode(regIDs[1][:20]), Owner: addrs[1].Hex(), - TokenId: hexutil.Encode(tokIDs[1]), + TokenID: hexutil.Encode(tokIDs[1]), TokenIndex: hexutil.Encode(tokIDx[1].Bytes()), }, }, @@ -248,11 +272,11 @@ func TestTypes_convertNFTs(t *testing.T) { errLen: 1, errMsg: "owner", nftLen: 1, - expectedNFTs: []*documentpb.NFT{ + expectedNFTs: []NFT{ { Registry: hexutil.Encode(regIDs[1][:20]), Owner: addrs[1].Hex(), - TokenId: hexutil.Encode(tokIDs[1]), + TokenID: hexutil.Encode(tokIDs[1]), TokenIndex: hexutil.Encode(tokIDx[1].Bytes()), }, }, @@ -261,6 +285,7 @@ func TestTypes_convertNFTs(t *testing.T) { name: "2 nft, CurrentIndexOfToken error", TR: func() documents.TokenRegistry { m := new(testingdocuments.MockRegistry) + m.On("OwnerOf", mock.Anything, mock.Anything).Return(addrs[0], nil).Once() m.On("OwnerOf", mock.Anything, mock.Anything).Return(addrs[1], nil).Once() m.On("CurrentIndexOfToken", mock.Anything, mock.Anything).Return(tokIDx[0], errors.New("CurrentIndexOfToken error")).Once() m.On("CurrentIndexOfToken", mock.Anything, mock.Anything).Return(tokIDx[1], nil).Once() @@ -278,15 +303,19 @@ func TestTypes_convertNFTs(t *testing.T) { }, } }, - isErr: true, - errLen: 1, - errMsg: "CurrentIndexOfToken", - nftLen: 1, - expectedNFTs: []*documentpb.NFT{ + isErr: false, + nftLen: 2, + expectedNFTs: []NFT{ + { + Registry: hexutil.Encode(regIDs[0][:20]), + Owner: addrs[0].Hex(), + TokenID: hexutil.Encode(tokIDs[0]), + TokenIndex: hexutil.Encode([]byte{}), + }, { Registry: hexutil.Encode(regIDs[1][:20]), Owner: addrs[1].Hex(), - TokenId: hexutil.Encode(tokIDs[1]), + TokenID: hexutil.Encode(tokIDs[1]), TokenIndex: hexutil.Encode(tokIDx[1].Bytes()), }, }, @@ -295,6 +324,8 @@ func TestTypes_convertNFTs(t *testing.T) { name: "2 nft, 2 CurrentIndexOfToken error", TR: func() documents.TokenRegistry { m := new(testingdocuments.MockRegistry) + m.On("OwnerOf", mock.Anything, mock.Anything).Return(addrs[0], nil).Once() + m.On("OwnerOf", mock.Anything, mock.Anything).Return(addrs[1], nil).Once() m.On("CurrentIndexOfToken", mock.Anything, mock.Anything).Return(tokIDx[0], errors.New("CurrentIndexOfToken error")).Once() m.On("CurrentIndexOfToken", mock.Anything, mock.Anything).Return(tokIDx[1], errors.New("CurrentIndexOfToken error")).Once() return m @@ -311,10 +342,23 @@ func TestTypes_convertNFTs(t *testing.T) { }, } }, - isErr: true, - errLen: 2, + isErr: false, errMsg: "CurrentIndexOfToken", - nftLen: 0, + nftLen: 2, + expectedNFTs: []NFT{ + { + Registry: hexutil.Encode(regIDs[0][:20]), + Owner: addrs[0].Hex(), + TokenID: hexutil.Encode(tokIDs[0]), + TokenIndex: hexutil.Encode([]byte{}), + }, + { + Registry: hexutil.Encode(regIDs[1][:20]), + Owner: addrs[1].Hex(), + TokenID: hexutil.Encode(tokIDs[1]), + TokenIndex: hexutil.Encode([]byte{}), + }, + }, }, { name: "2 nft, ownerOf and CurrentIndexOfToken error", @@ -339,9 +383,17 @@ func TestTypes_convertNFTs(t *testing.T) { } }, isErr: true, - errLen: 2, + errLen: 1, errMsg: "owner", - nftLen: 0, + nftLen: 1, + expectedNFTs: []NFT{ + { + Registry: hexutil.Encode(regIDs[1][:20]), + Owner: addrs[1].Hex(), + TokenID: hexutil.Encode(tokIDs[1]), + TokenIndex: hexutil.Encode([]byte{}), + }, + }, }, } for _, test := range tests { @@ -359,7 +411,7 @@ func TestTypes_convertNFTs(t *testing.T) { for i, nn := range n { assert.Equal(t, strings.ToLower(nn.Registry), strings.ToLower(test.expectedNFTs[i].Registry)) assert.Equal(t, strings.ToLower(nn.TokenIndex), strings.ToLower(test.expectedNFTs[i].TokenIndex)) - assert.Equal(t, strings.ToLower(nn.TokenID), strings.ToLower(test.expectedNFTs[i].TokenId)) + assert.Equal(t, strings.ToLower(nn.TokenID), strings.ToLower(test.expectedNFTs[i].TokenID)) assert.Equal(t, strings.ToLower(nn.Owner), strings.ToLower(test.expectedNFTs[i].Owner)) } } diff --git a/httpapi/router.go b/httpapi/router.go index 2c3e8a855..2d04e37da 100644 --- a/httpapi/router.go +++ b/httpapi/router.go @@ -4,15 +4,14 @@ import ( "context" "net/http" + "github.com/centrifuge/go-centrifuge/bootstrap" "github.com/centrifuge/go-centrifuge/config" "github.com/centrifuge/go-centrifuge/contextutil" - "github.com/centrifuge/go-centrifuge/documents" - "github.com/centrifuge/go-centrifuge/extensions/transferdetails" + "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/go-centrifuge/httpapi/coreapi" "github.com/centrifuge/go-centrifuge/httpapi/health" "github.com/centrifuge/go-centrifuge/httpapi/userapi" - "github.com/centrifuge/go-centrifuge/jobs" - "github.com/centrifuge/go-centrifuge/nft" + v2 "github.com/centrifuge/go-centrifuge/httpapi/v2" "github.com/centrifuge/go-centrifuge/utils" "github.com/centrifuge/go-centrifuge/utils/httputils" "github.com/ethereum/go-ethereum/common" @@ -24,7 +23,7 @@ import ( // Router returns the http mux for the server. // @title Centrifuge OS Node API // @description Centrifuge OS Node API -// @version 0.0.5 +// @version 0.0.6 // @contact.name Centrifuge // @contact.url https://github.com/centrifuge/go-centrifuge // @contact.email hello@centrifuge.io @@ -32,14 +31,22 @@ import ( // @license.name MIT // @host localhost:8082 // @schemes http -func Router( - config Config, - configSrv config.Service, - nftSrv nft.Service, - docsSrv documents.Service, - transferSrv transferdetails.Service, - jobsSrv jobs.Manager) *chi.Mux { +func Router(ctx context.Context) (*chi.Mux, error) { r := chi.NewRouter() + cctx, ok := ctx.Value(bootstrap.NodeObjRegistry).(map[string]interface{}) + if !ok { + return nil, errors.New("failed to get %s", bootstrap.NodeObjRegistry) + } + + cfg, ok := cctx[bootstrap.BootstrappedConfig].(Config) + if !ok { + return nil, errors.New("failed to get %s", bootstrap.BootstrappedConfig) + } + + configSrv, ok := cctx[config.BootstrappedConfigStorage].(config.Service) + if !ok { + return nil, errors.New("failed to get %s", config.BootstrappedConfigStorage) + } // add middlewares. do not change the order. Add any new middlewares to the bottom r.Use(middleware.Recoverer) @@ -47,15 +54,26 @@ func Router( r.Use(auth(configSrv)) // health check - health.Register(r, config) + health.Register(r, cfg) r.Route("/v1", func(r chi.Router) { // core apis - coreapi.Register(r, nftSrv, configSrv, docsSrv, jobsSrv) + coreapi.Register(cctx, r) // user apis - userapi.Register(r, docsSrv, nftSrv, transferSrv) + userapi.Register(cctx, r) }) - return r + + // beta apis + r.Route("/beta", func(r chi.Router) { + userapi.RegisterBeta(cctx, r) + }) + + // v2 apis + r.Route("/v2", func(r chi.Router) { + v2.Register(cctx, r) + }) + + return r, nil } // Config defines required methods for http API @@ -65,6 +83,7 @@ type Config interface { } func auth(configSrv config.Service) func(handler http.Handler) http.Handler { + // TODO(ved): regex would be a better alternative skippedURLs := []string{ "/ping", "/accounts", // since we use default account DID for endpoints diff --git a/httpapi/router_test.go b/httpapi/router_test.go index c2c143209..ac38509dd 100644 --- a/httpapi/router_test.go +++ b/httpapi/router_test.go @@ -3,12 +3,18 @@ package httpapi import ( + "context" "net/http" "net/http/httptest" "testing" + "github.com/centrifuge/go-centrifuge/bootstrap" "github.com/centrifuge/go-centrifuge/config" "github.com/centrifuge/go-centrifuge/config/configstore" + "github.com/centrifuge/go-centrifuge/httpapi/coreapi" + "github.com/centrifuge/go-centrifuge/httpapi/userapi" + v2 "github.com/centrifuge/go-centrifuge/httpapi/v2" + testingconfig "github.com/centrifuge/go-centrifuge/testingutils/config" testingidentity "github.com/centrifuge/go-centrifuge/testingutils/identity" testingnfts "github.com/centrifuge/go-centrifuge/testingutils/nfts" "github.com/stretchr/testify/assert" @@ -66,8 +72,26 @@ func TestRouter_auth(t *testing.T) { } func TestRouter(t *testing.T) { - r := Router(nil, nil, new(testingnfts.MockNFTService), nil, nil, nil) + cctx := map[string]interface{}{ + coreapi.BootstrappedCoreAPIService: coreapi.Service{}, + userapi.BootstrappedUserAPIService: userapi.Service{}, + bootstrap.BootstrappedInvoiceUnpaid: new(testingnfts.MockNFTService), + bootstrap.BootstrappedConfig: new(testingconfig.MockConfig), + config.BootstrappedConfigStorage: new(configstore.MockService), + v2.BootstrappedService: v2.Service{}, + } + + ctx := context.WithValue(context.Background(), bootstrap.NodeObjRegistry, cctx) + r, err := Router(ctx) + assert.NoError(t, err) assert.Len(t, r.Middlewares(), 3) - assert.Len(t, r.Routes(), 2) - assert.Len(t, r.Routes()[1].SubRoutes.Routes(), 12) + assert.Len(t, r.Routes(), 4) + // beta routes + assert.Len(t, r.Routes()[0].SubRoutes.Routes(), 3) + // health pattern + assert.Equal(t, "/ping", r.Routes()[1].Pattern) + // v1 routes + assert.Len(t, r.Routes()[2].SubRoutes.Routes(), 29) + // v2 routes + assert.Len(t, r.Routes()[3].SubRoutes.Routes(), 6) } diff --git a/httpapi/swagger.json b/httpapi/swagger.json new file mode 100644 index 000000000..6b4190bbd --- /dev/null +++ b/httpapi/swagger.json @@ -0,0 +1,4835 @@ +{ + "schemes": [ + "http" + ], + "swagger": "2.0", + "info": { + "description": "Centrifuge OS Node API", + "title": "Centrifuge OS Node API", + "contact": { + "name": "Centrifuge", + "url": "https://github.com/centrifuge/go-centrifuge", + "email": "hello@centrifuge.io" + }, + "license": { + "name": "MIT" + }, + "version": "0.0.6" + }, + "host": "localhost:8082", + "basePath": "/", + "paths": { + "/beta/nfts/registries/{registry_address}/mint": { + "post": { + "description": "Mints an NFT against a document.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "NFTsBeta" + ], + "summary": "Mints an NFT against a document.", + "operationId": "mint_nft", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "NFT registry address in hex", + "name": "registry_address", + "in": "path", + "required": true + }, + { + "description": "Mint NFT request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "$ref": "#/definitions/userapi.MintNFTRequest" + } + } + ], + "responses": { + "202": { + "description": "Accepted", + "schema": { + "type": "object", + "$ref": "#/definitions/userapi.MintNFTResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + } + }, + "/beta/nfts/registries/{registry_address}/tokens/{token_id}/owner": { + "get": { + "description": "Returns the Owner of the given NFT.", + "produces": [ + "application/json" + ], + "tags": [ + "NFTsBeta" + ], + "summary": "Returns the Owner of the given NFT.", + "operationId": "owner_of_nft", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "NFT token ID in hex", + "name": "token_id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Registry address in hex", + "name": "registry_address", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "$ref": "#/definitions/userapi.NFTOwnerResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + } + }, + "/beta/nfts/registries/{registry_address}/tokens/{token_id}/transfer": { + "post": { + "description": "Transfers given NFT to provide address.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "NFTsBeta" + ], + "summary": "Transfers given NFT to provide address.", + "operationId": "transfer_nft", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "NFT registry address in hex", + "name": "registry_address", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "NFT token ID in hex", + "name": "token_id", + "in": "path", + "required": true + }, + { + "description": "Mint NFT request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "$ref": "#/definitions/userapi.TransferNFTRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "$ref": "#/definitions/userapi.TransferNFTResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + } + }, + "/ping": { + "get": { + "description": "returns node version and network", + "produces": [ + "application/json" + ], + "tags": [ + "Health" + ], + "summary": "Health check for Node", + "operationId": "ping", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "$ref": "#/definitions/health.Pong" + } + } + } + } + }, + "/v1/accounts": { + "get": { + "description": "Returns all the accounts in the node.", + "produces": [ + "application/json" + ], + "tags": [ + "Accounts" + ], + "summary": "Returns all the accounts in the node.", + "operationId": "get_accounts", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "$ref": "#/definitions/coreapi.Accounts" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + }, + "post": { + "description": "Creates a new account without any default configurations.", + "produces": [ + "application/json" + ], + "tags": [ + "Accounts" + ], + "summary": "Creates a new account without any default configurations.", + "operationId": "create_account", + "parameters": [ + { + "description": "Account Create request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "$ref": "#/definitions/coreapi.Account" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "$ref": "#/definitions/coreapi.Account" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + } + }, + "/v1/accounts/generate": { + "post": { + "description": "Generates a new account with defaults.", + "produces": [ + "application/json" + ], + "tags": [ + "Accounts" + ], + "summary": "Generates a new account with defaults.", + "operationId": "generate_account", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "$ref": "#/definitions/coreapi.Account" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + } + }, + "/v1/accounts/{account_id}": { + "get": { + "description": "Returns the account associated with accountID.", + "produces": [ + "application/json" + ], + "tags": [ + "Accounts" + ], + "summary": "Returns the account associated with accountID.", + "operationId": "get_account", + "parameters": [ + { + "type": "string", + "description": "Account ID", + "name": "account_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "$ref": "#/definitions/coreapi.Account" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + }, + "put": { + "description": "Updates an existing account.", + "produces": [ + "application/json" + ], + "tags": [ + "Accounts" + ], + "summary": "Updates an existing account.", + "operationId": "update_account", + "parameters": [ + { + "type": "string", + "description": "Account ID", + "name": "account_id", + "in": "path", + "required": true + }, + { + "description": "Account Update request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "$ref": "#/definitions/coreapi.Account" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "$ref": "#/definitions/coreapi.Account" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + } + }, + "/v1/accounts/{account_id}/sign": { + "post": { + "description": "Signs and returns the signature of the Payload.", + "produces": [ + "application/json" + ], + "tags": [ + "Accounts" + ], + "summary": "Signs and returns the signature of the Payload.", + "operationId": "account_sign", + "parameters": [ + { + "type": "string", + "description": "Account ID", + "name": "account_id", + "in": "path", + "required": true + }, + { + "description": "Sign request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "$ref": "#/definitions/coreapi.SignRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "$ref": "#/definitions/coreapi.SignResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + } + }, + "/v1/documents": { + "post": { + "description": "Creates a new document and anchors it.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Documents" + ], + "summary": "Creates a new document and anchors it.", + "operationId": "create_document", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "description": "Document Create request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "$ref": "#/definitions/coreapi.CreateDocumentRequest" + } + } + ], + "responses": { + "202": { + "description": "Accepted", + "schema": { + "type": "object", + "$ref": "#/definitions/coreapi.DocumentResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + } + }, + "/v1/documents/{document_id}": { + "get": { + "description": "Returns the latest version of the document.", + "produces": [ + "application/json" + ], + "tags": [ + "Documents" + ], + "summary": "Returns the latest version of the document.", + "operationId": "get_document", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "Document Identifier", + "name": "document_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "$ref": "#/definitions/coreapi.DocumentResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + }, + "put": { + "description": "Updates an existing document and anchors it.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Documents" + ], + "summary": "Updates an existing document and anchors it.", + "operationId": "update_document", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "Document Identifier", + "name": "document_id", + "in": "path", + "required": true + }, + { + "description": "Document Update request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "$ref": "#/definitions/coreapi.CreateDocumentRequest" + } + } + ], + "responses": { + "202": { + "description": "Accepted", + "schema": { + "type": "object", + "$ref": "#/definitions/coreapi.DocumentResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + } + }, + "/v1/documents/{document_id}/funding_agreements": { + "get": { + "description": "Returns all the funding agreements in the document associated with document_id.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Funding Agreements" + ], + "summary": "Returns all the funding agreements in the document associated with document_id.", + "operationId": "get_funding_agreements", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "Document Identifier", + "name": "document_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "$ref": "#/definitions/userapi.FundingListResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + }, + "post": { + "description": "Creates a new funding agreement on the document.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Funding Agreements" + ], + "summary": "Creates a new funding agreement on the document.", + "operationId": "create_funding_agreement", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "Document Identifier", + "name": "document_id", + "in": "path", + "required": true + }, + { + "description": "Funding agreement Create Request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "$ref": "#/definitions/userapi.FundingRequest" + } + } + ], + "responses": { + "202": { + "description": "Accepted", + "schema": { + "type": "object", + "$ref": "#/definitions/userapi.FundingResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + } + }, + "/v1/documents/{document_id}/funding_agreements/{agreement_id}": { + "get": { + "description": "Returns the funding agreement associated with agreement_id in the document.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Funding Agreements" + ], + "summary": "Returns the funding agreement associated with agreement_id in the document.", + "operationId": "get_funding_agreement", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "Document Identifier", + "name": "document_id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Funding agreement Identifier", + "name": "agreement_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "$ref": "#/definitions/userapi.FundingResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + }, + "put": { + "description": "Updates the funding agreement associated with agreement_id in the document.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Funding Agreements" + ], + "summary": "Updates the funding agreement associated with agreement_id in the document.", + "operationId": "update_funding_agreement", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "Document Identifier", + "name": "document_id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Funding agreement Identifier", + "name": "agreement_id", + "in": "path", + "required": true + }, + { + "description": "Funding Agreement Update Request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "$ref": "#/definitions/userapi.FundingRequest" + } + } + ], + "responses": { + "202": { + "description": "Accepted", + "schema": { + "type": "object", + "$ref": "#/definitions/userapi.FundingResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + } + }, + "/v1/documents/{document_id}/funding_agreements/{agreement_id}/sign": { + "post": { + "description": "Signs the funding agreement associated with agreement_id.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Funding Agreements" + ], + "summary": "Signs the funding agreement associated with agreement_id.", + "operationId": "sign_funding_agreement", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "Document Identifier", + "name": "document_id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Funding agreement Identifier", + "name": "agreement_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "$ref": "#/definitions/userapi.FundingResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + } + }, + "/v1/documents/{document_id}/proofs": { + "post": { + "description": "Generates proofs for the fields from latest version of the document.", + "produces": [ + "application/json" + ], + "tags": [ + "Documents" + ], + "summary": "Generates proofs for the fields from latest version of the document.", + "operationId": "generate_document_proofs", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "Document Identifier", + "name": "document_id", + "in": "path", + "required": true + }, + { + "description": "Document proof request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "$ref": "#/definitions/coreapi.ProofsRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "$ref": "#/definitions/coreapi.ProofsResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + } + }, + "/v1/documents/{document_id}/transfer_details": { + "get": { + "description": "Returns a list of the latest versions of all transfer details on the document.", + "produces": [ + "application/json" + ], + "tags": [ + "Transfer Details" + ], + "summary": "Returns a list of the latest versions of all transfer details on the document.", + "operationId": "list_transfer_details", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "Document Identifier", + "name": "document_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "$ref": "#/definitions/userapi.TransferDetailListResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + }, + "post": { + "description": "Creates a new transfer detail extension on a document and anchors it.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Transfer Details" + ], + "summary": "Creates a new transfer detail extension on a document and anchors it.", + "operationId": "create_transfer_detail", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "description": "Transfer Detail Create Request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "$ref": "#/definitions/userapi.CreateTransferDetailRequest" + } + }, + { + "type": "string", + "description": "Document Identifier", + "name": "document_id", + "in": "path", + "required": true + } + ], + "responses": { + "202": { + "description": "Accepted", + "schema": { + "type": "object", + "$ref": "#/definitions/userapi.TransferDetailResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + } + }, + "/v1/documents/{document_id}/transfer_details/{transfer_id}": { + "get": { + "description": "Returns the latest version of the transfer detail.", + "produces": [ + "application/json" + ], + "tags": [ + "Transfer Details" + ], + "summary": "Returns the latest version of the transfer detail.", + "operationId": "get_transfer_detail", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "Document Identifier", + "name": "document_id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Transfer Detail Identifier", + "name": "transfer_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "$ref": "#/definitions/userapi.TransferDetailResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + }, + "put": { + "description": "Updates a new transfer detail extension on a document and anchors it.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Transfer Details" + ], + "summary": "Updates a new transfer detail extension on a document and anchors it.", + "operationId": "update_transfer_detail", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "description": "Transfer Detail Update Request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "$ref": "#/definitions/userapi.UpdateTransferDetailRequest" + } + }, + { + "type": "string", + "description": "Document Identifier", + "name": "document_id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Transfer Detail Identifier", + "name": "transfer_id", + "in": "path", + "required": true + } + ], + "responses": { + "202": { + "description": "Accepted", + "schema": { + "type": "object", + "$ref": "#/definitions/userapi.TransferDetailResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + } + }, + "/v1/documents/{document_id}/versions/{version_id}": { + "get": { + "description": "Returns the specific version of the document.", + "produces": [ + "application/json" + ], + "tags": [ + "Documents" + ], + "summary": "Returns the specific version of the document.", + "operationId": "get_document_version", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "Document Identifier", + "name": "document_id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Document Version Identifier", + "name": "version_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "$ref": "#/definitions/coreapi.DocumentResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + } + }, + "/v1/documents/{document_id}/versions/{version_id}/funding_agreements": { + "get": { + "description": "Returns all the funding agreements from a specific version of the document.", + "produces": [ + "application/json" + ], + "tags": [ + "Funding Agreements" + ], + "summary": "Returns all the funding agreements from a specific version of the document.", + "operationId": "get_funding_agreements_version", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "Document Identifier", + "name": "document_id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Document Version Identifier", + "name": "version_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "$ref": "#/definitions/userapi.FundingListResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + } + }, + "/v1/documents/{document_id}/versions/{version_id}/funding_agreements/{agreement_id}": { + "get": { + "description": "Returns the funding agreement from a specific version of the document.", + "produces": [ + "application/json" + ], + "tags": [ + "Funding Agreements" + ], + "summary": "Returns the funding agreement from a specific version of the document.", + "operationId": "get_funding_agreement_version", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "Document Identifier", + "name": "document_id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Document Version Identifier", + "name": "version_id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Funding agreement Identifier", + "name": "agreement_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "$ref": "#/definitions/userapi.FundingResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + } + }, + "/v1/documents/{document_id}/versions/{version_id}/proofs": { + "post": { + "description": "Generates proofs for the fields from a specific document version.", + "produces": [ + "application/json" + ], + "tags": [ + "Documents" + ], + "summary": "Generates proofs for the fields from a specific document version.", + "operationId": "generate_document_version_proofs", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "Document Identifier", + "name": "document_id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Document Version Identifier", + "name": "version_id", + "in": "path", + "required": true + }, + { + "description": "Document proof request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "$ref": "#/definitions/coreapi.ProofsRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "$ref": "#/definitions/coreapi.ProofsResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + } + }, + "/v1/entities": { + "post": { + "description": "Creates a new Entity and anchors it.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Entities" + ], + "summary": "Creates a new Entity and anchors it.", + "operationId": "create_entity", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "description": "Entity Create request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "$ref": "#/definitions/userapi.CreateEntityRequest" + } + } + ], + "responses": { + "202": { + "description": "Accepted", + "schema": { + "type": "object", + "$ref": "#/definitions/userapi.EntityResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + } + }, + "/v1/entities/{document_id}": { + "get": { + "description": "Returns the latest version of the Entity.", + "produces": [ + "application/json" + ], + "tags": [ + "Entities" + ], + "summary": "Returns the latest version of the Entity.", + "operationId": "get_entity", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "Document Identifier", + "name": "document_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "$ref": "#/definitions/userapi.EntityResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + }, + "put": { + "description": "Updates an existing Entity and anchors it.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Entities" + ], + "summary": "Updates an existing Entity and anchors it.", + "operationId": "update_entity", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "Document Identifier", + "name": "document_id", + "in": "path", + "required": true + }, + { + "description": "Entity Create request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "$ref": "#/definitions/userapi.CreateEntityRequest" + } + } + ], + "responses": { + "202": { + "description": "Accepted", + "schema": { + "type": "object", + "$ref": "#/definitions/userapi.EntityResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + } + }, + "/v1/entities/{document_id}/revoke": { + "post": { + "description": "Revoke revokes target id's access to entity.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Entities" + ], + "summary": "Revoke revokes target id's access to entity.", + "operationId": "revoke_entity", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "Document Identifier", + "name": "document_id", + "in": "path", + "required": true + }, + { + "description": "Entity Revoke request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "$ref": "#/definitions/userapi.ShareEntityRequest" + } + } + ], + "responses": { + "202": { + "description": "Accepted", + "schema": { + "type": "object", + "$ref": "#/definitions/userapi.ShareEntityResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + } + }, + "/v1/entities/{document_id}/share": { + "post": { + "description": "Share gives entity access to target identity.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Entities" + ], + "summary": "Share gives entity access to target identity.", + "operationId": "share_entity", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "Document Identifier", + "name": "document_id", + "in": "path", + "required": true + }, + { + "description": "Entity Share request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "$ref": "#/definitions/userapi.ShareEntityRequest" + } + } + ], + "responses": { + "202": { + "description": "Accepted", + "schema": { + "type": "object", + "$ref": "#/definitions/userapi.ShareEntityResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + } + }, + "/v1/invoices": { + "post": { + "description": "Creates a new invoice document and anchors it.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Invoices" + ], + "summary": "Creates a new invoice document and anchors it.", + "operationId": "create_invoice", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "description": "Invoice Create Request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "$ref": "#/definitions/userapi.CreateInvoiceRequest" + } + } + ], + "responses": { + "202": { + "description": "Accepted", + "schema": { + "type": "object", + "$ref": "#/definitions/userapi.InvoiceResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + } + }, + "/v1/invoices/{document_id}": { + "get": { + "description": "Returns the latest version of the Invoice.", + "produces": [ + "application/json" + ], + "tags": [ + "Invoices" + ], + "summary": "Returns the latest version of the Invoice.", + "operationId": "get_invoice", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "Document Identifier", + "name": "document_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "$ref": "#/definitions/userapi.InvoiceResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + }, + "put": { + "description": "Updates an existing invoice and anchors it.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Invoices" + ], + "summary": "Updates an existing Invoice and anchors it.", + "operationId": "update_invoice", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "Document Identifier", + "name": "document_id", + "in": "path", + "required": true + }, + { + "description": "Invoice Update request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "$ref": "#/definitions/userapi.CreateInvoiceRequest" + } + } + ], + "responses": { + "202": { + "description": "Accepted", + "schema": { + "type": "object", + "$ref": "#/definitions/userapi.InvoiceResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + } + }, + "/v1/invoices/{document_id}/mint/unpaid": { + "post": { + "description": "Mints an NFT for an unpaid invoice document.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Invoices" + ], + "summary": "Mints an NFT for an unpaid invoice document.", + "operationId": "invoice_unpaid_nft", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "description": "Invoice Unpaid NFT Mint Request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "$ref": "#/definitions/userapi.NFTMintInvoiceUnpaidRequest" + } + } + ], + "responses": { + "202": { + "description": "Accepted", + "schema": { + "type": "object", + "$ref": "#/definitions/userapi.NFTMintResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + } + }, + "/v1/invoices/{document_id}/versions/{version_id}": { + "get": { + "description": "Returns the specific version of an Invoice.", + "produces": [ + "application/json" + ], + "tags": [ + "Invoices" + ], + "summary": "Returns the specific version of an Invoice.", + "operationId": "get_invoice_version", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "Document Identifier", + "name": "document_id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Document Version Identifier", + "name": "version_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "$ref": "#/definitions/userapi.InvoiceResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + } + }, + "/v1/jobs/{job_id}": { + "get": { + "description": "Returns the status of a given Job.", + "produces": [ + "application/json" + ], + "tags": [ + "Jobs" + ], + "summary": "Returns the status of a given Job.", + "operationId": "get_job_status", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "Job ID", + "name": "job_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "$ref": "#/definitions/jobs.StatusResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + } + }, + "/v1/nfts/registries/{registry_address}/mint": { + "post": { + "description": "Mints an NFT against a document.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "NFTs" + ], + "summary": "Mints an NFT against a document.", + "operationId": "mint_nft", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "NFT registry address in hex", + "name": "registry_address", + "in": "path", + "required": true + }, + { + "description": "Mint NFT request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "$ref": "#/definitions/coreapi.MintNFTRequest" + } + } + ], + "responses": { + "202": { + "description": "Accepted", + "schema": { + "type": "object", + "$ref": "#/definitions/coreapi.MintNFTResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + } + }, + "/v1/nfts/registries/{registry_address}/tokens/{token_id}/owner": { + "get": { + "description": "Returns the Owner of the given NFT.", + "produces": [ + "application/json" + ], + "tags": [ + "NFTs" + ], + "summary": "Returns the Owner of the given NFT.", + "operationId": "owner_of_nft", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "NFT token ID in hex", + "name": "token_id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Registry address in hex", + "name": "registry_address", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "$ref": "#/definitions/coreapi.NFTOwnerResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + } + }, + "/v1/nfts/registries/{registry_address}/tokens/{token_id}/transfer": { + "post": { + "description": "Transfers given NFT to provide address.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "NFTs" + ], + "summary": "Transfers given NFT to provide address.", + "operationId": "transfer_nft", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "NFT registry address in hex", + "name": "registry_address", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "NFT token ID in hex", + "name": "token_id", + "in": "path", + "required": true + }, + { + "description": "Mint NFT request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "$ref": "#/definitions/coreapi.TransferNFTRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "$ref": "#/definitions/coreapi.TransferNFTResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + } + }, + "/v1/relationships/{document_id}/entity": { + "get": { + "description": "Returns the latest version of the Entity through relationship ID.", + "produces": [ + "application/json" + ], + "tags": [ + "Entities" + ], + "summary": "Returns the latest version of the Entity through relationship ID.", + "operationId": "get_entity_through_relationship_id", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "Entity Relationship Document Identifier", + "name": "document_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "$ref": "#/definitions/userapi.EntityResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + } + }, + "/v2/documents": { + "post": { + "description": "Creates a new document.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Documents" + ], + "summary": "Creates a new document.", + "operationId": "create_document_v2", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "description": "Document Create request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "$ref": "#/definitions/v2.CreateDocumentRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "type": "object", + "$ref": "#/definitions/coreapi.DocumentResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + } + }, + "/v2/documents/{document_id}": { + "patch": { + "description": "Updates a pending document.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Documents" + ], + "summary": "Updates a pending document.", + "operationId": "update_document_v2", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "description": "Document Update request", + "name": "body", + "in": "body", + "required": true, + "schema": { + "type": "object", + "$ref": "#/definitions/v2.UpdateDocumentRequest" + } + }, + { + "type": "string", + "description": "Document Identifier", + "name": "document_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "$ref": "#/definitions/coreapi.DocumentResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + } + }, + "/v2/documents/{document_id}/commit": { + "post": { + "description": "Commits a pending document.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Documents" + ], + "summary": "Commits a pending document.", + "operationId": "commit_document_v2", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "Document Identifier", + "name": "document_id", + "in": "path", + "required": true + } + ], + "responses": { + "202": { + "description": "Accepted", + "schema": { + "type": "object", + "$ref": "#/definitions/coreapi.DocumentResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + } + }, + "/v2/documents/{document_id}/committed": { + "get": { + "description": "Returns the latest committed document associated with docID.", + "produces": [ + "application/json" + ], + "tags": [ + "Documents" + ], + "summary": "Returns the latest committed document associated with docID.", + "operationId": "get_committed_document", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "Document Identifier", + "name": "document_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "$ref": "#/definitions/coreapi.DocumentResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + } + }, + "/v2/documents/{document_id}/pending": { + "get": { + "description": "Returns the pending document associated with docID.", + "produces": [ + "application/json" + ], + "tags": [ + "Documents" + ], + "summary": "Returns the pending document associated with docID.", + "operationId": "get_pending_document", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "Document Identifier", + "name": "document_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "$ref": "#/definitions/coreapi.DocumentResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + } + }, + "/v2/documents/{document_id}/versions/{version_id}": { + "get": { + "description": "Returns the specific version of the document.", + "produces": [ + "application/json" + ], + "tags": [ + "Documents" + ], + "summary": "Returns the specific version of the document.", + "operationId": "get_document_version_v2", + "parameters": [ + { + "type": "string", + "description": "Hex encoded centrifuge ID of the account for the intended API action", + "name": "authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "description": "Document Identifier", + "name": "document_id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Document Version Identifier", + "name": "version_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "$ref": "#/definitions/coreapi.DocumentResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "object", + "$ref": "#/definitions/httputils.HTTPError" + } + } + } + } + }, + "/webhook": { + "post": { + "description": "Webhook is a place holder to describe webhook response in swagger.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Webhook" + ], + "summary": "Webhook is a place holder to describe webhook response in swagger.", + "operationId": "webhook", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "object", + "$ref": "#/definitions/notification.Message" + } + } + } + } + } + }, + "definitions": { + "byteutils.HexBytes": { + "type": "array", + "items": {} + }, + "byteutils.OptionalHex": { + "type": "object", + "properties": { + "HexBytes": { + "type": "array", + "items": {} + } + } + }, + "coreapi.Account": { + "type": "object", + "properties": { + "eth_account": { + "type": "object", + "$ref": "#/definitions/coreapi.EthAccount" + }, + "eth_default_account_name": { + "type": "string" + }, + "identity_id": { + "type": "string" + }, + "p2p_key_pair": { + "type": "object", + "$ref": "#/definitions/coreapi.KeyPair" + }, + "receive_event_notification_endpoint": { + "type": "string" + }, + "signing_key_pair": { + "type": "object", + "$ref": "#/definitions/coreapi.KeyPair" + } + } + }, + "coreapi.Accounts": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/coreapi.Account" + } + } + } + }, + "coreapi.AttributeMapRequest": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "monetary_value": { + "type": "object", + "$ref": "#/definitions/coreapi.MonetaryValue" + }, + "type": { + "type": "string", + "enum": [ + "integer", + "decimal", + "string", + "bytes", + "timestamp", + "monetary" + ] + }, + "value": { + "type": "string" + } + } + } + }, + "coreapi.AttributeMapResponse": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "monetary_value": { + "type": "object", + "$ref": "#/definitions/coreapi.MonetaryValue" + }, + "type": { + "type": "string", + "enum": [ + "integer", + "decimal", + "string", + "bytes", + "timestamp", + "monetary" + ] + }, + "value": { + "type": "string" + } + } + } + }, + "coreapi.AttributeRequest": { + "type": "object", + "properties": { + "monetary_value": { + "type": "object", + "$ref": "#/definitions/coreapi.MonetaryValue" + }, + "type": { + "type": "string", + "enum": [ + "integer", + "decimal", + "string", + "bytes", + "timestamp", + "monetary" + ] + }, + "value": { + "type": "string" + } + } + }, + "coreapi.AttributeResponse": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "monetary_value": { + "type": "object", + "$ref": "#/definitions/coreapi.MonetaryValue" + }, + "type": { + "type": "string", + "enum": [ + "integer", + "decimal", + "string", + "bytes", + "timestamp", + "monetary" + ] + }, + "value": { + "type": "string" + } + } + }, + "coreapi.CreateDocumentRequest": { + "type": "object", + "properties": { + "attributes": { + "type": "object", + "$ref": "#/definitions/coreapi.AttributeMapRequest" + }, + "data": { + "type": "object" + }, + "read_access": { + "type": "array", + "items": { + "type": "string" + } + }, + "scheme": { + "type": "string", + "enum": [ + "generic", + "invoice", + "entity" + ] + }, + "write_access": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "coreapi.DocumentResponse": { + "type": "object", + "properties": { + "attributes": { + "type": "object", + "$ref": "#/definitions/coreapi.AttributeMapResponse" + }, + "data": { + "type": "object" + }, + "header": { + "type": "object", + "$ref": "#/definitions/coreapi.ResponseHeader" + }, + "scheme": { + "type": "string", + "enum": [ + "generic", + "invoice", + "entity" + ] + } + } + }, + "coreapi.EthAccount": { + "type": "object", + "properties": { + "address": { + "type": "string" + }, + "key": { + "type": "string" + }, + "password": { + "type": "string" + } + } + }, + "coreapi.KeyPair": { + "type": "object", + "properties": { + "pub": { + "type": "string" + }, + "pvt": { + "type": "string" + } + } + }, + "coreapi.MintNFTRequest": { + "type": "object", + "properties": { + "deposit_address": { + "type": "string" + }, + "document_id": { + "type": "string" + }, + "grant_nft_access": { + "type": "boolean" + }, + "proof_fields": { + "type": "array", + "items": { + "type": "string" + } + }, + "submit_nft_owner_access_proof": { + "type": "boolean" + }, + "submit_token_proof": { + "type": "boolean" + } + } + }, + "coreapi.MintNFTResponse": { + "type": "object", + "properties": { + "deposit_address": { + "type": "string" + }, + "document_id": { + "type": "string" + }, + "header": { + "type": "object", + "$ref": "#/definitions/coreapi.NFTResponseHeader" + }, + "registry_address": { + "type": "string" + }, + "token_id": { + "type": "string" + } + } + }, + "coreapi.MonetaryValue": { + "type": "object", + "properties": { + "chain_id": { + "type": "string" + }, + "id": { + "type": "string" + }, + "value": { + "type": "string" + } + } + }, + "coreapi.NFT": { + "type": "object", + "properties": { + "owner": { + "type": "string" + }, + "registry": { + "type": "string" + }, + "token_id": { + "type": "string" + }, + "token_index": { + "type": "string" + } + } + }, + "coreapi.NFTOwnerResponse": { + "type": "object", + "properties": { + "owner": { + "type": "string" + }, + "registry_address": { + "type": "string" + }, + "token_id": { + "type": "string" + } + } + }, + "coreapi.NFTResponseHeader": { + "type": "object", + "properties": { + "job_id": { + "type": "string" + } + } + }, + "coreapi.ProofResponseHeader": { + "type": "object", + "properties": { + "document_id": { + "type": "string" + }, + "state": { + "type": "string" + }, + "version_id": { + "type": "string" + } + } + }, + "coreapi.ProofsRequest": { + "type": "object", + "properties": { + "fields": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "coreapi.ProofsResponse": { + "type": "object", + "properties": { + "field_proofs": { + "type": "array", + "items": { + "$ref": "#/definitions/documents.Proof" + } + }, + "header": { + "type": "object", + "$ref": "#/definitions/coreapi.ProofResponseHeader" + } + } + }, + "coreapi.ResponseHeader": { + "type": "object", + "properties": { + "author": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "document_id": { + "type": "string" + }, + "job_id": { + "type": "string" + }, + "nfts": { + "type": "array", + "items": { + "$ref": "#/definitions/coreapi.NFT" + } + }, + "read_access": { + "type": "array", + "items": { + "type": "string" + } + }, + "status": { + "type": "string" + }, + "version_id": { + "type": "string" + }, + "write_access": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "coreapi.SignRequest": { + "type": "object", + "properties": { + "payload": { + "type": "string" + } + } + }, + "coreapi.SignResponse": { + "type": "object", + "properties": { + "payload": { + "type": "string" + }, + "public_key": { + "type": "string" + }, + "signature": { + "type": "string" + }, + "signer_id": { + "type": "string" + } + } + }, + "coreapi.TransferNFTRequest": { + "type": "object", + "properties": { + "to": { + "type": "string" + } + } + }, + "coreapi.TransferNFTResponse": { + "type": "object", + "properties": { + "header": { + "type": "object", + "$ref": "#/definitions/coreapi.NFTResponseHeader" + }, + "registry_address": { + "type": "string" + }, + "to": { + "type": "string" + }, + "token_id": { + "type": "string" + } + } + }, + "documents.BinaryAttachment": { + "type": "object", + "properties": { + "checksum": { + "description": "the md5 checksum of the original file for easier verification", + "type": "string" + }, + "data": { + "type": "string" + }, + "file_type": { + "description": "mime type of attached file", + "type": "string" + }, + "name": { + "type": "string" + }, + "size": { + "description": "in bytes", + "type": "integer" + } + } + }, + "documents.PaymentDetails": { + "type": "object", + "properties": { + "amount": { + "type": "string" + }, + "bank_account_currency": { + "type": "string" + }, + "bank_account_holder_name": { + "type": "string" + }, + "bank_account_number": { + "type": "string" + }, + "bank_address": { + "type": "string" + }, + "bank_country": { + "type": "string" + }, + "bank_key": { + "type": "string" + }, + "bank_name": { + "type": "string" + }, + "crypto_chain_uri": { + "description": "the ID of the chain to use in URI format. e.g. \"ethereum://42/\u003ctokenaddress\u003e\"", + "type": "string" + }, + "crypto_from": { + "description": "from address", + "type": "string" + }, + "crypto_to": { + "description": "to address", + "type": "string" + }, + "crypto_transaction_id": { + "description": "the transaction in which the payment happened", + "type": "string" + }, + "currency": { + "type": "string" + }, + "date_executed": { + "type": "string" + }, + "id": { + "description": "identifying this payment. could be a sequential number, could be a transaction hash of the crypto payment", + "type": "string" + }, + "payee": { + "description": "centrifuge id of payee", + "type": "string" + }, + "payer": { + "description": "centrifuge id of payer", + "type": "string" + }, + "reference": { + "description": "payment reference (e.g. reference field on bank transfer)", + "type": "string" + } + } + }, + "documents.Proof": { + "type": "object", + "properties": { + "hash": { + "type": "string" + }, + "property": { + "type": "string" + }, + "salt": { + "type": "string" + }, + "sorted_hashes": { + "type": "array", + "items": { + "type": "string" + } + }, + "value": { + "type": "string" + } + } + }, + "entity.Address": { + "type": "object", + "properties": { + "address_line_1": { + "type": "string" + }, + "address_line_2": { + "type": "string" + }, + "contact_person": { + "type": "string" + }, + "country": { + "type": "string" + }, + "is_main": { + "type": "boolean" + }, + "is_pay_to": { + "type": "boolean" + }, + "is_remit_to": { + "type": "boolean" + }, + "is_ship_to": { + "type": "boolean" + }, + "label": { + "type": "string" + }, + "state": { + "type": "string" + }, + "zip": { + "type": "string" + } + } + }, + "entity.BankPaymentMethod": { + "type": "object", + "properties": { + "address": { + "type": "object", + "$ref": "#/definitions/entity.Address" + }, + "bank_account_number": { + "type": "string" + }, + "bank_key": { + "type": "string" + }, + "holder_name": { + "type": "string" + }, + "identifier": { + "type": "string" + }, + "supported_currency": { + "type": "string" + } + } + }, + "entity.Contact": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "fax": { + "type": "string" + }, + "name": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "title": { + "type": "string" + } + } + }, + "entity.CryptoPaymentMethod": { + "type": "object", + "properties": { + "chain_uri": { + "type": "string" + }, + "identifier": { + "type": "string" + }, + "supported_currency": { + "type": "string" + }, + "to": { + "type": "string" + } + } + }, + "entity.Data": { + "type": "object", + "properties": { + "addresses": { + "type": "array", + "items": { + "$ref": "#/definitions/entity.Address" + } + }, + "contacts": { + "type": "array", + "items": { + "$ref": "#/definitions/entity.Contact" + } + }, + "identity": { + "type": "string" + }, + "legal_name": { + "type": "string" + }, + "payment_details": { + "type": "array", + "items": { + "$ref": "#/definitions/entity.PaymentDetail" + } + } + } + }, + "entity.OtherPaymentMethod": { + "type": "object", + "properties": { + "identifier": { + "type": "string" + }, + "pay_to": { + "type": "string" + }, + "supported_currency": { + "type": "string" + }, + "type": { + "type": "string" + } + } + }, + "entity.PaymentDetail": { + "type": "object", + "properties": { + "bank_payment_method": { + "type": "object", + "$ref": "#/definitions/entity.BankPaymentMethod" + }, + "crypto_payment_method": { + "type": "object", + "$ref": "#/definitions/entity.CryptoPaymentMethod" + }, + "other_payment_method": { + "type": "object", + "$ref": "#/definitions/entity.OtherPaymentMethod" + }, + "predefined": { + "type": "boolean" + } + } + }, + "funding.Data": { + "type": "object", + "properties": { + "agreement_id": { + "type": "string" + }, + "amount": { + "type": "string" + }, + "apr": { + "type": "string" + }, + "borrower_id": { + "type": "string" + }, + "currency": { + "type": "string" + }, + "days": { + "type": "string" + }, + "fee": { + "type": "string" + }, + "funder_id": { + "type": "string" + }, + "nft_address": { + "type": "string" + }, + "payment_details_id": { + "type": "string" + }, + "repayment_amount": { + "type": "string" + }, + "repayment_due_date": { + "type": "string" + }, + "repayment_occurred_date": { + "type": "string" + } + } + }, + "funding.Signature": { + "type": "object", + "properties": { + "identity": { + "type": "string" + }, + "outdated_signature": { + "type": "string" + }, + "signed_version": { + "type": "string" + }, + "valid": { + "type": "string" + } + } + }, + "health.Pong": { + "type": "object", + "properties": { + "network": { + "type": "string" + }, + "version": { + "type": "string" + } + } + }, + "httputils.HTTPError": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + }, + "identity.DID": { + "type": "array", + "items": {} + }, + "invoice.Data": { + "type": "object", + "properties": { + "attachments": { + "type": "array", + "items": { + "$ref": "#/definitions/documents.BinaryAttachment" + } + }, + "bill_to_city": { + "type": "string" + }, + "bill_to_company_name": { + "type": "string" + }, + "bill_to_contact_person_name": { + "type": "string" + }, + "bill_to_country": { + "type": "string" + }, + "bill_to_local_tax_id": { + "type": "string" + }, + "bill_to_state": { + "type": "string" + }, + "bill_to_street_1": { + "type": "string" + }, + "bill_to_street_2": { + "type": "string" + }, + "bill_to_vat_number": { + "type": "string" + }, + "bill_to_zipcode": { + "type": "string" + }, + "comment": { + "type": "string" + }, + "credit_for_invoice_date": { + "type": "string" + }, + "credit_note_invoice_number": { + "type": "string" + }, + "currency": { + "description": "ISO currency code", + "type": "string" + }, + "date_created": { + "type": "string" + }, + "date_due": { + "type": "string" + }, + "date_paid": { + "type": "string" + }, + "date_updated": { + "type": "string" + }, + "delivery_number": { + "description": "number of the delivery note", + "type": "string" + }, + "gross_amount": { + "description": "invoice amount including tax", + "type": "string" + }, + "is_credit_note": { + "type": "boolean" + }, + "line_items": { + "type": "array", + "items": { + "$ref": "#/definitions/invoice.LineItem" + } + }, + "net_amount": { + "description": "invoice amount excluding tax", + "type": "string" + }, + "number": { + "description": "invoice number or reference number", + "type": "string" + }, + "payee": { + "description": "centrifuge ID of the payee", + "type": "string" + }, + "payment_details": { + "type": "array", + "items": { + "$ref": "#/definitions/documents.PaymentDetails" + } + }, + "recipient": { + "description": "centrifuge ID of the recipient", + "type": "string" + }, + "recipient_invoice_id": { + "type": "string" + }, + "remit_to_city": { + "type": "string" + }, + "remit_to_company_name": { + "type": "string" + }, + "remit_to_contact_person_name": { + "type": "string" + }, + "remit_to_country": { + "type": "string" + }, + "remit_to_local_tax_id": { + "type": "string" + }, + "remit_to_state": { + "type": "string" + }, + "remit_to_street_1": { + "type": "string" + }, + "remit_to_street_2": { + "type": "string" + }, + "remit_to_tax_country": { + "type": "string" + }, + "remit_to_vat_number": { + "type": "string" + }, + "remit_to_zipcode": { + "type": "string" + }, + "requester_email": { + "type": "string" + }, + "requester_name": { + "type": "string" + }, + "sender": { + "description": "centrifuge ID of the sender", + "type": "string" + }, + "sender_city": { + "type": "string" + }, + "sender_company_name": { + "type": "string" + }, + "sender_contact_person_name": { + "type": "string" + }, + "sender_country": { + "description": "country ISO code of the sender of this invoice", + "type": "string" + }, + "sender_invoice_id": { + "type": "string" + }, + "sender_state": { + "type": "string" + }, + "sender_street_1": { + "description": "street and address details of the sender company", + "type": "string" + }, + "sender_street_2": { + "type": "string" + }, + "sender_zipcode": { + "type": "string" + }, + "ship_to_city": { + "type": "string" + }, + "ship_to_company_name": { + "type": "string" + }, + "ship_to_contact_person_name": { + "type": "string" + }, + "ship_to_country": { + "type": "string" + }, + "ship_to_state": { + "type": "string" + }, + "ship_to_street_1": { + "type": "string" + }, + "ship_to_street_2": { + "type": "string" + }, + "ship_to_zipcode": { + "type": "string" + }, + "shipping_terms": { + "type": "string" + }, + "status": { + "description": "invoice status", + "type": "string" + }, + "tax_amount": { + "type": "string" + }, + "tax_items": { + "type": "array", + "items": { + "$ref": "#/definitions/invoice.TaxItem" + } + }, + "tax_on_line_level": { + "type": "boolean" + }, + "tax_rate": { + "type": "string" + } + } + }, + "invoice.LineItem": { + "type": "object", + "properties": { + "delivery_note_number": { + "type": "string" + }, + "description": { + "type": "string" + }, + "item_number": { + "type": "string" + }, + "net_weight": { + "type": "string" + }, + "price_per_unit": { + "type": "string" + }, + "purchase_order_item_number": { + "type": "string" + }, + "purchase_order_number": { + "type": "string" + }, + "quantity": { + "type": "string" + }, + "sender_part_no": { + "type": "string" + }, + "tax_amount": { + "type": "string" + }, + "tax_code": { + "type": "string" + }, + "tax_rate": { + "type": "string" + }, + "total_amount": { + "description": "the total amount of the line item", + "type": "string" + }, + "unit_of_measure": { + "type": "string" + } + } + }, + "invoice.TaxItem": { + "type": "object", + "properties": { + "invoice_item_number": { + "type": "string" + }, + "item_number": { + "type": "string" + }, + "tax_amount": { + "type": "string" + }, + "tax_base_amount": { + "type": "string" + }, + "tax_code": { + "type": "string" + }, + "tax_rate": { + "type": "string" + } + } + }, + "jobs.StatusResponse": { + "type": "object", + "properties": { + "job_id": { + "type": "string" + }, + "last_updated": { + "type": "string" + }, + "message": { + "type": "string" + }, + "status": { + "type": "string" + } + } + }, + "notification.Message": { + "type": "object", + "properties": { + "account_id": { + "description": "account_id is the account associated to webhook", + "type": "string" + }, + "document_id": { + "type": "string" + }, + "document_type": { + "type": "string" + }, + "event_type": { + "type": "integer" + }, + "from_id": { + "description": "from_id if provided, original trigger of the event", + "type": "string" + }, + "message": { + "type": "string" + }, + "recorded": { + "type": "string" + }, + "status": { + "type": "string" + }, + "to_id": { + "description": "to_id if provided, final destination of the event", + "type": "string" + } + } + }, + "transferdetails.Data": { + "type": "object", + "properties": { + "amount": { + "type": "string" + }, + "currency": { + "type": "string" + }, + "data": { + "type": "string" + }, + "recipient_id": { + "type": "string" + }, + "scheduled_date": { + "type": "string" + }, + "sender_id": { + "type": "string" + }, + "settlement_date": { + "type": "string" + }, + "settlement_reference": { + "type": "string" + }, + "status": { + "type": "string" + }, + "transfer_id": { + "type": "string" + }, + "transfer_type": { + "type": "string" + } + } + }, + "userapi.CreateEntityRequest": { + "type": "object", + "properties": { + "attributes": { + "type": "object", + "$ref": "#/definitions/coreapi.AttributeMapRequest" + }, + "data": { + "type": "object", + "$ref": "#/definitions/entity.Data" + }, + "read_access": { + "type": "array", + "items": { + "type": "string" + } + }, + "write_access": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "userapi.CreateInvoiceRequest": { + "type": "object", + "properties": { + "attributes": { + "type": "object", + "$ref": "#/definitions/coreapi.AttributeMapRequest" + }, + "data": { + "type": "object", + "$ref": "#/definitions/invoice.Data" + }, + "read_access": { + "type": "array", + "items": { + "type": "string" + } + }, + "write_access": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "userapi.CreateTransferDetailRequest": { + "type": "object", + "properties": { + "data": { + "type": "object", + "$ref": "#/definitions/transferdetails.Data" + }, + "document_id": { + "type": "string" + } + } + }, + "userapi.EntityDataResponse": { + "type": "object", + "properties": { + "entity": { + "type": "object", + "$ref": "#/definitions/entity.Data" + }, + "relationships": { + "type": "array", + "items": { + "$ref": "#/definitions/userapi.Relationship" + } + } + } + }, + "userapi.EntityResponse": { + "type": "object", + "properties": { + "attributes": { + "type": "object", + "$ref": "#/definitions/coreapi.AttributeMapResponse" + }, + "data": { + "type": "object", + "$ref": "#/definitions/userapi.EntityDataResponse" + }, + "header": { + "type": "object", + "$ref": "#/definitions/coreapi.ResponseHeader" + } + } + }, + "userapi.FundingDataResponse": { + "type": "object", + "properties": { + "funding": { + "type": "object", + "$ref": "#/definitions/funding.Data" + }, + "signatures": { + "type": "array", + "items": { + "$ref": "#/definitions/funding.Signature" + } + } + } + }, + "userapi.FundingListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/userapi.FundingDataResponse" + } + }, + "header": { + "type": "object", + "$ref": "#/definitions/coreapi.ResponseHeader" + } + } + }, + "userapi.FundingRequest": { + "type": "object", + "properties": { + "data": { + "type": "object", + "$ref": "#/definitions/funding.Data" + } + } + }, + "userapi.FundingResponse": { + "type": "object", + "properties": { + "data": { + "type": "object", + "$ref": "#/definitions/userapi.FundingDataResponse" + }, + "header": { + "type": "object", + "$ref": "#/definitions/coreapi.ResponseHeader" + } + } + }, + "userapi.InvoiceResponse": { + "type": "object", + "properties": { + "attributes": { + "type": "object", + "$ref": "#/definitions/coreapi.AttributeMapResponse" + }, + "data": { + "type": "object", + "$ref": "#/definitions/invoice.Data" + }, + "header": { + "type": "object", + "$ref": "#/definitions/coreapi.ResponseHeader" + } + } + }, + "userapi.MintNFTRequest": { + "type": "object", + "properties": { + "deposit_address": { + "type": "string" + }, + "document_id": { + "type": "string" + }, + "proof_fields": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "userapi.MintNFTResponse": { + "type": "object", + "properties": { + "deposit_address": { + "type": "string" + }, + "document_id": { + "type": "string" + }, + "header": { + "type": "object", + "$ref": "#/definitions/userapi.NFTResponseHeader" + }, + "registry_address": { + "type": "string" + }, + "token_id": { + "type": "string" + } + } + }, + "userapi.NFTMintInvoiceUnpaidRequest": { + "type": "object", + "properties": { + "deposit_address": { + "description": "Deposit address for NFT Token created", + "type": "string" + } + } + }, + "userapi.NFTMintResponse": { + "type": "object", + "properties": { + "header": { + "type": "object", + "$ref": "#/definitions/userapi.ResponseHeader" + } + } + }, + "userapi.NFTOwnerResponse": { + "type": "object", + "properties": { + "owner": { + "type": "string" + }, + "registry_address": { + "type": "string" + }, + "token_id": { + "type": "string" + } + } + }, + "userapi.NFTResponseHeader": { + "type": "object", + "properties": { + "job_id": { + "type": "string" + } + } + }, + "userapi.Relationship": { + "type": "object", + "properties": { + "active": { + "type": "boolean" + }, + "entity_identifier": { + "type": "string" + }, + "owner_identity": { + "type": "string" + }, + "target_identity": { + "type": "string" + } + } + }, + "userapi.ResponseHeader": { + "type": "object", + "properties": { + "job_id": { + "type": "string" + } + } + }, + "userapi.ShareEntityRequest": { + "type": "object", + "properties": { + "target_identity": { + "type": "string" + } + } + }, + "userapi.ShareEntityResponse": { + "type": "object", + "properties": { + "header": { + "type": "object", + "$ref": "#/definitions/coreapi.ResponseHeader" + }, + "relationship": { + "type": "object", + "$ref": "#/definitions/userapi.Relationship" + } + } + }, + "userapi.TransferDetailListResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/transferdetails.Data" + } + }, + "header": { + "type": "object", + "$ref": "#/definitions/coreapi.ResponseHeader" + } + } + }, + "userapi.TransferDetailResponse": { + "type": "object", + "properties": { + "data": { + "type": "object", + "$ref": "#/definitions/transferdetails.Data" + }, + "header": { + "type": "object", + "$ref": "#/definitions/coreapi.ResponseHeader" + } + } + }, + "userapi.TransferNFTRequest": { + "type": "object", + "properties": { + "to": { + "type": "string" + } + } + }, + "userapi.TransferNFTResponse": { + "type": "object", + "properties": { + "header": { + "type": "object", + "$ref": "#/definitions/userapi.NFTResponseHeader" + }, + "registry_address": { + "type": "string" + }, + "to": { + "type": "string" + }, + "token_id": { + "type": "string" + } + } + }, + "userapi.UpdateTransferDetailRequest": { + "type": "object", + "properties": { + "data": { + "type": "object", + "$ref": "#/definitions/transferdetails.Data" + }, + "document_id": { + "type": "string" + }, + "transfer_id": { + "type": "string" + } + } + }, + "v2.CreateDocumentRequest": { + "type": "object", + "properties": { + "attributes": { + "type": "object", + "$ref": "#/definitions/coreapi.AttributeMapRequest" + }, + "data": { + "type": "object" + }, + "document_id": { + "description": "if provided, creates the next version of the document.", + "type": "string" + }, + "read_access": { + "type": "array", + "items": { + "type": "string" + } + }, + "scheme": { + "type": "string", + "enum": [ + "generic", + "invoice", + "entity" + ] + }, + "write_access": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "v2.UpdateDocumentRequest": { + "type": "object", + "properties": { + "attributes": { + "type": "object", + "$ref": "#/definitions/coreapi.AttributeMapRequest" + }, + "data": { + "type": "object" + }, + "read_access": { + "type": "array", + "items": { + "type": "string" + } + }, + "scheme": { + "type": "string", + "enum": [ + "generic", + "invoice", + "entity" + ] + }, + "write_access": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } +} \ No newline at end of file diff --git a/httpapi/userapi/bootstrapper.go b/httpapi/userapi/bootstrapper.go new file mode 100644 index 000000000..57dd1e297 --- /dev/null +++ b/httpapi/userapi/bootstrapper.go @@ -0,0 +1,60 @@ +package userapi + +import ( + "github.com/centrifuge/go-centrifuge/config" + "github.com/centrifuge/go-centrifuge/documents/entity" + "github.com/centrifuge/go-centrifuge/documents/entityrelationship" + "github.com/centrifuge/go-centrifuge/errors" + "github.com/centrifuge/go-centrifuge/extensions/funding" + "github.com/centrifuge/go-centrifuge/extensions/transferdetails" + "github.com/centrifuge/go-centrifuge/httpapi/coreapi" +) + +// BootstrappedUserAPIService key maps to the Service implementation in Bootstrap context. +const BootstrappedUserAPIService = "UserAPI Service" + +// Bootstrapper implements bootstrap.Bootstrapper. +type Bootstrapper struct{} + +// Bootstrap adds transaction.Repository into context. +func (b Bootstrapper) Bootstrap(ctx map[string]interface{}) error { + coreAPISrv, ok := ctx[coreapi.BootstrappedCoreAPIService].(coreapi.Service) + if !ok { + return errors.New("failed to get %s", coreapi.BootstrappedCoreAPIService) + } + + tdSrv, ok := ctx[transferdetails.BootstrappedTransferDetailService].(transferdetails.Service) + if !ok { + return errors.New("failed to get %s", transferdetails.BootstrappedTransferDetailService) + } + + erSrv, ok := ctx[entityrelationship.BootstrappedEntityRelationshipService].(entityrelationship.Service) + if !ok { + return errors.New("failed to get %s", entityrelationship.BootstrappedEntityRelationshipService) + } + + eSrv, ok := ctx[entity.BootstrappedEntityService].(entity.Service) + if !ok { + return errors.New("failed to get %s", entity.BootstrappedEntityService) + } + + fundingSrv, ok := ctx[funding.BootstrappedFundingService].(funding.Service) + if !ok { + return errors.New("failed to get %s", funding.BootstrappedFundingService) + } + + configSrv, ok := ctx[config.BootstrappedConfigStorage].(config.Service) + if !ok { + return errors.New("failed to get %s", config.BootstrappedConfigStorage) + } + + ctx[BootstrappedUserAPIService] = Service{ + coreAPISrv: coreAPISrv, + transferDetailsService: tdSrv, + entityRelationshipSrv: erSrv, + entitySrv: eSrv, + fundingSrv: fundingSrv, + config: configSrv, + } + return nil +} diff --git a/httpapi/userapi/bootstrapper_test.go b/httpapi/userapi/bootstrapper_test.go new file mode 100644 index 000000000..40c947cc7 --- /dev/null +++ b/httpapi/userapi/bootstrapper_test.go @@ -0,0 +1,90 @@ +// +build unit + +package userapi + +import ( + "os" + "testing" + + "github.com/centrifuge/go-centrifuge/bootstrap" + "github.com/centrifuge/go-centrifuge/bootstrap/bootstrappers/testlogging" + "github.com/centrifuge/go-centrifuge/config" + "github.com/centrifuge/go-centrifuge/config/configstore" + "github.com/centrifuge/go-centrifuge/documents/entity" + "github.com/centrifuge/go-centrifuge/documents/entityrelationship" + "github.com/centrifuge/go-centrifuge/extensions/funding" + "github.com/centrifuge/go-centrifuge/extensions/transferdetails" + "github.com/centrifuge/go-centrifuge/httpapi/coreapi" + "github.com/centrifuge/go-centrifuge/jobs/jobsv1" + "github.com/centrifuge/go-centrifuge/storage/leveldb" + testingidentity "github.com/centrifuge/go-centrifuge/testingutils/identity" + "github.com/stretchr/testify/assert" +) + +var ctx = map[string]interface{}{} +var cfg config.Configuration +var did = testingidentity.GenerateRandomDID() + +func TestMain(m *testing.M) { + ibootstappers := []bootstrap.TestBootstrapper{ + &testlogging.TestLoggingBootstrapper{}, + &config.Bootstrapper{}, + &leveldb.Bootstrapper{}, + jobsv1.Bootstrapper{}, + } + bootstrap.RunTestBootstrappers(ibootstappers, ctx) + cfg = ctx[bootstrap.BootstrappedConfig].(config.Configuration) + cfg.Set("keys.p2p.publicKey", "../../build/resources/p2pKey.pub.pem") + cfg.Set("keys.p2p.privateKey", "../../build/resources/p2pKey.key.pem") + cfg.Set("keys.signing.publicKey", "../../build/resources/signingKey.pub.pem") + cfg.Set("keys.signing.privateKey", "../../build/resources/signingKey.key.pem") + cfg.Set("networks.testing.contractAddresses.invoiceUnpaid", "0xf72855759a39fb75fc7341139f5d7a3974d4da08") + cfg.Set("identityId", did.String()) + result := m.Run() + bootstrap.RunTestTeardown(ibootstappers) + os.Exit(result) +} + +func TestBootstrapper_Bootstrap(t *testing.T) { + ctx := make(map[string]interface{}) + b := Bootstrapper{} + + // missing core-api service + err := b.Bootstrap(ctx) + assert.Error(t, err) + assert.Contains(t, err.Error(), coreapi.BootstrappedCoreAPIService) + + // missing transfer detail service + ctx[coreapi.BootstrappedCoreAPIService] = coreapi.Service{} + err = b.Bootstrap(ctx) + assert.Error(t, err) + assert.Contains(t, err.Error(), transferdetails.BootstrappedTransferDetailService) + + // missing entityrelationship service + ctx[transferdetails.BootstrappedTransferDetailService] = new(MockTransferService) + err = b.Bootstrap(ctx) + assert.Error(t, err) + assert.Contains(t, err.Error(), entityrelationship.BootstrappedEntityRelationshipService) + + // missing entity service + ctx[entityrelationship.BootstrappedEntityRelationshipService] = new(entity.MockEntityRelationService) + err = b.Bootstrap(ctx) + assert.Error(t, err) + assert.Contains(t, err.Error(), entity.BootstrappedEntityService) + + // missing funding service + ctx[entity.BootstrappedEntityService] = new(entity.MockService) + err = b.Bootstrap(ctx) + assert.Error(t, err) + assert.Contains(t, err.Error(), funding.BootstrappedFundingService) + + // missing config service + ctx[funding.BootstrappedFundingService] = new(funding.MockService) + err = b.Bootstrap(ctx) + assert.Error(t, err) + assert.Contains(t, err.Error(), config.BootstrappedConfigStorage) + + // success + ctx[config.BootstrappedConfigStorage] = new(configstore.MockService) + assert.NoError(t, b.Bootstrap(ctx)) +} diff --git a/httpapi/userapi/entity_api.go b/httpapi/userapi/entity_api.go new file mode 100644 index 000000000..dd18d2fa6 --- /dev/null +++ b/httpapi/userapi/entity_api.go @@ -0,0 +1,348 @@ +package userapi + +import ( + "encoding/json" + "io/ioutil" + "net/http" + + "github.com/centrifuge/go-centrifuge/httpapi/coreapi" + "github.com/centrifuge/go-centrifuge/jobs" + "github.com/centrifuge/go-centrifuge/utils/httputils" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/go-chi/chi" + "github.com/go-chi/render" +) + +// CreateEntity creates a new Entity. +// @summary Creates a new Entity and anchors it. +// @description Creates a new Entity and anchors it. +// @id create_entity +// @tags Entities +// @accept json +// @param authorization header string true "Hex encoded centrifuge ID of the account for the intended API action" +// @param body body userapi.CreateEntityRequest true "Entity Create request" +// @produce json +// @Failure 500 {object} httputils.HTTPError +// @Failure 400 {object} httputils.HTTPError +// @Failure 403 {object} httputils.HTTPError +// @success 202 {object} userapi.EntityResponse +// @router /v1/entities [post] +func (h handler) CreateEntity(w http.ResponseWriter, r *http.Request) { + var err error + var code int + defer httputils.RespondIfError(&code, &err, w, r) + + ctx := r.Context() + data, err := ioutil.ReadAll(r.Body) + if err != nil { + code = http.StatusInternalServerError + log.Error(err) + return + } + + var request CreateEntityRequest + err = json.Unmarshal(data, &request) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + return + } + + model, jobID, err := h.srv.CreateEntity(ctx, request) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + return + } + + resp, err := toEntityResponse(ctx, h.srv.entityRelationshipSrv, model, h.tokenRegistry, jobID) + if err != nil { + code = http.StatusInternalServerError + log.Error(err) + return + } + + render.Status(r, http.StatusAccepted) + render.JSON(w, r, resp) +} + +// UpdateEntity updates an existing entity and anchors it. +// @summary Updates an existing Entity and anchors it. +// @description Updates an existing Entity and anchors it. +// @id update_entity +// @tags Entities +// @accept json +// @param authorization header string true "Hex encoded centrifuge ID of the account for the intended API action" +// @param document_id path string true "Document Identifier" +// @param body body userapi.CreateEntityRequest true "Entity Create request" +// @produce json +// @Failure 500 {object} httputils.HTTPError +// @Failure 400 {object} httputils.HTTPError +// @Failure 403 {object} httputils.HTTPError +// @success 202 {object} userapi.EntityResponse +// @router /v1/entities/{document_id} [put] +func (h handler) UpdateEntity(w http.ResponseWriter, r *http.Request) { + var err error + var code int + defer httputils.RespondIfError(&code, &err, w, r) + + docID, err := hexutil.Decode(chi.URLParam(r, coreapi.DocumentIDParam)) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + err = coreapi.ErrInvalidDocumentID + return + } + + ctx := r.Context() + data, err := ioutil.ReadAll(r.Body) + if err != nil { + code = http.StatusInternalServerError + log.Error(err) + return + } + + var request CreateEntityRequest + err = json.Unmarshal(data, &request) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + return + } + + model, jobID, err := h.srv.UpdateEntity(ctx, docID, request) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + return + } + + resp, err := toEntityResponse(ctx, h.srv.entityRelationshipSrv, model, h.tokenRegistry, jobID) + if err != nil { + code = http.StatusInternalServerError + log.Error(err) + return + } + + render.Status(r, http.StatusAccepted) + render.JSON(w, r, resp) +} + +// GetEntity returns the latest version of the Entity. +// @summary Returns the latest version of the Entity. +// @description Returns the latest version of the Entity. +// @id get_entity +// @tags Entities +// @param authorization header string true "Hex encoded centrifuge ID of the account for the intended API action" +// @param document_id path string true "Document Identifier" +// @produce json +// @Failure 403 {object} httputils.HTTPError +// @Failure 400 {object} httputils.HTTPError +// @Failure 404 {object} httputils.HTTPError +// @Failure 500 {object} httputils.HTTPError +// @success 200 {object} userapi.EntityResponse +// @router /v1/entities/{document_id} [get] +func (h handler) GetEntity(w http.ResponseWriter, r *http.Request) { + var err error + var code int + defer httputils.RespondIfError(&code, &err, w, r) + + docID, err := hexutil.Decode(chi.URLParam(r, coreapi.DocumentIDParam)) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + err = coreapi.ErrInvalidDocumentID + return + } + + ctx := r.Context() + model, err := h.srv.GetEntity(ctx, docID) + if err != nil { + code = http.StatusNotFound + log.Error(err) + err = coreapi.ErrDocumentNotFound + return + } + + resp, err := toEntityResponse(ctx, h.srv.entityRelationshipSrv, model, h.tokenRegistry, jobs.NilJobID()) + if err != nil { + code = http.StatusInternalServerError + log.Error(err) + return + } + + render.Status(r, http.StatusOK) + render.JSON(w, r, resp) +} + +// ShareEntity gives entity access to target identity. +// @summary Share gives entity access to target identity. +// @description Share gives entity access to target identity. +// @id share_entity +// @tags Entities +// @accept json +// @param authorization header string true "Hex encoded centrifuge ID of the account for the intended API action" +// @param document_id path string true "Document Identifier" +// @param body body userapi.ShareEntityRequest true "Entity Share request" +// @produce json +// @Failure 500 {object} httputils.HTTPError +// @Failure 400 {object} httputils.HTTPError +// @Failure 403 {object} httputils.HTTPError +// @success 202 {object} userapi.ShareEntityResponse +// @router /v1/entities/{document_id}/share [post] +func (h handler) ShareEntity(w http.ResponseWriter, r *http.Request) { + var err error + var code int + defer httputils.RespondIfError(&code, &err, w, r) + + docID, err := hexutil.Decode(chi.URLParam(r, coreapi.DocumentIDParam)) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + err = coreapi.ErrInvalidDocumentID + return + } + + ctx := r.Context() + data, err := ioutil.ReadAll(r.Body) + if err != nil { + code = http.StatusInternalServerError + log.Error(err) + return + } + + var request ShareEntityRequest + err = json.Unmarshal(data, &request) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + return + } + + model, jobID, err := h.srv.ShareEntity(ctx, docID, request) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + return + } + + resp, err := toEntityShareResponse(model, h.tokenRegistry, jobID) + if err != nil { + code = http.StatusInternalServerError + log.Error(err) + return + } + + render.Status(r, http.StatusAccepted) + render.JSON(w, r, resp) +} + +// RevokeEntity revokes target id's access to entity. +// @summary Revoke revokes target id's access to entity. +// @description Revoke revokes target id's access to entity. +// @id revoke_entity +// @tags Entities +// @accept json +// @param authorization header string true "Hex encoded centrifuge ID of the account for the intended API action" +// @param document_id path string true "Document Identifier" +// @param body body userapi.ShareEntityRequest true "Entity Revoke request" +// @produce json +// @Failure 500 {object} httputils.HTTPError +// @Failure 400 {object} httputils.HTTPError +// @Failure 403 {object} httputils.HTTPError +// @success 202 {object} userapi.ShareEntityResponse +// @router /v1/entities/{document_id}/revoke [post] +func (h handler) RevokeEntity(w http.ResponseWriter, r *http.Request) { + var err error + var code int + defer httputils.RespondIfError(&code, &err, w, r) + + docID, err := hexutil.Decode(chi.URLParam(r, coreapi.DocumentIDParam)) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + err = coreapi.ErrInvalidDocumentID + return + } + + ctx := r.Context() + data, err := ioutil.ReadAll(r.Body) + if err != nil { + code = http.StatusInternalServerError + log.Error(err) + return + } + + var request ShareEntityRequest + err = json.Unmarshal(data, &request) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + return + } + + model, jobID, err := h.srv.RevokeRelationship(ctx, docID, request) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + return + } + + resp, err := toEntityShareResponse(model, h.tokenRegistry, jobID) + if err != nil { + code = http.StatusInternalServerError + log.Error(err) + return + } + + resp.Relationship.Active = false + render.Status(r, http.StatusAccepted) + render.JSON(w, r, resp) +} + +// GetEntityThroughRelationship returns the latest version of the Entity through relationship ID. +// @summary Returns the latest version of the Entity through relationship ID. +// @description Returns the latest version of the Entity through relationship ID. +// @id get_entity_through_relationship_id +// @tags Entities +// @param authorization header string true "Hex encoded centrifuge ID of the account for the intended API action" +// @param document_id path string true "Entity Relationship Document Identifier" +// @produce json +// @Failure 403 {object} httputils.HTTPError +// @Failure 400 {object} httputils.HTTPError +// @Failure 404 {object} httputils.HTTPError +// @Failure 500 {object} httputils.HTTPError +// @success 200 {object} userapi.EntityResponse +// @router /v1/relationships/{document_id}/entity [get] +func (h handler) GetEntityThroughRelationship(w http.ResponseWriter, r *http.Request) { + var err error + var code int + defer httputils.RespondIfError(&code, &err, w, r) + + docID, err := hexutil.Decode(chi.URLParam(r, coreapi.DocumentIDParam)) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + err = coreapi.ErrInvalidDocumentID + return + } + + ctx := r.Context() + model, err := h.srv.GetEntityByRelationship(ctx, docID) + if err != nil { + code = http.StatusNotFound + log.Error(err) + err = coreapi.ErrDocumentNotFound + return + } + + resp, err := toEntityResponse(ctx, h.srv.entityRelationshipSrv, model, h.tokenRegistry, jobs.NilJobID()) + if err != nil { + code = http.StatusInternalServerError + log.Error(err) + return + } + + render.Status(r, http.StatusOK) + render.JSON(w, r, resp) +} diff --git a/httpapi/userapi/entity_api_test.go b/httpapi/userapi/entity_api_test.go new file mode 100644 index 000000000..05fe121af --- /dev/null +++ b/httpapi/userapi/entity_api_test.go @@ -0,0 +1,513 @@ +// +build unit + +package userapi + +import ( + "bytes" + "context" + "encoding/json" + "io" + "net/http" + "net/http/httptest" + "testing" + + coredocumentpb "github.com/centrifuge/centrifuge-protobufs/gen/go/coredocument" + "github.com/centrifuge/go-centrifuge/config" + "github.com/centrifuge/go-centrifuge/documents" + "github.com/centrifuge/go-centrifuge/documents/entity" + "github.com/centrifuge/go-centrifuge/documents/entityrelationship" + "github.com/centrifuge/go-centrifuge/errors" + "github.com/centrifuge/go-centrifuge/httpapi/coreapi" + "github.com/centrifuge/go-centrifuge/jobs" + testingdocuments "github.com/centrifuge/go-centrifuge/testingutils/documents" + testingidentity "github.com/centrifuge/go-centrifuge/testingutils/identity" + "github.com/centrifuge/go-centrifuge/utils" + "github.com/centrifuge/go-centrifuge/utils/byteutils" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/go-chi/chi" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func entityData() map[string]interface{} { + return map[string]interface{}{ + "identity": "0xBAEb33a61f05e6F269f1c4b4CFF91A901B54DaF7", + "legal_name": "John doe", + "addresses": []map[string]interface{}{ + { + "is_main": true, + "country": "germany", + }, + }, + } +} + +func TestHandler_CreateEntity(t *testing.T) { + data := map[string]interface{}{ + "data": entityData(), + "attributes": map[string]map[string]string{ + "string_test": { + "type": "invalid", + "value": "hello, world", + }, + }, + } + + getHTTPReqAndResp := func(ctx context.Context, b io.Reader) (*httptest.ResponseRecorder, *http.Request) { + return httptest.NewRecorder(), httptest.NewRequest("POST", "/entities", b).WithContext(ctx) + } + + // empty body + rctx := chi.NewRouteContext() + ctx := context.WithValue(context.Background(), chi.RouteCtxKey, rctx) + h := handler{} + w, r := getHTTPReqAndResp(ctx, nil) + h.CreateEntity(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), "unexpected end of JSON input") + + // invalid body + w, r = getHTTPReqAndResp(ctx, bytes.NewReader(marshall(t, data))) + h.CreateEntity(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), documents.ErrNotValidAttrType.Error()) + + // failed response conversion + data["attributes"] = map[string]map[string]string{ + "string_test": { + "type": "string", + "value": "hello, world", + }, + } + m := new(testingdocuments.MockModel) + m.On("GetData").Return(entity.Data{}) + m.On("Scheme").Return(entity.Scheme) + m.On("GetAttributes").Return(nil) + m.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, errors.New("failed to get collaborators")) + docSrv := new(testingdocuments.MockService) + srv := Service{coreAPISrv: newCoreAPIService(docSrv)} + h = handler{srv: srv} + docSrv.On("CreateModel", mock.Anything, mock.Anything).Return(m, jobs.NewJobID(), nil) + w, r = getHTTPReqAndResp(ctx, bytes.NewReader(marshall(t, data))) + h.CreateEntity(w, r) + assert.Equal(t, w.Code, http.StatusInternalServerError) + assert.Contains(t, w.Body.String(), "failed to get collaborators") + m.AssertExpectations(t) + docSrv.AssertExpectations(t) + + // success + m = new(testingdocuments.MockModel) + m.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, nil).Once() + m.On("GetData").Return(entity.Data{}) + m.On("Scheme").Return(entity.Scheme) + m.On("ID").Return(utils.RandomSlice(32)).Once() + m.On("CurrentVersion").Return(utils.RandomSlice(32)).Once() + m.On("Author").Return(nil, errors.New("somerror")) + m.On("Timestamp").Return(nil, errors.New("somerror")) + m.On("NFTs").Return(nil) + m.On("GetAttributes").Return(nil) + collab := testingidentity.GenerateRandomDID() + m.On("IsDIDCollaborator", collab).Return(false, nil).Once() + ctx = context.WithValue(ctx, config.AccountHeaderKey, collab.String()) + docSrv = new(testingdocuments.MockService) + srv = Service{coreAPISrv: newCoreAPIService(docSrv)} + h = handler{srv: srv} + docSrv.On("CreateModel", mock.Anything, mock.Anything).Return(m, jobs.NewJobID(), nil) + w, r = getHTTPReqAndResp(ctx, bytes.NewReader(marshall(t, data))) + h.CreateEntity(w, r) + assert.Equal(t, w.Code, http.StatusAccepted) + m.AssertExpectations(t) + docSrv.AssertExpectations(t) +} + +func TestHandler_UpdateEntity(t *testing.T) { + getHTTPReqAndResp := func(ctx context.Context, b io.Reader) (*httptest.ResponseRecorder, *http.Request) { + return httptest.NewRecorder(), httptest.NewRequest("PUT", "/entities/{document_id}", b).WithContext(ctx) + } + // empty document_id and invalid id + rctx := chi.NewRouteContext() + rctx.URLParams.Keys = make([]string, 1, 1) + rctx.URLParams.Values = make([]string, 1, 1) + rctx.URLParams.Keys[0] = "document_id" + ctx := context.WithValue(context.Background(), chi.RouteCtxKey, rctx) + h := handler{} + for _, id := range []string{"", "invalid"} { + rctx.URLParams.Values[0] = id + w, r := getHTTPReqAndResp(ctx, nil) + h.UpdateEntity(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), coreapi.ErrInvalidDocumentID.Error()) + } + + // empty body + id := hexutil.Encode(utils.RandomSlice(32)) + rctx.URLParams.Values[0] = id + w, r := getHTTPReqAndResp(ctx, nil) + h.UpdateEntity(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), "unexpected end of JSON input") + + // failed conversion + data := map[string]interface{}{ + "data": entityData(), + "attributes": map[string]map[string]string{ + "string_test": { + "type": "invalid", + "value": "hello, world", + }, + }, + } + + d, err := json.Marshal(data) + assert.NoError(t, err) + w, r = getHTTPReqAndResp(ctx, bytes.NewReader(d)) + h.UpdateEntity(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), documents.ErrNotValidAttrType.Error()) + + // failed to update + data["attributes"] = map[string]map[string]string{ + "string_test": { + "type": "string", + "value": "hello, world", + }, + } + d, err = json.Marshal(data) + assert.NoError(t, err) + docSrv := new(testingdocuments.MockService) + srv := Service{coreAPISrv: newCoreAPIService(docSrv)} + h = handler{srv: srv} + docSrv.On("UpdateModel", mock.Anything, mock.Anything).Return(nil, jobs.NilJobID(), errors.New("failed to update model")) + w, r = getHTTPReqAndResp(ctx, bytes.NewReader(d)) + h.UpdateEntity(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), "failed to update model") + docSrv.AssertExpectations(t) + + // failed response conversion + m := new(testingdocuments.MockModel) + m.On("GetData").Return(entity.Data{}) + m.On("Scheme").Return(entity.Scheme) + m.On("GetAttributes").Return(nil) + m.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, errors.New("failed to get collaborators")) + docSrv = new(testingdocuments.MockService) + srv = Service{coreAPISrv: newCoreAPIService(docSrv)} + h = handler{srv: srv} + docSrv.On("UpdateModel", mock.Anything, mock.Anything).Return(m, jobs.NewJobID(), nil) + w, r = getHTTPReqAndResp(ctx, bytes.NewReader(d)) + h.UpdateEntity(w, r) + assert.Equal(t, w.Code, http.StatusInternalServerError) + assert.Contains(t, w.Body.String(), "failed to get collaborators") + docSrv.AssertExpectations(t) + m.AssertExpectations(t) + + // success + m = new(testingdocuments.MockModel) + m.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, nil).Once() + m.On("GetData").Return(entity.Data{}) + m.On("Scheme").Return(entity.Scheme) + m.On("ID").Return(utils.RandomSlice(32)).Once() + m.On("CurrentVersion").Return(utils.RandomSlice(32)).Once() + m.On("Author").Return(nil, errors.New("somerror")) + m.On("Timestamp").Return(nil, errors.New("somerror")) + m.On("NFTs").Return(nil) + m.On("GetAttributes").Return(nil) + collab := testingidentity.GenerateRandomDID() + m.On("IsDIDCollaborator", collab).Return(false, nil).Once() + ctx = context.WithValue(ctx, config.AccountHeaderKey, collab.String()) + docSrv = new(testingdocuments.MockService) + srv = Service{coreAPISrv: newCoreAPIService(docSrv)} + h = handler{srv: srv} + docSrv.On("UpdateModel", mock.Anything, mock.Anything).Return(m, jobs.NewJobID(), nil) + w, r = getHTTPReqAndResp(ctx, bytes.NewReader(d)) + h.UpdateEntity(w, r) + assert.Equal(t, w.Code, http.StatusAccepted) + m.AssertExpectations(t) + docSrv.AssertExpectations(t) +} + +func TestHandler_GetEntity(t *testing.T) { + getHTTPReqAndResp := func(ctx context.Context) (*httptest.ResponseRecorder, *http.Request) { + return httptest.NewRecorder(), httptest.NewRequest("GET", "/entities/{document_id}", nil).WithContext(ctx) + } + + rctx := chi.NewRouteContext() + rctx.URLParams.Keys = make([]string, 1, 1) + rctx.URLParams.Values = make([]string, 1, 1) + rctx.URLParams.Keys[0] = "document_id" + ctx := context.WithValue(context.Background(), chi.RouteCtxKey, rctx) + h := handler{} + + // empty document_id and invalid + for _, id := range []string{"", "invalid"} { + rctx.URLParams.Values[0] = id + w, r := getHTTPReqAndResp(ctx) + h.GetEntity(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), coreapi.ErrInvalidDocumentID.Error()) + } + + // missing document + id := utils.RandomSlice(32) + rctx.URLParams.Values[0] = hexutil.Encode(id) + docSrv := new(testingdocuments.MockService) + docSrv.On("GetCurrentVersion", id).Return(nil, errors.New("failed")) + h = handler{srv: Service{coreAPISrv: newCoreAPIService(docSrv)}} + w, r := getHTTPReqAndResp(ctx) + h.GetEntity(w, r) + assert.Equal(t, w.Code, http.StatusNotFound) + assert.Contains(t, w.Body.String(), coreapi.ErrDocumentNotFound.Error()) + docSrv.AssertExpectations(t) + + // failed doc response + m := new(testingdocuments.MockModel) + m.On("GetData").Return(entity.Data{}) + m.On("Scheme").Return(entity.Scheme) + m.On("GetAttributes").Return(nil) + m.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, errors.New("failed to get collaborators")) + docSrv = new(testingdocuments.MockService) + docSrv.On("GetCurrentVersion", id).Return(m, nil) + h = handler{srv: Service{coreAPISrv: newCoreAPIService(docSrv)}} + w, r = getHTTPReqAndResp(ctx) + h.GetEntity(w, r) + assert.Equal(t, w.Code, http.StatusInternalServerError) + assert.Contains(t, w.Body.String(), "failed to get collaborators") + docSrv.AssertExpectations(t) + m.AssertExpectations(t) + + // success + m = new(testingdocuments.MockModel) + m.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, nil).Once() + m.On("GetData").Return(entity.Data{}) + m.On("Scheme").Return(entity.Scheme) + m.On("ID").Return(utils.RandomSlice(32)).Once() + m.On("CurrentVersion").Return(utils.RandomSlice(32)).Once() + m.On("Author").Return(nil, errors.New("somerror")) + m.On("Timestamp").Return(nil, errors.New("somerror")) + m.On("NFTs").Return(nil) + m.On("GetAttributes").Return(nil) + collab := testingidentity.GenerateRandomDID() + m.On("IsDIDCollaborator", collab).Return(false, nil).Once() + ctx = context.WithValue(ctx, config.AccountHeaderKey, collab.String()) + docSrv = new(testingdocuments.MockService) + docSrv.On("GetCurrentVersion", id).Return(m, nil) + h = handler{srv: Service{coreAPISrv: newCoreAPIService(docSrv)}} + w, r = getHTTPReqAndResp(ctx) + h.GetEntity(w, r) + assert.Equal(t, w.Code, http.StatusOK) + docSrv.AssertExpectations(t) + m.AssertExpectations(t) +} + +func TestHandler_ShareEntity(t *testing.T) { + getHTTPReqAndResp := func(ctx context.Context, b io.Reader) (*httptest.ResponseRecorder, *http.Request) { + return httptest.NewRecorder(), httptest.NewRequest("GET", "/entities/{document_id}/share", b).WithContext(ctx) + } + + rctx := chi.NewRouteContext() + rctx.URLParams.Keys = make([]string, 1, 1) + rctx.URLParams.Values = make([]string, 1, 1) + rctx.URLParams.Keys[0] = "document_id" + ctx := context.WithValue(context.Background(), chi.RouteCtxKey, rctx) + h := handler{} + + // empty document_id and invalid + for _, id := range []string{"", "invalid"} { + rctx.URLParams.Values[0] = id + w, r := getHTTPReqAndResp(ctx, nil) + h.ShareEntity(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), coreapi.ErrInvalidDocumentID.Error()) + } + + // empty body + id := hexutil.Encode(utils.RandomSlice(32)) + rctx.URLParams.Values[0] = id + w, r := getHTTPReqAndResp(ctx, nil) + h.ShareEntity(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), "unexpected end of JSON input") + + // failed creation + docSrv := new(testingdocuments.MockService) + m := new(testingdocuments.MockModel) + h.srv.coreAPISrv = newCoreAPIService(docSrv) + did := testingidentity.GenerateRandomDID() + did1 := testingidentity.GenerateRandomDID() + ctx = context.WithValue(ctx, config.AccountHeaderKey, did.String()) + docSrv.On("CreateModel", ctx, mock.Anything).Return(m, jobs.NewJobID(), errors.New("failed create")).Once() + req := ShareEntityRequest{TargetIdentity: did1} + d, err := json.Marshal(req) + assert.NoError(t, err) + w, r = getHTTPReqAndResp(ctx, bytes.NewReader(d)) + h.ShareEntity(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), "failed create") + + // failed convert + docSrv.On("CreateModel", ctx, mock.Anything).Return(m, jobs.NewJobID(), nil).Once() + m.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, errors.New("failed to get collaborators")).Once() + w, r = getHTTPReqAndResp(ctx, bytes.NewReader(d)) + h.ShareEntity(w, r) + assert.Equal(t, w.Code, http.StatusInternalServerError) + assert.Contains(t, w.Body.String(), "failed to get collaborators") + + // success + id1 := byteutils.HexBytes(utils.RandomSlice(32)) + did2 := testingidentity.GenerateRandomDID() + er := &entityrelationship.EntityRelationship{ + CoreDocument: &documents.CoreDocument{ + Document: coredocumentpb.CoreDocument{}, + }, + + Data: entityrelationship.Data{ + TargetIdentity: &did1, + OwnerIdentity: &did2, + EntityIdentifier: id1, + }, + } + docSrv.On("CreateModel", ctx, mock.Anything).Return(er, jobs.NewJobID(), nil).Once() + w, r = getHTTPReqAndResp(ctx, bytes.NewReader(d)) + h.ShareEntity(w, r) + assert.Equal(t, w.Code, http.StatusAccepted) + m.AssertExpectations(t) + docSrv.AssertExpectations(t) +} + +func TestHandler_RevokeEntity(t *testing.T) { + getHTTPReqAndResp := func(ctx context.Context, b io.Reader) (*httptest.ResponseRecorder, *http.Request) { + return httptest.NewRecorder(), httptest.NewRequest("GET", "/entities/{document_id}/revoke", b).WithContext(ctx) + } + + rctx := chi.NewRouteContext() + rctx.URLParams.Keys = make([]string, 1, 1) + rctx.URLParams.Values = make([]string, 1, 1) + rctx.URLParams.Keys[0] = "document_id" + ctx := context.WithValue(context.Background(), chi.RouteCtxKey, rctx) + h := handler{} + + // empty document_id and invalid + for _, id := range []string{"", "invalid"} { + rctx.URLParams.Values[0] = id + w, r := getHTTPReqAndResp(ctx, nil) + h.RevokeEntity(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), coreapi.ErrInvalidDocumentID.Error()) + } + + // empty body + id := hexutil.Encode(utils.RandomSlice(32)) + rctx.URLParams.Values[0] = id + w, r := getHTTPReqAndResp(ctx, nil) + h.RevokeEntity(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), "unexpected end of JSON input") + + // failed creation + docSrv := new(testingdocuments.MockService) + m := new(testingdocuments.MockModel) + h.srv.coreAPISrv = newCoreAPIService(docSrv) + did := testingidentity.GenerateRandomDID() + did1 := testingidentity.GenerateRandomDID() + ctx = context.WithValue(ctx, config.AccountHeaderKey, did.String()) + docSrv.On("UpdateModel", ctx, mock.Anything).Return(m, jobs.NewJobID(), errors.New("failed update")).Once() + req := ShareEntityRequest{TargetIdentity: did1} + d, err := json.Marshal(req) + assert.NoError(t, err) + w, r = getHTTPReqAndResp(ctx, bytes.NewReader(d)) + h.RevokeEntity(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), "failed update") + + // failed convert + docSrv.On("UpdateModel", ctx, mock.Anything).Return(m, jobs.NewJobID(), nil).Once() + m.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, errors.New("failed to get collaborators")).Once() + w, r = getHTTPReqAndResp(ctx, bytes.NewReader(d)) + h.RevokeEntity(w, r) + assert.Equal(t, w.Code, http.StatusInternalServerError) + assert.Contains(t, w.Body.String(), "failed to get collaborators") + + // success + id1 := byteutils.HexBytes(utils.RandomSlice(32)) + did2 := testingidentity.GenerateRandomDID() + er := &entityrelationship.EntityRelationship{ + CoreDocument: &documents.CoreDocument{ + Document: coredocumentpb.CoreDocument{}, + }, + + Data: entityrelationship.Data{ + TargetIdentity: &did1, + OwnerIdentity: &did2, + EntityIdentifier: id1, + }, + } + docSrv.On("UpdateModel", ctx, mock.Anything).Return(er, jobs.NewJobID(), nil).Once() + w, r = getHTTPReqAndResp(ctx, bytes.NewReader(d)) + h.RevokeEntity(w, r) + assert.Equal(t, w.Code, http.StatusAccepted) + assert.Contains(t, w.Body.String(), "\"active\":false") + m.AssertExpectations(t) + docSrv.AssertExpectations(t) +} + +func TestHandler_GetEntityThroughRelationship(t *testing.T) { + getHTTPReqAndResp := func(ctx context.Context) (*httptest.ResponseRecorder, *http.Request) { + return httptest.NewRecorder(), httptest.NewRequest("GET", "/relationships/{document_id}/entity", nil).WithContext(ctx) + } + + rctx := chi.NewRouteContext() + rctx.URLParams.Keys = make([]string, 1, 1) + rctx.URLParams.Values = make([]string, 1, 1) + rctx.URLParams.Keys[0] = "document_id" + ctx := context.WithValue(context.Background(), chi.RouteCtxKey, rctx) + h := handler{} + + // empty document_id and invalid + for _, id := range []string{"", "invalid"} { + rctx.URLParams.Values[0] = id + w, r := getHTTPReqAndResp(ctx) + h.GetEntityThroughRelationship(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), coreapi.ErrInvalidDocumentID.Error()) + } + + // missing document + id := utils.RandomSlice(32) + rctx.URLParams.Values[0] = hexutil.Encode(id) + eSrv := new(entity.MockService) + eSrv.On("GetEntityByRelationship", mock.Anything, id).Return(nil, errors.New("failed")).Once() + h = handler{srv: Service{entitySrv: eSrv}} + w, r := getHTTPReqAndResp(ctx) + h.GetEntityThroughRelationship(w, r) + assert.Equal(t, w.Code, http.StatusNotFound) + assert.Contains(t, w.Body.String(), coreapi.ErrDocumentNotFound.Error()) + + // failed doc response + m := new(testingdocuments.MockModel) + m.On("GetData").Return(entity.Data{}) + m.On("Scheme").Return(entity.Scheme) + m.On("GetAttributes").Return(nil) + m.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, errors.New("failed to get collaborators")).Once() + eSrv.On("GetEntityByRelationship", mock.Anything, id).Return(m, nil) + w, r = getHTTPReqAndResp(ctx) + h.GetEntityThroughRelationship(w, r) + assert.Equal(t, w.Code, http.StatusInternalServerError) + assert.Contains(t, w.Body.String(), "failed to get collaborators") + + // success + m.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, nil).Once() + m.On("ID").Return(utils.RandomSlice(32)).Once() + m.On("CurrentVersion").Return(utils.RandomSlice(32)).Once() + m.On("Author").Return(nil, errors.New("somerror")) + m.On("Timestamp").Return(nil, errors.New("somerror")) + m.On("NFTs").Return(nil) + m.On("GetAttributes").Return(nil) + collab := testingidentity.GenerateRandomDID() + m.On("IsDIDCollaborator", collab).Return(false, nil).Once() + ctx = context.WithValue(ctx, config.AccountHeaderKey, collab.String()) + w, r = getHTTPReqAndResp(ctx) + h.GetEntityThroughRelationship(w, r) + assert.Equal(t, w.Code, http.StatusOK) + eSrv.AssertExpectations(t) + m.AssertExpectations(t) +} diff --git a/httpapi/userapi/funding_api.go b/httpapi/userapi/funding_api.go new file mode 100644 index 000000000..c99ba76bc --- /dev/null +++ b/httpapi/userapi/funding_api.go @@ -0,0 +1,425 @@ +package userapi + +import ( + "encoding/json" + "io/ioutil" + "net/http" + + "github.com/centrifuge/go-centrifuge/errors" + "github.com/centrifuge/go-centrifuge/httpapi/coreapi" + "github.com/centrifuge/go-centrifuge/jobs" + "github.com/centrifuge/go-centrifuge/utils/httputils" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/go-chi/chi" + "github.com/go-chi/render" +) + +// ErrInvalidAgreementID is a sentinel error when the agreement ID is invalid. +const ErrInvalidAgreementID = errors.Error("Invalid funding agreement ID") + +// CreateFundingAgreement creates a new funding agreement on the document associated with document_id. +// @summary Creates a new funding agreement on the document. +// @description Creates a new funding agreement on the document. +// @id create_funding_agreement +// @tags Funding Agreements +// @accept json +// @param authorization header string true "Hex encoded centrifuge ID of the account for the intended API action" +// @param document_id path string true "Document Identifier" +// @param body body userapi.FundingRequest true "Funding agreement Create Request" +// @produce json +// @Failure 500 {object} httputils.HTTPError +// @Failure 400 {object} httputils.HTTPError +// @Failure 403 {object} httputils.HTTPError +// @success 202 {object} userapi.FundingResponse +// @router /v1/documents/{document_id}/funding_agreements [post] +func (h handler) CreateFundingAgreement(w http.ResponseWriter, r *http.Request) { + var err error + var code int + defer httputils.RespondIfError(&code, &err, w, r) + + docID, err := hexutil.Decode(chi.URLParam(r, coreapi.DocumentIDParam)) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + err = coreapi.ErrInvalidDocumentID + return + } + + ctx := r.Context() + data, err := ioutil.ReadAll(r.Body) + if err != nil { + code = http.StatusInternalServerError + log.Error(err) + return + } + + var request FundingRequest + err = json.Unmarshal(data, &request) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + return + } + + m, j, err := h.srv.CreateFundingAgreement(ctx, docID, &request.Data) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + return + } + + resp, err := toFundingAgreementResponse(ctx, h.srv.fundingSrv, m, request.Data.AgreementID, h.tokenRegistry, j) + if err != nil { + code = http.StatusInternalServerError + log.Error(err) + return + } + + render.Status(r, http.StatusAccepted) + render.JSON(w, r, resp) +} + +// GetFundingAgreements returns all the funding agreements in the document associated with document_id. +// @summary Returns all the funding agreements in the document associated with document_id. +// @description Returns all the funding agreements in the document associated with document_id. +// @id get_funding_agreements +// @tags Funding Agreements +// @accept json +// @param authorization header string true "Hex encoded centrifuge ID of the account for the intended API action" +// @param document_id path string true "Document Identifier" +// @produce json +// @Failure 500 {object} httputils.HTTPError +// @Failure 400 {object} httputils.HTTPError +// @Failure 404 {object} httputils.HTTPError +// @Failure 403 {object} httputils.HTTPError +// @success 200 {object} userapi.FundingListResponse +// @router /v1/documents/{document_id}/funding_agreements [get] +func (h handler) GetFundingAgreements(w http.ResponseWriter, r *http.Request) { + var err error + var code int + defer httputils.RespondIfError(&code, &err, w, r) + + docID, err := hexutil.Decode(chi.URLParam(r, coreapi.DocumentIDParam)) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + err = coreapi.ErrInvalidDocumentID + return + } + + ctx := r.Context() + m, err := h.srv.coreAPISrv.GetDocument(ctx, docID) + if err != nil { + code = http.StatusNotFound + log.Error(err) + return + } + + resp, err := toFundingAgreementListResponse(ctx, h.srv.fundingSrv, m, h.tokenRegistry) + if err != nil { + code = http.StatusInternalServerError + log.Error(err) + return + } + + render.Status(r, http.StatusOK) + render.JSON(w, r, resp) +} + +// GetFundingAgreement returns the funding agreement associated with agreement_id in the document. +// @summary Returns the funding agreement associated with agreement_id in the document. +// @description Returns the funding agreement associated with agreement_id in the document. +// @id get_funding_agreement +// @tags Funding Agreements +// @accept json +// @param authorization header string true "Hex encoded centrifuge ID of the account for the intended API action" +// @param document_id path string true "Document Identifier" +// @param agreement_id path string true "Funding agreement Identifier" +// @produce json +// @Failure 500 {object} httputils.HTTPError +// @Failure 400 {object} httputils.HTTPError +// @Failure 404 {object} httputils.HTTPError +// @Failure 403 {object} httputils.HTTPError +// @success 200 {object} userapi.FundingResponse +// @router /v1/documents/{document_id}/funding_agreements/{agreement_id} [get] +func (h handler) GetFundingAgreement(w http.ResponseWriter, r *http.Request) { + var err error + var code int + defer httputils.RespondIfError(&code, &err, w, r) + + docID, err := hexutil.Decode(chi.URLParam(r, coreapi.DocumentIDParam)) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + err = coreapi.ErrInvalidDocumentID + return + } + + _, err = hexutil.Decode(chi.URLParam(r, agreementIDParam)) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + err = ErrInvalidAgreementID + return + } + + ctx := r.Context() + m, err := h.srv.coreAPISrv.GetDocument(ctx, docID) + if err != nil { + code = http.StatusNotFound + log.Error(err) + return + } + + resp, err := toFundingAgreementResponse(ctx, h.srv.fundingSrv, m, chi.URLParam(r, agreementIDParam), h.tokenRegistry, jobs.NilJobID()) + if err != nil { + code = http.StatusInternalServerError + log.Error(err) + return + } + + render.Status(r, http.StatusOK) + render.JSON(w, r, resp) +} + +// UpdateFundingAgreement updates the funding agreement associated with agreement_id in the document. +// @summary Updates the funding agreement associated with agreement_id in the document. +// @description Updates the funding agreement associated with agreement_id in the document. +// @id update_funding_agreement +// @tags Funding Agreements +// @accept json +// @param authorization header string true "Hex encoded centrifuge ID of the account for the intended API action" +// @param document_id path string true "Document Identifier" +// @param agreement_id path string true "Funding agreement Identifier" +// @param body body userapi.FundingRequest true "Funding Agreement Update Request" +// @produce json +// @Failure 500 {object} httputils.HTTPError +// @Failure 400 {object} httputils.HTTPError +// @Failure 404 {object} httputils.HTTPError +// @Failure 403 {object} httputils.HTTPError +// @success 202 {object} userapi.FundingResponse +// @router /v1/documents/{document_id}/funding_agreements/{agreement_id} [PUT] +func (h handler) UpdateFundingAgreement(w http.ResponseWriter, r *http.Request) { + var err error + var code int + defer httputils.RespondIfError(&code, &err, w, r) + + docID, err := hexutil.Decode(chi.URLParam(r, coreapi.DocumentIDParam)) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + err = coreapi.ErrInvalidDocumentID + return + } + + fundingID, err := hexutil.Decode(chi.URLParam(r, agreementIDParam)) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + err = ErrInvalidAgreementID + return + } + + ctx := r.Context() + data, err := ioutil.ReadAll(r.Body) + if err != nil { + code = http.StatusInternalServerError + log.Error(err) + return + } + + var request FundingRequest + err = json.Unmarshal(data, &request) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + return + } + + m, jobID, err := h.srv.fundingSrv.UpdateFundingAgreement(ctx, docID, fundingID, &request.Data) + if err != nil { + code = http.StatusNotFound + log.Error(err) + return + } + + resp, err := toFundingAgreementResponse(ctx, h.srv.fundingSrv, m, chi.URLParam(r, agreementIDParam), h.tokenRegistry, jobID) + if err != nil { + code = http.StatusInternalServerError + log.Error(err) + return + } + + render.Status(r, http.StatusAccepted) + render.JSON(w, r, resp) +} + +// SignFundingAgreement signs the funding agreement associated with agreement_id. +// @summary Signs the funding agreement associated with agreement_id. +// @description Signs the funding agreement associated with agreement_id. +// @id sign_funding_agreement +// @tags Funding Agreements +// @accept json +// @param authorization header string true "Hex encoded centrifuge ID of the account for the intended API action" +// @param document_id path string true "Document Identifier" +// @param agreement_id path string true "Funding agreement Identifier" +// @produce json +// @Failure 500 {object} httputils.HTTPError +// @Failure 400 {object} httputils.HTTPError +// @Failure 404 {object} httputils.HTTPError +// @Failure 403 {object} httputils.HTTPError +// @success 200 {object} userapi.FundingResponse +// @router /v1/documents/{document_id}/funding_agreements/{agreement_id}/sign [post] +func (h handler) SignFundingAgreement(w http.ResponseWriter, r *http.Request) { + var err error + var code int + defer httputils.RespondIfError(&code, &err, w, r) + + docID, err := hexutil.Decode(chi.URLParam(r, coreapi.DocumentIDParam)) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + err = coreapi.ErrInvalidDocumentID + return + } + + fundingID, err := hexutil.Decode(chi.URLParam(r, agreementIDParam)) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + err = ErrInvalidAgreementID + return + } + + ctx := r.Context() + m, jobID, err := h.srv.fundingSrv.SignFundingAgreement(ctx, docID, fundingID) + if err != nil { + code = http.StatusNotFound + log.Error(err) + return + } + + resp, err := toFundingAgreementResponse(ctx, h.srv.fundingSrv, m, chi.URLParam(r, agreementIDParam), h.tokenRegistry, jobID) + if err != nil { + code = http.StatusInternalServerError + log.Error(err) + return + } + + render.Status(r, http.StatusAccepted) + render.JSON(w, r, resp) +} + +// GetFundingAgreementFromVersion returns the funding agreement from a specific version of the document. +// @summary Returns the funding agreement from a specific version of the document. +// @description Returns the funding agreement from a specific version of the document. +// @id get_funding_agreement_version +// @tags Funding Agreements +// @param authorization header string true "Hex encoded centrifuge ID of the account for the intended API action" +// @param document_id path string true "Document Identifier" +// @param version_id path string true "Document Version Identifier" +// @param agreement_id path string true "Funding agreement Identifier" +// @produce json +// @Failure 403 {object} httputils.HTTPError +// @Failure 400 {object} httputils.HTTPError +// @Failure 404 {object} httputils.HTTPError +// @success 200 {object} userapi.FundingResponse +// @router /v1/documents/{document_id}/versions/{version_id}/funding_agreements/{agreement_id} [get] +func (h handler) GetFundingAgreementFromVersion(w http.ResponseWriter, r *http.Request) { + var err error + var code int + defer httputils.RespondIfError(&code, &err, w, r) + + ids := make([][]byte, 2, 2) + for i, idStr := range []string{chi.URLParam(r, coreapi.DocumentIDParam), chi.URLParam(r, coreapi.VersionIDParam)} { + var id []byte + id, err = hexutil.Decode(idStr) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + err = coreapi.ErrInvalidDocumentID + return + } + + ids[i] = id + } + + _, err = hexutil.Decode(chi.URLParam(r, agreementIDParam)) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + err = ErrInvalidAgreementID + return + } + + model, err := h.srv.coreAPISrv.GetDocumentVersion(r.Context(), ids[0], ids[1]) + if err != nil { + code = http.StatusNotFound + log.Error(err) + err = coreapi.ErrDocumentNotFound + return + } + + resp, err := toFundingAgreementResponse(r.Context(), h.srv.fundingSrv, model, chi.URLParam(r, agreementIDParam), h.tokenRegistry, jobs.NilJobID()) + if err != nil { + code = http.StatusNotFound + log.Error(err) + return + } + + render.Status(r, http.StatusOK) + render.JSON(w, r, resp) +} + +// GetFundingAgreementsFromVersion returns all the funding agreements from a specific version of the document. +// @summary Returns all the funding agreements from a specific version of the document. +// @description Returns all the funding agreements from a specific version of the document. +// @id get_funding_agreements_version +// @tags Funding Agreements +// @param authorization header string true "Hex encoded centrifuge ID of the account for the intended API action" +// @param document_id path string true "Document Identifier" +// @param version_id path string true "Document Version Identifier" +// @produce json +// @Failure 403 {object} httputils.HTTPError +// @Failure 400 {object} httputils.HTTPError +// @Failure 404 {object} httputils.HTTPError +// @Failure 500 {object} httputils.HTTPError +// @success 200 {object} userapi.FundingListResponse +// @router /v1/documents/{document_id}/versions/{version_id}/funding_agreements [get] +func (h handler) GetFundingAgreementsFromVersion(w http.ResponseWriter, r *http.Request) { + var err error + var code int + defer httputils.RespondIfError(&code, &err, w, r) + + ids := make([][]byte, 2, 2) + for i, idStr := range []string{chi.URLParam(r, coreapi.DocumentIDParam), chi.URLParam(r, coreapi.VersionIDParam)} { + var id []byte + id, err = hexutil.Decode(idStr) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + err = coreapi.ErrInvalidDocumentID + return + } + + ids[i] = id + } + + model, err := h.srv.coreAPISrv.GetDocumentVersion(r.Context(), ids[0], ids[1]) + if err != nil { + code = http.StatusNotFound + log.Error(err) + err = coreapi.ErrDocumentNotFound + return + } + + resp, err := toFundingAgreementListResponse(r.Context(), h.srv.fundingSrv, model, h.tokenRegistry) + if err != nil { + code = http.StatusInternalServerError + log.Error(err) + return + } + + render.Status(r, http.StatusOK) + render.JSON(w, r, resp) +} diff --git a/httpapi/userapi/funding_api_test.go b/httpapi/userapi/funding_api_test.go new file mode 100644 index 000000000..31e0defac --- /dev/null +++ b/httpapi/userapi/funding_api_test.go @@ -0,0 +1,452 @@ +// +build unit + +package userapi + +import ( + "bytes" + "context" + "encoding/json" + "io" + "net/http" + "net/http/httptest" + "testing" + + "github.com/centrifuge/go-centrifuge/documents" + "github.com/centrifuge/go-centrifuge/documents/invoice" + "github.com/centrifuge/go-centrifuge/errors" + "github.com/centrifuge/go-centrifuge/extensions/funding" + "github.com/centrifuge/go-centrifuge/httpapi/coreapi" + "github.com/centrifuge/go-centrifuge/jobs" + testingconfig "github.com/centrifuge/go-centrifuge/testingutils/config" + testingdocuments "github.com/centrifuge/go-centrifuge/testingutils/documents" + "github.com/centrifuge/go-centrifuge/utils" + "github.com/centrifuge/go-centrifuge/utils/byteutils" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/go-chi/chi" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func TestHandler_CreateFundingAgreement(t *testing.T) { + getHTTPReqAndResp := func(ctx context.Context, b io.Reader) (*httptest.ResponseRecorder, *http.Request) { + return httptest.NewRecorder(), httptest.NewRequest("POST", "/documents/{document_id}/funding_agreements", b).WithContext(ctx) + } + // empty document_id and invalid id + rctx := chi.NewRouteContext() + rctx.URLParams.Keys = make([]string, 1, 1) + rctx.URLParams.Values = make([]string, 1, 1) + rctx.URLParams.Keys[0] = "document_id" + ctx := context.WithValue(context.Background(), chi.RouteCtxKey, rctx) + h := handler{} + for _, id := range []string{"", "invalid"} { + rctx.URLParams.Values[0] = id + w, r := getHTTPReqAndResp(ctx, nil) + h.CreateFundingAgreement(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), coreapi.ErrInvalidDocumentID.Error()) + } + + // empty body + rctx.URLParams.Values[0] = byteutils.HexBytes(utils.RandomSlice(32)).String() + w, r := getHTTPReqAndResp(ctx, nil) + h.CreateFundingAgreement(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), "unexpected end of JSON input") + + // creation failed + d, err := json.Marshal(map[string]interface{}{ + "data": funding.Data{}, + }) + assert.NoError(t, err) + fundingSrv := new(funding.MockService) + fundingSrv.On("CreateFundingAgreement", mock.Anything, mock.Anything, mock.Anything).Return(nil, jobs.NilJobID(), errors.New("failed to create funding agreement")).Once() + h.srv = Service{fundingSrv: fundingSrv} + w, r = getHTTPReqAndResp(ctx, bytes.NewReader(d)) + h.CreateFundingAgreement(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), "failed to create funding agreement") + + // failed response conversion + m := new(testingdocuments.MockModel) + m.On("ID").Return(utils.RandomSlice(32)) + m.On("CurrentVersion").Return(utils.RandomSlice(32)) + m.On("Author").Return(nil, errors.New("somerror")) + m.On("Timestamp").Return(nil, errors.New("somerror")) + m.On("NFTs").Return(nil) + m.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, errors.New("failed to get collaborators")).Once() + fundingSrv.On("CreateFundingAgreement", mock.Anything, mock.Anything, mock.Anything).Return(m, jobs.NewJobID(), nil) + w, r = getHTTPReqAndResp(ctx, bytes.NewReader(d)) + h.CreateFundingAgreement(w, r) + assert.Equal(t, w.Code, http.StatusInternalServerError) + assert.Contains(t, w.Body.String(), "failed to get collaborators") + + // success + m.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, nil) + fundingSrv.On("GetDataAndSignatures", mock.Anything, mock.Anything, mock.Anything).Return(funding.Data{}, nil, nil) + w, r = getHTTPReqAndResp(ctx, bytes.NewReader(d)) + h.CreateFundingAgreement(w, r) + assert.Equal(t, w.Code, http.StatusAccepted) + fundingSrv.AssertExpectations(t) + m.AssertExpectations(t) +} + +func TestHandler_GetFundingAgreements(t *testing.T) { + getHTTPReqAndResp := func(ctx context.Context) (*httptest.ResponseRecorder, *http.Request) { + return httptest.NewRecorder(), httptest.NewRequest("GET", "/documents/{document_id}/funding_agreements", nil).WithContext(ctx) + } + // empty document_id and invalid id + rctx := chi.NewRouteContext() + rctx.URLParams.Keys = make([]string, 1, 1) + rctx.URLParams.Values = make([]string, 1, 1) + rctx.URLParams.Keys[0] = "document_id" + ctx := context.WithValue(context.Background(), chi.RouteCtxKey, rctx) + h := handler{} + for _, id := range []string{"", "invalid"} { + rctx.URLParams.Values[0] = id + w, r := getHTTPReqAndResp(ctx) + h.GetFundingAgreements(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), coreapi.ErrInvalidDocumentID.Error()) + } + + // missing Doc + id := utils.RandomSlice(32) + rctx.URLParams.Values[0] = byteutils.HexBytes(id).String() + docSrv := new(testingdocuments.MockService) + docSrv.On("GetCurrentVersion", id).Return(nil, errors.New("doc not found")).Once() + h.srv.coreAPISrv = newCoreAPIService(docSrv) + w, r := getHTTPReqAndResp(ctx) + h.GetFundingAgreements(w, r) + assert.Equal(t, w.Code, http.StatusNotFound) + + // failed conversion + m := new(testingdocuments.MockModel) + m.On("ID").Return(utils.RandomSlice(32)) + m.On("CurrentVersion").Return(utils.RandomSlice(32)) + m.On("Author").Return(nil, errors.New("somerror")) + m.On("Timestamp").Return(nil, errors.New("somerror")) + m.On("NFTs").Return(nil) + m.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, errors.New("failed to get collaborators")).Once() + docSrv.On("GetCurrentVersion", id).Return(m, nil) + w, r = getHTTPReqAndResp(ctx) + h.GetFundingAgreements(w, r) + assert.Equal(t, w.Code, http.StatusInternalServerError) + + // success + m.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, nil) + m.On("AttributeExists", mock.Anything).Return(false) + w, r = getHTTPReqAndResp(ctx) + h.GetFundingAgreements(w, r) + assert.Equal(t, w.Code, http.StatusOK) + docSrv.AssertExpectations(t) + m.AssertExpectations(t) +} + +func TestHandler_GetFundingAgreement(t *testing.T) { + getHTTPReqAndResp := func(ctx context.Context) (*httptest.ResponseRecorder, *http.Request) { + return httptest.NewRecorder(), httptest.NewRequest("GET", "/documents/{document_id}/funding_agreements/{agreement_id}", nil).WithContext(ctx) + } + // empty document_id and invalid id + rctx := chi.NewRouteContext() + rctx.URLParams.Keys = make([]string, 2, 2) + rctx.URLParams.Values = make([]string, 2, 2) + rctx.URLParams.Keys[0] = "document_id" + rctx.URLParams.Keys[1] = "agreement_id" + ctx := context.WithValue(context.Background(), chi.RouteCtxKey, rctx) + h := handler{} + for _, id := range []string{"", "invalid"} { + rctx.URLParams.Values[0] = id + w, r := getHTTPReqAndResp(ctx) + h.GetFundingAgreement(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), coreapi.ErrInvalidDocumentID.Error()) + } + + id := utils.RandomSlice(32) + rctx.URLParams.Values[0] = byteutils.HexBytes(id).String() + for _, id := range []string{"", "invalid"} { + rctx.URLParams.Values[1] = id + w, r := getHTTPReqAndResp(ctx) + h.GetFundingAgreement(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), ErrInvalidAgreementID.Error()) + } + + // missing Doc + fundingID := hexutil.Encode(utils.RandomSlice(32)) + rctx.URLParams.Values[1] = fundingID + docSrv := new(testingdocuments.MockService) + docSrv.On("GetCurrentVersion", id).Return(nil, errors.New("doc not found")).Once() + h.srv.coreAPISrv = newCoreAPIService(docSrv) + w, r := getHTTPReqAndResp(ctx) + h.GetFundingAgreement(w, r) + assert.Equal(t, w.Code, http.StatusNotFound) + + // failed response conversion + fundingSrv := new(funding.MockService) + h.srv.fundingSrv = fundingSrv + m := new(testingdocuments.MockModel) + docSrv.On("GetCurrentVersion", id).Return(m, nil) + m.On("ID").Return(utils.RandomSlice(32)) + m.On("CurrentVersion").Return(utils.RandomSlice(32)) + m.On("Author").Return(nil, errors.New("somerror")) + m.On("Timestamp").Return(nil, errors.New("somerror")) + m.On("NFTs").Return(nil) + m.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, errors.New("failed to get collaborators")).Once() + w, r = getHTTPReqAndResp(ctx) + h.GetFundingAgreement(w, r) + assert.Equal(t, w.Code, http.StatusInternalServerError) + assert.Contains(t, w.Body.String(), "failed to get collaborators") + + // success + m.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, nil) + fundingSrv.On("GetDataAndSignatures", mock.Anything, mock.Anything, mock.Anything).Return(funding.Data{}, nil, nil) + w, r = getHTTPReqAndResp(ctx) + h.GetFundingAgreement(w, r) + assert.Equal(t, w.Code, http.StatusOK) + fundingSrv.AssertExpectations(t) + m.AssertExpectations(t) + fundingSrv.AssertExpectations(t) + docSrv.AssertExpectations(t) +} + +func TestHandler_UpdateFundingAgreement(t *testing.T) { + getHTTPReqAndResp := func(ctx context.Context, body io.Reader) (*httptest.ResponseRecorder, *http.Request) { + return httptest.NewRecorder(), httptest.NewRequest("PUT", "/documents/{document_id}/funding_agreements/{agreement_id}", body).WithContext(ctx) + } + // empty document_id and invalid id + rctx := chi.NewRouteContext() + rctx.URLParams.Keys = make([]string, 2, 2) + rctx.URLParams.Values = make([]string, 2, 2) + rctx.URLParams.Keys[0] = "document_id" + rctx.URLParams.Keys[1] = "agreement_id" + ctx := context.WithValue(context.Background(), chi.RouteCtxKey, rctx) + h := handler{} + for _, id := range []string{"", "invalid"} { + rctx.URLParams.Values[0] = id + w, r := getHTTPReqAndResp(ctx, nil) + h.UpdateFundingAgreement(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), coreapi.ErrInvalidDocumentID.Error()) + } + + id := utils.RandomSlice(32) + rctx.URLParams.Values[0] = byteutils.HexBytes(id).String() + for _, id := range []string{"", "invalid"} { + rctx.URLParams.Values[1] = id + w, r := getHTTPReqAndResp(ctx, nil) + h.UpdateFundingAgreement(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), ErrInvalidAgreementID.Error()) + } + + // empty body + fundingID := utils.RandomSlice(32) + rctx.URLParams.Values[1] = hexutil.Encode(fundingID) + w, r := getHTTPReqAndResp(ctx, nil) + h.UpdateFundingAgreement(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), "unexpected end of JSON input") + + // update failed + fundingSrv := new(funding.MockService) + data := funding.CreateData() + rctx.URLParams.Values[1] = data.AgreementID + fundingID, err := hexutil.Decode(data.AgreementID) + assert.NoError(t, err) + fundingSrv.On("UpdateFundingAgreement", mock.Anything, id, fundingID, mock.Anything).Return(nil, nil, errors.New("failed to update")).Once() + h.srv.fundingSrv = fundingSrv + d, err := json.Marshal(map[string]interface{}{ + "data": data, + }) + assert.NoError(t, err) + w, r = getHTTPReqAndResp(ctx, bytes.NewReader(d)) + h.UpdateFundingAgreement(w, r) + assert.Equal(t, w.Code, http.StatusNotFound) + assert.Contains(t, w.Body.String(), "failed to update") + + // success + inv, agreementID := funding.CreateInvoiceWithFunding(t, testingconfig.CreateAccountContext(t, cfg), did) + fundingID, err = hexutil.Decode(agreementID) + assert.NoError(t, err) + rctx.URLParams.Values[1] = agreementID + fundingSrv.On("UpdateFundingAgreement", mock.Anything, id, fundingID, mock.Anything).Return(inv, jobs.NewJobID(), nil) + fundingSrv.On("GetDataAndSignatures", mock.Anything, mock.Anything, mock.Anything).Return(data, nil, nil) + w, r = getHTTPReqAndResp(ctx, bytes.NewReader(d)) + h.UpdateFundingAgreement(w, r) + assert.Equal(t, w.Code, http.StatusAccepted) + assert.Contains(t, w.Body.String(), data.AgreementID) + fundingSrv.AssertExpectations(t) +} + +func TestHandler_SignFundingAgreement(t *testing.T) { + getHTTPReqAndResp := func(ctx context.Context) (*httptest.ResponseRecorder, *http.Request) { + return httptest.NewRecorder(), httptest.NewRequest("POST", "/documents/{document_id}/funding_agreements/{agreement_id}/sign", nil).WithContext(ctx) + } + // empty document_id and invalid id + rctx := chi.NewRouteContext() + rctx.URLParams.Keys = make([]string, 2, 2) + rctx.URLParams.Values = make([]string, 2, 2) + rctx.URLParams.Keys[0] = "document_id" + rctx.URLParams.Keys[1] = "agreement_id" + ctx := context.WithValue(context.Background(), chi.RouteCtxKey, rctx) + h := handler{} + for _, id := range []string{"", "invalid"} { + rctx.URLParams.Values[0] = id + w, r := getHTTPReqAndResp(ctx) + h.SignFundingAgreement(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), coreapi.ErrInvalidDocumentID.Error()) + } + + id := utils.RandomSlice(32) + rctx.URLParams.Values[0] = byteutils.HexBytes(id).String() + for _, id := range []string{"", "invalid"} { + rctx.URLParams.Values[1] = id + w, r := getHTTPReqAndResp(ctx) + h.SignFundingAgreement(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), ErrInvalidAgreementID.Error()) + } + + // failed to sign + fundingID := utils.RandomSlice(32) + rctx.URLParams.Values[1] = hexutil.Encode(fundingID) + fundingSrv := new(funding.MockService) + h.srv.fundingSrv = fundingSrv + inv, _ := invoice.CreateInvoiceWithEmbedCD(t, testingconfig.CreateAccountContext(t, cfg), did, nil) + fundingSrv.On("SignFundingAgreement", mock.Anything, id, fundingID).Return(nil, nil, errors.New("failed to sign")).Once() + w, r := getHTTPReqAndResp(ctx) + h.SignFundingAgreement(w, r) + assert.Equal(t, w.Code, http.StatusNotFound) + assert.Contains(t, w.Body.String(), "failed to sign") + + // success + fundingSrv.On("SignFundingAgreement", mock.Anything, id, fundingID).Return(inv, jobs.NewJobID(), nil).Once() + fundingSrv.On("GetDataAndSignatures", mock.Anything, mock.Anything, mock.Anything).Return(funding.Data{}, nil, nil) + w, r = getHTTPReqAndResp(ctx) + h.SignFundingAgreement(w, r) + assert.Equal(t, w.Code, http.StatusAccepted) + fundingSrv.AssertExpectations(t) + fundingSrv.AssertExpectations(t) +} + +func TestHandler_GetFundingAgreementFromVersion(t *testing.T) { + getHTTPReqAndResp := func(ctx context.Context) (*httptest.ResponseRecorder, *http.Request) { + return httptest.NewRecorder(), httptest.NewRequest("GET", "/documents/{document_id}/versions/{version_id}/funding_agreements/{agreement_id}", nil).WithContext(ctx) + } + + // empty document_id and invalid + rctx := chi.NewRouteContext() + rctx.URLParams.Keys = make([]string, 3, 3) + rctx.URLParams.Values = make([]string, 3, 3) + rctx.URLParams.Keys[0] = "document_id" + rctx.URLParams.Keys[1] = "version_id" + rctx.URLParams.Keys[2] = "agreement_id" + ctx := context.WithValue(context.Background(), chi.RouteCtxKey, rctx) + h := handler{} + + for _, id := range []string{"", "invalid"} { + rctx.URLParams.Values[0] = id + rctx.URLParams.Values[1] = id + w, r := getHTTPReqAndResp(ctx) + h.GetFundingAgreementFromVersion(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), coreapi.ErrInvalidDocumentID.Error()) + } + + // invalid agreement id + id := utils.RandomSlice(32) + vid := utils.RandomSlice(32) + rctx.URLParams.Values[0] = hexutil.Encode(id) + rctx.URLParams.Values[1] = hexutil.Encode(vid) + w, r := getHTTPReqAndResp(ctx) + h.GetFundingAgreementFromVersion(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), ErrInvalidAgreementID.Error()) + + // missing document + fundingID := utils.RandomSlice(32) + rctx.URLParams.Values[2] = hexutil.Encode(fundingID) + docSrv := new(testingdocuments.MockService) + docSrv.On("GetVersion", id, vid).Return(nil, errors.New("missing document")).Once() + w, r = getHTTPReqAndResp(ctx) + h.srv.coreAPISrv = newCoreAPIService(docSrv) + h.GetFundingAgreementFromVersion(w, r) + assert.Equal(t, w.Code, http.StatusNotFound) + assert.Contains(t, w.Body.String(), coreapi.ErrDocumentNotFound.Error()) + + // missing agreement + fundingSrv := new(funding.MockService) + h.srv.fundingSrv = fundingSrv + fundingSrv.On("GetDataAndSignatures", mock.Anything, mock.Anything, mock.Anything).Return(funding.Data{}, nil, errors.New("failed coneverison")).Once() + inv, agID := funding.CreateInvoiceWithFunding(t, testingconfig.CreateAccountContext(t, cfg), did) + docSrv.On("GetVersion", id, vid).Return(inv, nil) + w, r = getHTTPReqAndResp(ctx) + h.GetFundingAgreementFromVersion(w, r) + assert.Equal(t, w.Code, http.StatusNotFound) + + // success + fundingSrv.On("GetDataAndSignatures", mock.Anything, mock.Anything, mock.Anything).Return(funding.Data{}, nil, nil) + rctx.URLParams.Values[2] = agID + w, r = getHTTPReqAndResp(ctx) + h.GetFundingAgreementFromVersion(w, r) + assert.Equal(t, w.Code, http.StatusOK) + docSrv.AssertExpectations(t) + fundingSrv.AssertExpectations(t) +} + +func TestHandler_GetFundingAgreementsFromVersion(t *testing.T) { + getHTTPReqAndResp := func(ctx context.Context) (*httptest.ResponseRecorder, *http.Request) { + return httptest.NewRecorder(), httptest.NewRequest("GET", "/documents/{document_id}/versions/{version_id}/funding_agreements", nil).WithContext(ctx) + } + + // empty document_id and invalid + rctx := chi.NewRouteContext() + rctx.URLParams.Keys = make([]string, 2, 2) + rctx.URLParams.Values = make([]string, 2, 2) + rctx.URLParams.Keys[0] = "document_id" + rctx.URLParams.Keys[1] = "version_id" + ctx := context.WithValue(context.Background(), chi.RouteCtxKey, rctx) + h := handler{} + + for _, id := range []string{"", "invalid"} { + rctx.URLParams.Values[0] = id + rctx.URLParams.Values[1] = id + w, r := getHTTPReqAndResp(ctx) + h.GetFundingAgreementsFromVersion(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), coreapi.ErrInvalidDocumentID.Error()) + } + + // missing document + id := utils.RandomSlice(32) + vid := utils.RandomSlice(32) + rctx.URLParams.Values[0] = hexutil.Encode(id) + rctx.URLParams.Values[1] = hexutil.Encode(vid) + docSrv := new(testingdocuments.MockService) + docSrv.On("GetVersion", id, vid).Return(nil, errors.New("missing document")).Once() + w, r := getHTTPReqAndResp(ctx) + h.srv.coreAPISrv = newCoreAPIService(docSrv) + h.GetFundingAgreementsFromVersion(w, r) + assert.Equal(t, w.Code, http.StatusNotFound) + assert.Contains(t, w.Body.String(), coreapi.ErrDocumentNotFound.Error()) + + // failed conversion + fundingSrv := new(funding.MockService) + h.srv.fundingSrv = fundingSrv + fundingSrv.On("GetDataAndSignatures", mock.Anything, mock.Anything, mock.Anything).Return(funding.Data{}, nil, errors.New("failed coneverison")).Once() + inv, _ := funding.CreateInvoiceWithFunding(t, testingconfig.CreateAccountContext(t, cfg), did) + docSrv.On("GetVersion", id, vid).Return(inv, nil) + w, r = getHTTPReqAndResp(ctx) + h.GetFundingAgreementsFromVersion(w, r) + assert.Equal(t, w.Code, http.StatusInternalServerError) + + // success + fundingSrv.On("GetDataAndSignatures", mock.Anything, mock.Anything, mock.Anything).Return(funding.Data{}, nil, nil) + w, r = getHTTPReqAndResp(ctx) + h.GetFundingAgreementsFromVersion(w, r) + assert.Equal(t, w.Code, http.StatusOK) + docSrv.AssertExpectations(t) + fundingSrv.AssertExpectations(t) +} diff --git a/httpapi/userapi/invoice_api.go b/httpapi/userapi/invoice_api.go new file mode 100644 index 000000000..d2d8cd359 --- /dev/null +++ b/httpapi/userapi/invoice_api.go @@ -0,0 +1,284 @@ +package userapi + +import ( + "encoding/json" + "io/ioutil" + "net/http" + + "github.com/centrifuge/go-centrifuge/httpapi/coreapi" + "github.com/centrifuge/go-centrifuge/jobs" + "github.com/centrifuge/go-centrifuge/utils/httputils" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/go-chi/chi" + "github.com/go-chi/render" +) + +// CreateInvoice creates an invoice document. +// @summary Creates a new invoice document and anchors it. +// @description Creates a new invoice document and anchors it. +// @id create_invoice +// @tags Invoices +// @accept json +// @param authorization header string true "Hex encoded centrifuge ID of the account for the intended API action" +// @param body body userapi.CreateInvoiceRequest true "Invoice Create Request" +// @produce json +// @Failure 500 {object} httputils.HTTPError +// @Failure 400 {object} httputils.HTTPError +// @Failure 403 {object} httputils.HTTPError +// @success 202 {object} userapi.InvoiceResponse +// @router /v1/invoices [post] +func (h handler) CreateInvoice(w http.ResponseWriter, r *http.Request) { + var err error + var code int + defer httputils.RespondIfError(&code, &err, w, r) + + ctx := r.Context() + data, err := ioutil.ReadAll(r.Body) + if err != nil { + code = http.StatusInternalServerError + log.Error(err) + return + } + + var request CreateInvoiceRequest + err = json.Unmarshal(data, &request) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + return + } + + m, j, err := h.srv.CreateInvoice(ctx, request) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + return + } + + resp, err := toInvoiceResponse(m, h.tokenRegistry, j) + if err != nil { + code = http.StatusInternalServerError + log.Error(err) + return + } + + render.Status(r, http.StatusAccepted) + render.JSON(w, r, resp) +} + +// UpdateInvoice updates an existing Invoice. +// @summary Updates an existing Invoice and anchors it. +// @description Updates an existing invoice and anchors it. +// @id update_invoice +// @tags Invoices +// @accept json +// @param authorization header string true "Hex encoded centrifuge ID of the account for the intended API action" +// @param document_id path string true "Document Identifier" +// @param body body userapi.CreateInvoiceRequest true "Invoice Update request" +// @produce json +// @Failure 500 {object} httputils.HTTPError +// @Failure 400 {object} httputils.HTTPError +// @Failure 403 {object} httputils.HTTPError +// @success 202 {object} userapi.InvoiceResponse +// @router /v1/invoices/{document_id} [put] +func (h handler) UpdateInvoice(w http.ResponseWriter, r *http.Request) { + var err error + var code int + defer httputils.RespondIfError(&code, &err, w, r) + + docID, err := hexutil.Decode(chi.URLParam(r, coreapi.DocumentIDParam)) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + err = coreapi.ErrInvalidDocumentID + return + } + + ctx := r.Context() + data, err := ioutil.ReadAll(r.Body) + if err != nil { + code = http.StatusInternalServerError + log.Error(err) + return + } + + var request CreateInvoiceRequest + err = json.Unmarshal(data, &request) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + return + } + + model, jobID, err := h.srv.UpdateInvoice(ctx, docID, request) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + return + } + + resp, err := toInvoiceResponse(model, h.tokenRegistry, jobID) + if err != nil { + code = http.StatusInternalServerError + log.Error(err) + return + } + + render.Status(r, http.StatusAccepted) + render.JSON(w, r, resp) +} + +// GetInvoice returns the latest version of the Invoice. +// @summary Returns the latest version of the Invoice. +// @description Returns the latest version of the Invoice. +// @id get_invoice +// @tags Invoices +// @param authorization header string true "Hex encoded centrifuge ID of the account for the intended API action" +// @param document_id path string true "Document Identifier" +// @produce json +// @Failure 403 {object} httputils.HTTPError +// @Failure 400 {object} httputils.HTTPError +// @Failure 404 {object} httputils.HTTPError +// @Failure 500 {object} httputils.HTTPError +// @success 200 {object} userapi.InvoiceResponse +// @router /v1/invoices/{document_id} [get] +func (h handler) GetInvoice(w http.ResponseWriter, r *http.Request) { + var err error + var code int + defer httputils.RespondIfError(&code, &err, w, r) + + docID, err := hexutil.Decode(chi.URLParam(r, coreapi.DocumentIDParam)) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + err = coreapi.ErrInvalidDocumentID + return + } + + model, err := h.srv.GetInvoice(r.Context(), docID) + if err != nil { + code = http.StatusNotFound + log.Error(err) + err = coreapi.ErrDocumentNotFound + return + } + + resp, err := toInvoiceResponse(model, h.tokenRegistry, jobs.NilJobID()) + if err != nil { + code = http.StatusInternalServerError + log.Error(err) + return + } + + render.Status(r, http.StatusOK) + render.JSON(w, r, resp) +} + +// GetInvoiceVersion returns the specific version of an Invoice. +// @summary Returns the specific version of an Invoice. +// @description Returns the specific version of an Invoice. +// @id get_invoice_version +// @tags Invoices +// @param authorization header string true "Hex encoded centrifuge ID of the account for the intended API action" +// @param document_id path string true "Document Identifier" +// @param version_id path string true "Document Version Identifier" +// @produce json +// @Failure 403 {object} httputils.HTTPError +// @Failure 400 {object} httputils.HTTPError +// @Failure 404 {object} httputils.HTTPError +// @Failure 500 {object} httputils.HTTPError +// @success 200 {object} userapi.InvoiceResponse +// @router /v1/invoices/{document_id}/versions/{version_id} [get] +func (h handler) GetInvoiceVersion(w http.ResponseWriter, r *http.Request) { + var err error + var code int + defer httputils.RespondIfError(&code, &err, w, r) + + ids := make([][]byte, 2, 2) + for i, idStr := range []string{chi.URLParam(r, coreapi.DocumentIDParam), chi.URLParam(r, coreapi.VersionIDParam)} { + var id []byte + id, err = hexutil.Decode(idStr) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + err = coreapi.ErrInvalidDocumentID + return + } + + ids[i] = id + } + + model, err := h.srv.GetInvoiceVersion(r.Context(), ids[0], ids[1]) + if err != nil { + code = http.StatusNotFound + log.Error(err) + err = coreapi.ErrDocumentNotFound + return + } + + resp, err := toInvoiceResponse(model, h.tokenRegistry, jobs.NilJobID()) + if err != nil { + code = http.StatusInternalServerError + log.Error(err) + return + } + + render.Status(r, http.StatusOK) + render.JSON(w, r, resp) +} + +// MintInvoiceUnpaidNFT mints an NFT for an unpaid invoice document. +// @summary Mints an NFT for an unpaid invoice document. +// @description Mints an NFT for an unpaid invoice document. +// @id invoice_unpaid_nft +// @tags Invoices +// @accept json +// @param authorization header string true "Hex encoded centrifuge ID of the account for the intended API action" +// @param body body userapi.NFTMintInvoiceUnpaidRequest true "Invoice Unpaid NFT Mint Request" +// @produce json +// @Failure 500 {object} httputils.HTTPError +// @Failure 400 {object} httputils.HTTPError +// @Failure 403 {object} httputils.HTTPError +// @success 202 {object} userapi.NFTMintResponse +// @router /v1/invoices/{document_id}/mint/unpaid [post] +func (h handler) MintInvoiceUnpaidNFT(w http.ResponseWriter, r *http.Request) { + var err error + var code int + defer httputils.RespondIfError(&code, &err, w, r) + + docID, err := hexutil.Decode(chi.URLParam(r, coreapi.DocumentIDParam)) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + err = coreapi.ErrInvalidDocumentID + return + } + + ctx := r.Context() + data, err := ioutil.ReadAll(r.Body) + if err != nil { + code = http.StatusInternalServerError + log.Error(err) + return + } + + var req NFTMintInvoiceUnpaidRequest + err = json.Unmarshal(data, &req) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + return + } + + m, err := h.srv.MintInvoiceUnpaidNFT(ctx, docID, req.DepositAddress) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + return + } + + render.Status(r, http.StatusAccepted) + render.JSON(w, r, NFTMintResponse{ + Header: &ResponseHeader{JobID: m.JobID}, + }) +} diff --git a/httpapi/userapi/invoice_api_test.go b/httpapi/userapi/invoice_api_test.go new file mode 100644 index 000000000..78794925b --- /dev/null +++ b/httpapi/userapi/invoice_api_test.go @@ -0,0 +1,432 @@ +// +build unit + +package userapi + +import ( + "bytes" + "context" + "encoding/json" + "io" + "net/http" + "net/http/httptest" + "testing" + + "github.com/centrifuge/go-centrifuge/config/configstore" + "github.com/centrifuge/go-centrifuge/documents" + "github.com/centrifuge/go-centrifuge/documents/invoice" + "github.com/centrifuge/go-centrifuge/errors" + "github.com/centrifuge/go-centrifuge/httpapi/coreapi" + "github.com/centrifuge/go-centrifuge/jobs" + "github.com/centrifuge/go-centrifuge/nft" + "github.com/centrifuge/go-centrifuge/testingutils/config" + "github.com/centrifuge/go-centrifuge/testingutils/documents" + "github.com/centrifuge/go-centrifuge/testingutils/nfts" + "github.com/centrifuge/go-centrifuge/utils" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/go-chi/chi" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func invData() map[string]interface{} { + return map[string]interface{}{ + "number": "12345", + "status": "unpaid", + "recipient": "0xBAEb33a61f05e6F269f1c4b4CFF91A901B54DaF7", + "date_confirmed": "2019-05-24T14:48:44Z", // rfc3339 + "currency": "EUR", + "attachments": []map[string]interface{}{ + { + "name": "test", + "file_type": "pdf", + "size": 1000202, + "data": "0xBAEb33a61f05e6F269f1c4b4CFF91A901B54DaF7", + "checksum": "0xBAEb33a61f05e6F269f1c4b4CFF91A901B54DaF3", + }, + }, + } +} + +func marshall(t *testing.T, data interface{}) []byte { + d, err := json.Marshal(data) + assert.NoError(t, err) + return d +} + +func TestHandler_CreateInvoice(t *testing.T) { + data := map[string]interface{}{ + "data": invData(), + "attributes": map[string]map[string]string{ + "string_test": { + "type": "invalid", + "value": "hello, world", + }, + }, + } + + getHTTPReqAndResp := func(ctx context.Context, b io.Reader) (*httptest.ResponseRecorder, *http.Request) { + return httptest.NewRecorder(), httptest.NewRequest("POST", "/invoices", b).WithContext(ctx) + } + + // empty body + rctx := chi.NewRouteContext() + ctx := context.WithValue(context.Background(), chi.RouteCtxKey, rctx) + h := handler{} + w, r := getHTTPReqAndResp(ctx, nil) + h.CreateInvoice(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), "unexpected end of JSON input") + + // invalid body + w, r = getHTTPReqAndResp(ctx, bytes.NewReader(marshall(t, data))) + h.CreateInvoice(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), documents.ErrNotValidAttrType.Error()) + + // failed response conversion + data["attributes"] = map[string]map[string]string{ + "string_test": { + "type": "string", + "value": "hello, world", + }, + } + m := new(testingdocuments.MockModel) + m.On("GetData").Return(invoice.Data{}) + m.On("Scheme").Return(invoice.Scheme) + m.On("GetAttributes").Return(nil) + m.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, errors.New("failed to get collaborators")) + docSrv := new(testingdocuments.MockService) + srv := Service{coreAPISrv: newCoreAPIService(docSrv)} + h = handler{srv: srv} + docSrv.On("CreateModel", mock.Anything, mock.Anything).Return(m, jobs.NewJobID(), nil) + w, r = getHTTPReqAndResp(ctx, bytes.NewReader(marshall(t, data))) + h.CreateInvoice(w, r) + assert.Equal(t, w.Code, http.StatusInternalServerError) + assert.Contains(t, w.Body.String(), "failed to get collaborators") + m.AssertExpectations(t) + docSrv.AssertExpectations(t) + + // success + m = new(testingdocuments.MockModel) + m.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, nil).Once() + m.On("GetData").Return(invoice.Data{}) + m.On("Scheme").Return(invoice.Scheme) + m.On("ID").Return(utils.RandomSlice(32)).Once() + m.On("CurrentVersion").Return(utils.RandomSlice(32)).Once() + m.On("Author").Return(nil, errors.New("somerror")) + m.On("Timestamp").Return(nil, errors.New("somerror")) + m.On("NFTs").Return(nil) + m.On("GetAttributes").Return(nil) + docSrv = new(testingdocuments.MockService) + srv = Service{coreAPISrv: newCoreAPIService(docSrv)} + h = handler{srv: srv} + docSrv.On("CreateModel", mock.Anything, mock.Anything).Return(m, jobs.NewJobID(), nil) + w, r = getHTTPReqAndResp(ctx, bytes.NewReader(marshall(t, data))) + h.CreateInvoice(w, r) + assert.Equal(t, w.Code, http.StatusAccepted) + m.AssertExpectations(t) + docSrv.AssertExpectations(t) +} + +func TestHandler_GetInvoice(t *testing.T) { + getHTTPReqAndResp := func(ctx context.Context) (*httptest.ResponseRecorder, *http.Request) { + return httptest.NewRecorder(), httptest.NewRequest("GET", "/invoices/{document_id}", nil).WithContext(ctx) + } + + rctx := chi.NewRouteContext() + rctx.URLParams.Keys = make([]string, 1, 1) + rctx.URLParams.Values = make([]string, 1, 1) + rctx.URLParams.Keys[0] = "document_id" + ctx := context.WithValue(context.Background(), chi.RouteCtxKey, rctx) + h := handler{} + + // empty document_id and invalid + for _, id := range []string{"", "invalid"} { + rctx.URLParams.Values[0] = id + w, r := getHTTPReqAndResp(ctx) + h.GetInvoice(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), coreapi.ErrInvalidDocumentID.Error()) + } + + // missing document + id := utils.RandomSlice(32) + rctx.URLParams.Values[0] = hexutil.Encode(id) + docSrv := new(testingdocuments.MockService) + docSrv.On("GetCurrentVersion", id).Return(nil, errors.New("failed")) + h = handler{srv: Service{coreAPISrv: newCoreAPIService(docSrv)}} + w, r := getHTTPReqAndResp(ctx) + h.GetInvoice(w, r) + assert.Equal(t, w.Code, http.StatusNotFound) + assert.Contains(t, w.Body.String(), coreapi.ErrDocumentNotFound.Error()) + docSrv.AssertExpectations(t) + + // failed doc response + m := new(testingdocuments.MockModel) + m.On("GetData").Return(invoice.Data{}) + m.On("Scheme").Return(invoice.Scheme) + m.On("GetAttributes").Return(nil) + m.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, errors.New("failed to get collaborators")) + docSrv = new(testingdocuments.MockService) + docSrv.On("GetCurrentVersion", id).Return(m, nil) + h = handler{srv: Service{coreAPISrv: newCoreAPIService(docSrv)}} + w, r = getHTTPReqAndResp(ctx) + h.GetInvoice(w, r) + assert.Equal(t, w.Code, http.StatusInternalServerError) + assert.Contains(t, w.Body.String(), "failed to get collaborators") + docSrv.AssertExpectations(t) + m.AssertExpectations(t) + + // success + m = new(testingdocuments.MockModel) + m.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, nil).Once() + m.On("GetData").Return(invoice.Data{}) + m.On("Scheme").Return(invoice.Scheme) + m.On("ID").Return(utils.RandomSlice(32)).Once() + m.On("CurrentVersion").Return(utils.RandomSlice(32)).Once() + m.On("Author").Return(nil, errors.New("somerror")) + m.On("Timestamp").Return(nil, errors.New("somerror")) + m.On("NFTs").Return(nil) + m.On("GetAttributes").Return(nil) + docSrv = new(testingdocuments.MockService) + docSrv.On("GetCurrentVersion", id).Return(m, nil) + h = handler{srv: Service{coreAPISrv: newCoreAPIService(docSrv)}} + w, r = getHTTPReqAndResp(ctx) + h.GetInvoice(w, r) + assert.Equal(t, w.Code, http.StatusOK) + docSrv.AssertExpectations(t) + m.AssertExpectations(t) +} + +func TestHandler_UpdateInvoice(t *testing.T) { + getHTTPReqAndResp := func(ctx context.Context, b io.Reader) (*httptest.ResponseRecorder, *http.Request) { + return httptest.NewRecorder(), httptest.NewRequest("PUT", "/invoice/{document_id}", b).WithContext(ctx) + } + // empty document_id and invalid id + rctx := chi.NewRouteContext() + rctx.URLParams.Keys = make([]string, 1, 1) + rctx.URLParams.Values = make([]string, 1, 1) + rctx.URLParams.Keys[0] = "document_id" + ctx := context.WithValue(context.Background(), chi.RouteCtxKey, rctx) + h := handler{} + for _, id := range []string{"", "invalid"} { + rctx.URLParams.Values[0] = id + w, r := getHTTPReqAndResp(ctx, nil) + h.UpdateInvoice(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), coreapi.ErrInvalidDocumentID.Error()) + } + + // empty body + id := hexutil.Encode(utils.RandomSlice(32)) + rctx.URLParams.Values[0] = id + w, r := getHTTPReqAndResp(ctx, nil) + h.UpdateInvoice(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), "unexpected end of JSON input") + + // failed conversion + data := map[string]interface{}{ + "data": invData(), + "attributes": map[string]map[string]string{ + "string_test": { + "type": "invalid", + "value": "hello, world", + }, + }, + } + + d, err := json.Marshal(data) + assert.NoError(t, err) + w, r = getHTTPReqAndResp(ctx, bytes.NewReader(d)) + h.UpdateInvoice(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), documents.ErrNotValidAttrType.Error()) + + // failed to update + data["attributes"] = map[string]map[string]string{ + "string_test": { + "type": "string", + "value": "hello, world", + }, + } + d, err = json.Marshal(data) + assert.NoError(t, err) + docSrv := new(testingdocuments.MockService) + srv := Service{coreAPISrv: newCoreAPIService(docSrv)} + h = handler{srv: srv} + docSrv.On("UpdateModel", mock.Anything, mock.Anything).Return(nil, jobs.NilJobID(), errors.New("failed to update model")) + w, r = getHTTPReqAndResp(ctx, bytes.NewReader(d)) + h.UpdateInvoice(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), "failed to update model") + docSrv.AssertExpectations(t) + + // failed response conversion + m := new(testingdocuments.MockModel) + m.On("GetData").Return(invoice.Data{}) + m.On("Scheme").Return(invoice.Scheme) + m.On("GetAttributes").Return(nil) + m.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, errors.New("failed to get collaborators")) + docSrv = new(testingdocuments.MockService) + srv = Service{coreAPISrv: newCoreAPIService(docSrv)} + h = handler{srv: srv} + docSrv.On("UpdateModel", mock.Anything, mock.Anything).Return(m, jobs.NewJobID(), nil) + w, r = getHTTPReqAndResp(ctx, bytes.NewReader(d)) + h.UpdateInvoice(w, r) + assert.Equal(t, w.Code, http.StatusInternalServerError) + assert.Contains(t, w.Body.String(), "failed to get collaborators") + docSrv.AssertExpectations(t) + m.AssertExpectations(t) + + // success + m = new(testingdocuments.MockModel) + m.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, nil).Once() + m.On("GetData").Return(invoice.Data{}) + m.On("Scheme").Return(invoice.Scheme) + m.On("ID").Return(utils.RandomSlice(32)).Once() + m.On("CurrentVersion").Return(utils.RandomSlice(32)).Once() + m.On("Author").Return(nil, errors.New("somerror")) + m.On("Timestamp").Return(nil, errors.New("somerror")) + m.On("NFTs").Return(nil) + m.On("GetAttributes").Return(nil) + docSrv = new(testingdocuments.MockService) + srv = Service{coreAPISrv: newCoreAPIService(docSrv)} + h = handler{srv: srv} + docSrv.On("UpdateModel", mock.Anything, mock.Anything).Return(m, jobs.NewJobID(), nil) + w, r = getHTTPReqAndResp(ctx, bytes.NewReader(d)) + h.UpdateInvoice(w, r) + assert.Equal(t, w.Code, http.StatusAccepted) + m.AssertExpectations(t) + docSrv.AssertExpectations(t) +} + +func TestHandler_GetInvoiceVersion(t *testing.T) { + getHTTPReqAndResp := func(ctx context.Context) (*httptest.ResponseRecorder, *http.Request) { + return httptest.NewRecorder(), httptest.NewRequest("GET", "/invoices/{document_id}/versions/{version_id}", nil).WithContext(ctx) + } + + // empty document_id and invalid + rctx := chi.NewRouteContext() + rctx.URLParams.Keys = make([]string, 2, 2) + rctx.URLParams.Values = make([]string, 2, 2) + rctx.URLParams.Keys[0] = "document_id" + rctx.URLParams.Keys[1] = "version_id" + ctx := context.WithValue(context.Background(), chi.RouteCtxKey, rctx) + h := handler{} + + for _, id := range []string{"", "invalid"} { + rctx.URLParams.Values[0] = id + rctx.URLParams.Values[1] = id + w, r := getHTTPReqAndResp(ctx) + h.GetInvoiceVersion(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), coreapi.ErrInvalidDocumentID.Error()) + } + + // missing document + id := utils.RandomSlice(32) + vid := utils.RandomSlice(32) + rctx.URLParams.Values[0] = hexutil.Encode(id) + rctx.URLParams.Values[1] = hexutil.Encode(vid) + docSrv := new(testingdocuments.MockService) + docSrv.On("GetVersion", id, vid).Return(nil, errors.New("failed")) + h = handler{srv: Service{coreAPISrv: newCoreAPIService(docSrv)}} + w, r := getHTTPReqAndResp(ctx) + h.GetInvoiceVersion(w, r) + assert.Equal(t, w.Code, http.StatusNotFound) + assert.Contains(t, w.Body.String(), coreapi.ErrDocumentNotFound.Error()) + docSrv.AssertExpectations(t) + + // failed doc response + m := new(testingdocuments.MockModel) + m.On("GetData").Return(invoice.Data{}) + m.On("Scheme").Return(invoice.Scheme) + m.On("GetAttributes").Return(nil) + m.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, errors.New("failed to get collaborators")) + docSrv = new(testingdocuments.MockService) + docSrv.On("GetVersion", id, vid).Return(m, nil) + h = handler{srv: Service{coreAPISrv: newCoreAPIService(docSrv)}} + w, r = getHTTPReqAndResp(ctx) + h.GetInvoiceVersion(w, r) + assert.Equal(t, w.Code, http.StatusInternalServerError) + assert.Contains(t, w.Body.String(), "failed to get collaborators") + docSrv.AssertExpectations(t) + m.AssertExpectations(t) + + // success + m = new(testingdocuments.MockModel) + m.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, nil).Once() + m.On("GetData").Return(invoice.Data{}) + m.On("Scheme").Return(invoice.Scheme) + m.On("ID").Return(utils.RandomSlice(32)).Once() + m.On("CurrentVersion").Return(utils.RandomSlice(32)).Once() + m.On("Author").Return(nil, errors.New("somerror")) + m.On("Timestamp").Return(nil, errors.New("somerror")) + m.On("NFTs").Return(nil) + m.On("GetAttributes").Return(nil) + docSrv = new(testingdocuments.MockService) + docSrv.On("GetVersion", id, vid).Return(m, nil) + h = handler{srv: Service{coreAPISrv: newCoreAPIService(docSrv)}} + w, r = getHTTPReqAndResp(ctx) + h.GetInvoiceVersion(w, r) + assert.Equal(t, w.Code, http.StatusOK) + docSrv.AssertExpectations(t) + m.AssertExpectations(t) +} + +func TestHandler_MintInvoiceUnpaidNFT(t *testing.T) { + getHTTPReqAndResp := func(ctx context.Context, b io.Reader) (*httptest.ResponseRecorder, *http.Request) { + return httptest.NewRecorder(), httptest.NewRequest("POST", "/v1/invoices/{document_id}/mint/unpaid", b).WithContext(ctx) + } + + srv := new(testingnfts.MockNFTService) + mc := configstore.MockService{} + mc.On("GetConfig").Return(cfg, nil) + h := handler{srv: Service{coreAPISrv: coreapi.NewService(nil, nil, srv, nil), config: mc}} + rctx := chi.NewRouteContext() + rctx.URLParams.Keys = make([]string, 1, 1) + rctx.URLParams.Values = make([]string, 1, 1) + rctx.URLParams.Keys[0] = "document_id" + c := testingconfig.CreateAccountContext(t, cfg) + ctx := context.WithValue(c, chi.RouteCtxKey, rctx) + + // invalid docID + w, r := getHTTPReqAndResp(ctx, nil) + h.MintInvoiceUnpaidNFT(w, r) + assert.Equal(t, http.StatusBadRequest, w.Code) + assert.Contains(t, w.Body.String(), coreapi.ErrInvalidDocumentID.Error()) + + // empty data + docID := utils.RandomSlice(32) + rctx.URLParams.Values[0] = hexutil.Encode(docID) + w, r = getHTTPReqAndResp(ctx, nil) + h.MintInvoiceUnpaidNFT(w, r) + assert.Equal(t, http.StatusBadRequest, w.Code) + assert.Contains(t, w.Body.String(), "unexpected end of JSON input") + data := map[string]interface{}{ + "deposit_address": hexutil.Encode(utils.RandomSlice(20)), + } + + d, err := json.Marshal(data) + assert.NoError(t, err) + w, r = getHTTPReqAndResp(ctx, bytes.NewReader(d)) + srv.On("MintNFT", ctx, mock.Anything).Return(nil, nil, errors.New("failed to mint nft")).Once() + h.MintInvoiceUnpaidNFT(w, r) + assert.Equal(t, http.StatusBadRequest, w.Code) + assert.Contains(t, w.Body.String(), "failed to mint nft") + srv.AssertExpectations(t) + + // success + w, r = getHTTPReqAndResp(ctx, bytes.NewReader(d)) + tokenID := hexutil.Encode(utils.RandomSlice(32)) + srv.On("MintNFT", ctx, mock.Anything).Return( + &nft.TokenResponse{ + TokenID: tokenID, + JobID: jobs.NewJobID().String(), + }, nil, nil).Once() + h.MintInvoiceUnpaidNFT(w, r) + assert.Equal(t, http.StatusAccepted, w.Code) + assert.Contains(t, w.Body.String(), "job_id") + srv.AssertExpectations(t) +} diff --git a/httpapi/userapi/nfts_api.go b/httpapi/userapi/nfts_api.go new file mode 100644 index 000000000..da1a7ed59 --- /dev/null +++ b/httpapi/userapi/nfts_api.go @@ -0,0 +1,202 @@ +package userapi + +import ( + "encoding/json" + "io/ioutil" + "net/http" + + "github.com/centrifuge/go-centrifuge/errors" + "github.com/centrifuge/go-centrifuge/nft" + "github.com/centrifuge/go-centrifuge/utils/httputils" + "github.com/ethereum/go-ethereum/common" + "github.com/go-chi/chi" + "github.com/go-chi/render" +) + +const ( + // ErrInvalidTokenID is a sentinel error when token ID is invalid + ErrInvalidTokenID = errors.Error("Invalid Token ID") + + // ErrInvalidRegistryAddress is a sentinel error when registry address is invalid + ErrInvalidRegistryAddress = errors.Error("Invalid registry address") +) + +// MintNFT mints an NFT. +// @summary Mints an NFT against a document. +// @description Mints an NFT against a document. +// @id mint_nft +// @tags NFTsBeta +// @accept json +// @param authorization header string true "Hex encoded centrifuge ID of the account for the intended API action" +// @param registry_address path string true "NFT registry address in hex" +// @param body body userapi.MintNFTRequest true "Mint NFT request" +// @produce json +// @Failure 403 {object} httputils.HTTPError +// @Failure 500 {object} httputils.HTTPError +// @Failure 400 {object} httputils.HTTPError +// @success 202 {object} userapi.MintNFTResponse +// @router /beta/nfts/registries/{registry_address}/mint [post] +func (h handler) MintNFT(w http.ResponseWriter, r *http.Request) { + var err error + var code int + defer httputils.RespondIfError(&code, &err, w, r) + + if !common.IsHexAddress(chi.URLParam(r, registryAddressParam)) { + code = http.StatusBadRequest + err = ErrInvalidRegistryAddress + log.Error(err) + return + } + + registry := common.HexToAddress(chi.URLParam(r, registryAddressParam)) + ctx := r.Context() + data, err := ioutil.ReadAll(r.Body) + if err != nil { + code = http.StatusInternalServerError + log.Error(err) + return + } + + var req MintNFTRequest + err = json.Unmarshal(data, &req) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + return + } + + resp, err := h.srv.MintNFT(ctx, toNFTMintRequest(req, registry)) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + return + } + + nftResp := MintNFTResponse{ + Header: NFTResponseHeader{JobID: resp.JobID}, + RegistryAddress: registry, + DepositAddress: req.DepositAddress, + DocumentID: req.DocumentID, + TokenID: resp.TokenID, + } + render.Status(r, http.StatusAccepted) + render.JSON(w, r, nftResp) +} + +// TransferNFT transfers given NFT to provide address. +// @summary Transfers given NFT to provide address. +// @description Transfers given NFT to provide address. +// @id transfer_nft +// @tags NFTsBeta +// @accept json +// @param authorization header string true "Hex encoded centrifuge ID of the account for the intended API action" +// @param registry_address path string true "NFT registry address in hex" +// @param token_id path string true "NFT token ID in hex" +// @param body body userapi.TransferNFTRequest true "Mint NFT request" +// @produce json +// @Failure 403 {object} httputils.HTTPError +// @Failure 500 {object} httputils.HTTPError +// @Failure 400 {object} httputils.HTTPError +// @success 200 {object} userapi.TransferNFTResponse +// @router /beta/nfts/registries/{registry_address}/tokens/{token_id}/transfer [post] +func (h handler) TransferNFT(w http.ResponseWriter, r *http.Request) { + var err error + var code int + defer httputils.RespondIfError(&code, &err, w, r) + + if !common.IsHexAddress(chi.URLParam(r, registryAddressParam)) { + code = http.StatusBadRequest + err = ErrInvalidRegistryAddress + log.Error(err) + return + } + + registry := common.HexToAddress(chi.URLParam(r, registryAddressParam)) + tokenID, err := nft.TokenIDFromString(chi.URLParam(r, tokenIDParam)) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + err = ErrInvalidTokenID + return + } + + ctx := r.Context() + data, err := ioutil.ReadAll(r.Body) + if err != nil { + code = http.StatusInternalServerError + log.Error(err) + return + } + + var req TransferNFTRequest + err = json.Unmarshal(data, &req) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + return + } + + resp, err := h.srv.TransferNFT(ctx, req.To, registry, tokenID) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + return + } + + render.Status(r, http.StatusOK) + render.JSON(w, r, TransferNFTResponse{ + RegistryAddress: registry, + To: req.To, + TokenID: resp.TokenID, + Header: NFTResponseHeader{JobID: resp.JobID}, + }) +} + +// OwnerOfNFT returns the owner of the given NFT. +// @summary Returns the Owner of the given NFT. +// @description Returns the Owner of the given NFT. +// @id owner_of_nft +// @tags NFTsBeta +// @param authorization header string true "Hex encoded centrifuge ID of the account for the intended API action" +// @param token_id path string true "NFT token ID in hex" +// @param registry_address path string true "Registry address in hex" +// @produce json +// @Failure 403 {object} httputils.HTTPError +// @Failure 400 {object} httputils.HTTPError +// @success 200 {object} userapi.NFTOwnerResponse +// @router /beta/nfts/registries/{registry_address}/tokens/{token_id}/owner [get] +func (h handler) OwnerOfNFT(w http.ResponseWriter, r *http.Request) { + var err error + var code int + defer httputils.RespondIfError(&code, &err, w, r) + + if !common.IsHexAddress(chi.URLParam(r, registryAddressParam)) { + code = http.StatusBadRequest + err = ErrInvalidRegistryAddress + log.Error(err) + return + } + + tokenID, err := nft.TokenIDFromString(chi.URLParam(r, tokenIDParam)) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + err = ErrInvalidTokenID + return + } + + registry := common.HexToAddress(chi.URLParam(r, registryAddressParam)) + owner, err := h.srv.OwnerOfNFT(registry, tokenID) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + return + } + + render.Status(r, http.StatusOK) + render.JSON(w, r, NFTOwnerResponse{ + TokenID: tokenID.String(), + RegistryAddress: registry, + Owner: owner, + }) +} diff --git a/httpapi/userapi/nfts_api_test.go b/httpapi/userapi/nfts_api_test.go new file mode 100644 index 000000000..be7a419aa --- /dev/null +++ b/httpapi/userapi/nfts_api_test.go @@ -0,0 +1,219 @@ +// +build unit + +package userapi + +import ( + "bytes" + "context" + "encoding/json" + "io" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/centrifuge/go-centrifuge/httpapi/coreapi" + + "github.com/centrifuge/go-centrifuge/errors" + "github.com/centrifuge/go-centrifuge/jobs" + "github.com/centrifuge/go-centrifuge/nft" + testingnfts "github.com/centrifuge/go-centrifuge/testingutils/nfts" + "github.com/centrifuge/go-centrifuge/utils" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/go-chi/chi" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func testTokenIDAndRegistryAddress(t *testing.T, rctx *chi.Context, getRequestFunc func(ctx context.Context) (*httptest.ResponseRecorder, *http.Request), route http.HandlerFunc) { + // empty registry + ctx := context.WithValue(context.Background(), chi.RouteCtxKey, rctx) + w, r := getRequestFunc(ctx) + route(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), ErrInvalidRegistryAddress.Error()) + + // invalid registry address + rctx.URLParams.Values[1] = "some value" + w, r = getRequestFunc(ctx) + route(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), ErrInvalidRegistryAddress.Error()) + + // empty token id + rctx.URLParams.Values[1] = hexutil.Encode(utils.RandomSlice(20)) + w, r = getRequestFunc(ctx) + route(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), ErrInvalidTokenID.Error()) + + // invalid token ID + rctx.URLParams.Values[0] = "some invalid token id" + w, r = getRequestFunc(ctx) + route(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), ErrInvalidTokenID.Error()) + rctx.URLParams.Values[0] = hexutil.Encode(utils.RandomSlice(32)) +} + +func TestHandler_MintNFT(t *testing.T) { + getHTTPReqAndResp := func(ctx context.Context, b io.Reader) (*httptest.ResponseRecorder, *http.Request) { + return httptest.NewRecorder(), httptest.NewRequest("POST", "/nfts/registries/{registry_address}/mint", b).WithContext(ctx) + } + + // empty registry tests + h := handler{} + rctx := chi.NewRouteContext() + rctx.URLParams.Keys = make([]string, 1, 1) + rctx.URLParams.Values = make([]string, 1, 1) + rctx.URLParams.Keys[0] = registryAddressParam + rctx.URLParams.Values[0] = "" + ctx := context.WithValue(context.Background(), chi.RouteCtxKey, rctx) + w, r := getHTTPReqAndResp(ctx, nil) + h.MintNFT(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), ErrInvalidRegistryAddress.Error()) + + // invalid registry + rctx.URLParams.Values[0] = "some invalid" + w, r = getHTTPReqAndResp(ctx, nil) + h.MintNFT(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), ErrInvalidRegistryAddress.Error()) + + // empty data + rctx.URLParams.Values[0] = hexutil.Encode(utils.RandomSlice(20)) + w, r = getHTTPReqAndResp(ctx, nil) + h.MintNFT(w, r) + assert.Equal(t, http.StatusBadRequest, w.Code) + assert.Contains(t, w.Body.String(), "unexpected end of JSON input") + data := map[string]interface{}{ + "document_id": hexutil.Encode(utils.RandomSlice(32)), + "deposit_address": hexutil.Encode(utils.RandomSlice(20)), + } + + d, err := json.Marshal(data) + assert.NoError(t, err) + w, r = getHTTPReqAndResp(ctx, bytes.NewReader(d)) + srv := new(testingnfts.MockNFTService) + srv.On("MintNFT", ctx, mock.Anything).Return(nil, nil, errors.New("failed to mint nft")).Once() + h.srv.coreAPISrv = coreapi.NewService(nil, nil, srv, nil) + h.MintNFT(w, r) + assert.Equal(t, http.StatusBadRequest, w.Code) + assert.Contains(t, w.Body.String(), "failed to mint nft") + srv.AssertExpectations(t) + + // success + w, r = getHTTPReqAndResp(ctx, bytes.NewReader(d)) + srv = new(testingnfts.MockNFTService) + tokenID := hexutil.Encode(utils.RandomSlice(32)) + srv.On("MintNFT", ctx, mock.Anything).Return( + &nft.TokenResponse{ + TokenID: tokenID, + JobID: jobs.NewJobID().String(), + }, nil, nil).Once() + h.srv.coreAPISrv = coreapi.NewService(nil, nil, srv, nil) + h.MintNFT(w, r) + assert.Equal(t, http.StatusAccepted, w.Code) + assert.Contains(t, w.Body.String(), tokenID) + srv.AssertExpectations(t) +} + +func TestHandler_TransferNFT(t *testing.T) { + var b io.Reader + getHTTPReqAndResp := func(ctx context.Context) (*httptest.ResponseRecorder, *http.Request) { + return httptest.NewRecorder(), httptest.NewRequest("POST", "/nfts/registries/{registry_address}/tokens/{token_id}/transfer", b).WithContext(ctx) + } + + // empty token and registry tests + h := handler{} + rctx := chi.NewRouteContext() + rctx.URLParams.Keys = make([]string, 2, 2) + rctx.URLParams.Values = make([]string, 2, 2) + rctx.URLParams.Keys[0] = tokenIDParam + rctx.URLParams.Values[0] = "" + rctx.URLParams.Keys[1] = registryAddressParam + rctx.URLParams.Values[1] = "" + ctx := context.WithValue(context.Background(), chi.RouteCtxKey, rctx) + testTokenIDAndRegistryAddress(t, rctx, getHTTPReqAndResp, h.TransferNFT) + + // empty body + tokenID, err := nft.TokenIDFromString(rctx.URLParams.Values[0]) + assert.NoError(t, err) + w, r := getHTTPReqAndResp(ctx) + h = handler{} + h.TransferNFT(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), "unexpected end of JSON input") + + // Service fail + to := hexutil.Encode(utils.RandomSlice(20)) + body := map[string]interface{}{ + "to": to, + } + d, err := json.Marshal(body) + assert.NoError(t, err) + srv := new(testingnfts.MockNFTService) + srv.On("TransferFrom", ctx, mock.Anything, mock.Anything, mock.Anything).Return(nil, nil, errors.New("failed to transfer")).Once() + h.srv.coreAPISrv = coreapi.NewService(nil, nil, srv, nil) + b = bytes.NewReader(d) + w, r = getHTTPReqAndResp(ctx) + h.TransferNFT(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), "failed to transfer") + srv.AssertExpectations(t) + + // success + srv = new(testingnfts.MockNFTService) + srv.On("TransferFrom", ctx, mock.Anything, mock.Anything, mock.Anything).Return(&nft.TokenResponse{ + TokenID: tokenID.String(), + JobID: jobs.NewJobID().String(), + }, nil, nil).Once() + h.srv.coreAPISrv = coreapi.NewService(nil, nil, srv, nil) + b = bytes.NewReader(d) + w, r = getHTTPReqAndResp(ctx) + h.TransferNFT(w, r) + assert.Equal(t, w.Code, http.StatusOK) + assert.Contains(t, w.Body.String(), tokenID.String()) + srv.AssertExpectations(t) +} + +func TestHandler_OwnerOfNFT(t *testing.T) { + getHTTPReqAndResp := func(ctx context.Context) (*httptest.ResponseRecorder, *http.Request) { + return httptest.NewRecorder(), httptest.NewRequest("GET", "/nfts/registries/{registry_address}/tokens/{token_id}/owner", nil).WithContext(ctx) + } + + // empty token and registry tests + h := handler{} + rctx := chi.NewRouteContext() + rctx.URLParams.Keys = make([]string, 2, 2) + rctx.URLParams.Values = make([]string, 2, 2) + rctx.URLParams.Keys[0] = tokenIDParam + rctx.URLParams.Values[0] = "" + rctx.URLParams.Keys[1] = registryAddressParam + rctx.URLParams.Values[1] = "" + ctx := context.WithValue(context.Background(), chi.RouteCtxKey, rctx) + testTokenIDAndRegistryAddress(t, rctx, getHTTPReqAndResp, h.OwnerOfNFT) + + // owner failed + srv := new(testingnfts.MockNFTService) + srv.On("OwnerOf", mock.Anything, mock.Anything).Return(nil, errors.New("failed to get owner")).Once() + h.srv.coreAPISrv = coreapi.NewService(nil, nil, srv, nil) + w, r := getHTTPReqAndResp(ctx) + h.OwnerOfNFT(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), "failed to get owner") + srv.AssertExpectations(t) + + // success + owner := common.BytesToAddress(utils.RandomSlice(20)) + srv = new(testingnfts.MockNFTService) + srv.On("OwnerOf", mock.Anything, mock.Anything).Return(owner, nil).Once() + h.srv.coreAPISrv = coreapi.NewService(nil, nil, srv, nil) + w, r = getHTTPReqAndResp(ctx) + h.OwnerOfNFT(w, r) + assert.Equal(t, w.Code, http.StatusOK) + assert.Contains(t, w.Body.String(), strings.ToLower(owner.String())) + srv.AssertExpectations(t) +} diff --git a/httpapi/userapi/service.go b/httpapi/userapi/service.go index 903e6b43f..4ae0b9962 100644 --- a/httpapi/userapi/service.go +++ b/httpapi/userapi/service.go @@ -2,16 +2,32 @@ package userapi import ( "context" + "fmt" + "github.com/centrifuge/go-centrifuge/config" + "github.com/centrifuge/go-centrifuge/contextutil" "github.com/centrifuge/go-centrifuge/documents" + "github.com/centrifuge/go-centrifuge/documents/entity" + "github.com/centrifuge/go-centrifuge/documents/entityrelationship" + "github.com/centrifuge/go-centrifuge/documents/invoice" + "github.com/centrifuge/go-centrifuge/extensions/funding" "github.com/centrifuge/go-centrifuge/extensions/transferdetails" + "github.com/centrifuge/go-centrifuge/httpapi/coreapi" + "github.com/centrifuge/go-centrifuge/identity" "github.com/centrifuge/go-centrifuge/jobs" + "github.com/centrifuge/go-centrifuge/nft" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" ) // Service provides functionality for User APIs. type Service struct { - docSrv documents.Service + coreAPISrv coreapi.Service transferDetailsService transferdetails.Service + entityRelationshipSrv entityrelationship.Service + entitySrv entity.Service + fundingSrv funding.Service + config config.Service } // TODO: this can be refactored into a generic Service which handles all kinds of custom attributes @@ -28,7 +44,7 @@ func (s Service) UpdateTransferDetail(ctx context.Context, req transferdetails.U // GetCurrentTransferDetail returns the current version on a Transfer Detail func (s Service) GetCurrentTransferDetail(ctx context.Context, docID, transferID []byte) (*transferdetails.TransferDetail, documents.Model, error) { - model, err := s.docSrv.GetCurrentVersion(ctx, docID) + model, err := s.coreAPISrv.GetDocument(ctx, docID) if err != nil { return nil, nil, err } @@ -42,7 +58,7 @@ func (s Service) GetCurrentTransferDetail(ctx context.Context, docID, transferID // GetCurrentTransferDetailsList returns a list of Transfer Details on the current version of a document func (s Service) GetCurrentTransferDetailsList(ctx context.Context, docID []byte) (*transferdetails.TransferDetailList, documents.Model, error) { - model, err := s.docSrv.GetCurrentVersion(ctx, docID) + model, err := s.coreAPISrv.GetDocument(ctx, docID) if err != nil { return nil, nil, err } @@ -57,7 +73,7 @@ func (s Service) GetCurrentTransferDetailsList(ctx context.Context, docID []byte // GetVersionTransferDetail returns a Transfer Detail on a particular version of a Document func (s Service) GetVersionTransferDetail(ctx context.Context, docID, versionID, transferID []byte) (*transferdetails.TransferDetail, documents.Model, error) { - model, err := s.docSrv.GetVersion(ctx, docID, versionID) + model, err := s.coreAPISrv.GetDocumentVersion(ctx, docID, versionID) if err != nil { return nil, nil, err } @@ -72,7 +88,7 @@ func (s Service) GetVersionTransferDetail(ctx context.Context, docID, versionID, // GetVersionTransferDetailsList returns a list of Transfer Details on a particular version of a Document func (s Service) GetVersionTransferDetailsList(ctx context.Context, docID, versionID []byte) (*transferdetails.TransferDetailList, documents.Model, error) { - model, err := s.docSrv.GetVersion(ctx, docID, versionID) + model, err := s.coreAPISrv.GetDocumentVersion(ctx, docID, versionID) if err != nil { return nil, nil, err } @@ -84,3 +100,181 @@ func (s Service) GetVersionTransferDetailsList(ctx context.Context, docID, versi return data, model, nil } + +func convertInvRequest(req CreateInvoiceRequest) (documents.CreatePayload, error) { + coreAPIReq := coreapi.CreateDocumentRequest{ + Scheme: invoice.Scheme, + WriteAccess: req.WriteAccess, + ReadAccess: req.ReadAccess, + Data: req.Data, + Attributes: req.Attributes, + } + + return coreapi.ToDocumentsCreatePayload(coreAPIReq) +} + +// CreateInvoice creates an invoice. +func (s Service) CreateInvoice(ctx context.Context, req CreateInvoiceRequest) (documents.Model, jobs.JobID, error) { + docReq, err := convertInvRequest(req) + if err != nil { + return nil, jobs.NilJobID(), err + } + + return s.coreAPISrv.CreateDocument(ctx, docReq) +} + +// UpdateInvoice updates an invoice +func (s Service) UpdateInvoice(ctx context.Context, docID []byte, req CreateInvoiceRequest) (documents.Model, jobs.JobID, error) { + docReq, err := convertInvRequest(req) + if err != nil { + return nil, jobs.NilJobID(), err + } + + return s.coreAPISrv.UpdateDocument(ctx, documents.UpdatePayload{CreatePayload: docReq, DocumentID: docID}) +} + +// GetInvoice returns the latest version of the Invoice associated with Document ID. +func (s Service) GetInvoice(ctx context.Context, docID []byte) (documents.Model, error) { + return s.coreAPISrv.GetDocument(ctx, docID) +} + +// GetInvoiceVersion gets a specific version of the provided invoice document +func (s Service) GetInvoiceVersion(ctx context.Context, docID, versionID []byte) (documents.Model, error) { + return s.coreAPISrv.GetDocumentVersion(ctx, docID, versionID) +} + +func convertEntityRequest(req CreateEntityRequest) (documents.CreatePayload, error) { + coreAPIReq := coreapi.CreateDocumentRequest{ + Scheme: entity.Scheme, + WriteAccess: req.WriteAccess, + ReadAccess: req.ReadAccess, + Data: req.Data, + Attributes: req.Attributes, + } + + return coreapi.ToDocumentsCreatePayload(coreAPIReq) +} + +// CreateEntity creates Entity document and anchors it. +func (s Service) CreateEntity(ctx context.Context, req CreateEntityRequest) (documents.Model, jobs.JobID, error) { + docReq, err := convertEntityRequest(req) + if err != nil { + return nil, jobs.NilJobID(), err + } + + return s.coreAPISrv.CreateDocument(ctx, docReq) +} + +// UpdateEntity updates existing entity associated with docID with provided data and anchors it. +func (s Service) UpdateEntity(ctx context.Context, docID []byte, req CreateEntityRequest) (documents.Model, jobs.JobID, error) { + docReq, err := convertEntityRequest(req) + if err != nil { + return nil, jobs.NilJobID(), err + } + + return s.coreAPISrv.UpdateDocument(ctx, documents.UpdatePayload{ + DocumentID: docID, + CreatePayload: docReq, + }) +} + +// GetEntity returns the Entity associated with docID. +func (s Service) GetEntity(ctx context.Context, docID []byte) (documents.Model, error) { + return s.coreAPISrv.GetDocument(ctx, docID) +} + +// ShareEntity shares an entity relationship document with target identity. +func (s Service) ShareEntity(ctx context.Context, docID []byte, req ShareEntityRequest) (documents.Model, jobs.JobID, error) { + r, err := convertShareEntityRequest(ctx, docID, req.TargetIdentity) + if err != nil { + return nil, jobs.NilJobID(), err + } + + return s.coreAPISrv.CreateDocument(ctx, r) +} + +// RevokeRelationship revokes target_identity's access to entity. +func (s Service) RevokeRelationship(ctx context.Context, docID []byte, req ShareEntityRequest) (documents.Model, jobs.JobID, error) { + r, err := convertShareEntityRequest(ctx, docID, req.TargetIdentity) + if err != nil { + return nil, jobs.NilJobID(), err + } + + return s.coreAPISrv.UpdateDocument(ctx, documents.UpdatePayload{ + DocumentID: docID, + CreatePayload: r, + }) +} + +// GetEntityByRelationship returns an entity through a relationship ID. +func (s Service) GetEntityByRelationship(ctx context.Context, docID []byte) (documents.Model, error) { + return s.entitySrv.GetEntityByRelationship(ctx, docID) +} + +// MintNFT mints an NFT. +func (s Service) MintNFT(ctx context.Context, request nft.MintNFTRequest) (*nft.TokenResponse, error) { + return s.coreAPISrv.MintNFT(ctx, request) +} + +// TransferNFT transfers NFT with tokenID in a given registry to `to` address. +func (s Service) TransferNFT(ctx context.Context, to, registry common.Address, tokenID nft.TokenID) (*nft.TokenResponse, error) { + return s.coreAPISrv.TransferNFT(ctx, registry, to, tokenID) +} + +// OwnerOfNFT returns the owner of the NFT. +func (s Service) OwnerOfNFT(registry common.Address, tokenID nft.TokenID) (common.Address, error) { + return s.coreAPISrv.OwnerOfNFT(registry, tokenID) +} + +// MintInvoiceUnpaidNFT mints an NFT for an unpaid invoice document. +func (s Service) MintInvoiceUnpaidNFT(ctx context.Context, docID []byte, depositAddr common.Address) (*nft.TokenResponse, error) { + // Get proof fields + proofFields, err := getRequiredInvoiceUnpaidProofFields(ctx) + if err != nil { + return nil, err + } + + cfg, err := s.config.GetConfig() + if err != nil { + return nil, err + } + poRegistry := cfg.GetContractAddress(config.InvoiceUnpaidNFT) + + nreq := nft.MintNFTRequest{ + DocumentID: docID, + RegistryAddress: poRegistry, + DepositAddress: depositAddr, + ProofFields: proofFields, + GrantNFTReadAccess: true, + SubmitNFTReadAccessProof: true, + SubmitTokenProof: true, + } + + return s.coreAPISrv.MintNFT(ctx, nreq) +} + +// getRequiredInvoiceUnpaidProofFields returns required proof fields for an unpaid invoice mint +func getRequiredInvoiceUnpaidProofFields(ctx context.Context) ([]string, error) { + var proofFields []string + + acc, err := contextutil.Account(ctx) + if err != nil { + return nil, err + } + accDIDBytes := acc.GetIdentityID() + keys, err := acc.GetKeys() + if err != nil { + return nil, err + } + + signingRoot := fmt.Sprintf("%s.%s", documents.DRTreePrefix, documents.SigningRootField) + signerID := hexutil.Encode(append(accDIDBytes, keys[identity.KeyPurposeSigning.Name].PublicKey...)) + signatureSender := fmt.Sprintf("%s.signatures[%s].signature", documents.SignaturesTreePrefix, signerID) + proofFields = []string{"invoice.gross_amount", "invoice.currency", "invoice.date_due", "invoice.sender", "invoice.status", signingRoot, signatureSender, documents.CDTreePrefix + ".next_version"} + return proofFields, nil +} + +// CreateFundingAgreement creates a new funding agreement on a document and anchors the document. +func (s Service) CreateFundingAgreement(ctx context.Context, docID []byte, data *funding.Data) (documents.Model, jobs.JobID, error) { + return s.fundingSrv.CreateFundingAgreement(ctx, docID, data) +} diff --git a/httpapi/userapi/service_test.go b/httpapi/userapi/service_test.go index 39afa0c82..35a73ba8e 100644 --- a/httpapi/userapi/service_test.go +++ b/httpapi/userapi/service_test.go @@ -4,12 +4,25 @@ package userapi import ( "context" + "fmt" "testing" + "github.com/centrifuge/go-centrifuge/config" + "github.com/centrifuge/go-centrifuge/config/configstore" + "github.com/centrifuge/go-centrifuge/contextutil" "github.com/centrifuge/go-centrifuge/documents" + "github.com/centrifuge/go-centrifuge/documents/entity" + "github.com/centrifuge/go-centrifuge/documents/invoice" + "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/go-centrifuge/extensions/transferdetails" + "github.com/centrifuge/go-centrifuge/httpapi/coreapi" + "github.com/centrifuge/go-centrifuge/identity" "github.com/centrifuge/go-centrifuge/jobs" "github.com/centrifuge/go-centrifuge/testingutils/documents" + "github.com/centrifuge/go-centrifuge/testingutils/identity" + "github.com/centrifuge/go-centrifuge/utils" + "github.com/centrifuge/go-centrifuge/utils/byteutils" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -47,15 +60,19 @@ func (m *MockTransferService) DeriveTransferList(ctx context.Context, model docu return td, nm, args.Error(2) } +func newCoreAPIService(docSrv documents.Service) coreapi.Service { + return coreapi.NewService(docSrv, nil, nil, nil) +} + func TestService_CreateTransferDetail(t *testing.T) { transferSrv := new(MockTransferService) docSrv := new(testingdocuments.MockService) - service := Service{docSrv: docSrv, transferDetailsService: transferSrv} + service := Service{coreAPISrv: newCoreAPIService(docSrv), transferDetailsService: transferSrv} m := new(testingdocuments.MockModel) transferSrv.On("CreateTransferDetail", mock.Anything, mock.Anything).Return(m, jobs.NewJobID(), nil) nm, _, err := service.CreateTransferDetail(context.Background(), transferdetails.CreateTransferDetailRequest{ DocumentID: "test_id", - Data: transferdetails.TransferDetailData{}, + Data: transferdetails.Data{}, }) assert.NoError(t, err) assert.Equal(t, m, nm) @@ -64,13 +81,13 @@ func TestService_CreateTransferDetail(t *testing.T) { func TestService_UpdateDocument(t *testing.T) { transferSrv := new(MockTransferService) docSrv := new(testingdocuments.MockService) - service := Service{docSrv: docSrv, transferDetailsService: transferSrv} + service := Service{coreAPISrv: newCoreAPIService(docSrv), transferDetailsService: transferSrv} m := new(testingdocuments.MockModel) transferSrv.On("UpdateTransferDetail", mock.Anything, mock.Anything).Return(m, jobs.NewJobID(), nil) nm, _, err := service.UpdateTransferDetail(context.Background(), transferdetails.UpdateTransferDetailRequest{ DocumentID: "test_id", TransferID: "test_transfer", - Data: transferdetails.TransferDetailData{}, + Data: transferdetails.Data{}, }) assert.NoError(t, err) assert.Equal(t, m, nm) @@ -79,7 +96,7 @@ func TestService_UpdateDocument(t *testing.T) { func TestService_GetCurrentTransferDetail(t *testing.T) { transferSrv := new(MockTransferService) docSrv := new(testingdocuments.MockService) - service := Service{docSrv: docSrv, transferDetailsService: transferSrv} + service := Service{coreAPISrv: newCoreAPIService(docSrv), transferDetailsService: transferSrv} m := new(testingdocuments.MockModel) td := new(transferdetails.TransferDetail) docSrv.On("GetCurrentVersion", mock.Anything, mock.Anything).Return(m, nil) @@ -93,7 +110,7 @@ func TestService_GetCurrentTransferDetail(t *testing.T) { func TestService_GetCurrentTransferDetailList(t *testing.T) { transferSrv := new(MockTransferService) docSrv := new(testingdocuments.MockService) - service := Service{docSrv: docSrv, transferDetailsService: transferSrv} + service := Service{coreAPISrv: newCoreAPIService(docSrv), transferDetailsService: transferSrv} m := new(testingdocuments.MockModel) td := new(transferdetails.TransferDetailList) docSrv.On("GetCurrentVersion", mock.Anything, mock.Anything).Return(m, nil) @@ -107,7 +124,7 @@ func TestService_GetCurrentTransferDetailList(t *testing.T) { func TestService_GetVersionTransferDetail(t *testing.T) { transferSrv := new(MockTransferService) docSrv := new(testingdocuments.MockService) - service := Service{docSrv: docSrv, transferDetailsService: transferSrv} + service := Service{coreAPISrv: newCoreAPIService(docSrv), transferDetailsService: transferSrv} m := new(testingdocuments.MockModel) td := new(transferdetails.TransferDetail) docSrv.On("GetVersion", mock.Anything, mock.Anything, mock.Anything).Return(m, nil) @@ -121,7 +138,7 @@ func TestService_GetVersionTransferDetail(t *testing.T) { func TestService_GetVersionTransferDetailList(t *testing.T) { transferSrv := new(MockTransferService) docSrv := new(testingdocuments.MockService) - service := Service{docSrv: docSrv, transferDetailsService: transferSrv} + service := Service{coreAPISrv: newCoreAPIService(docSrv), transferDetailsService: transferSrv} m := new(testingdocuments.MockModel) td := new(transferdetails.TransferDetailList) docSrv.On("GetVersion", mock.Anything, mock.Anything, mock.Anything).Return(m, nil) @@ -131,3 +148,288 @@ func TestService_GetVersionTransferDetailList(t *testing.T) { assert.Equal(t, td, ntd) assert.Equal(t, m, nm) } + +func TestService_CreateEntity(t *testing.T) { + ctx := context.Background() + did := testingidentity.GenerateRandomDID() + req := CreateEntityRequest{ + WriteAccess: []identity.DID{did}, + Data: entity.Data{ + Identity: &did, + LegalName: "John Doe", + Addresses: []entity.Address{ + { + IsMain: true, + Country: "Germany", + Label: "home", + }, + }, + }, + Attributes: coreapi.AttributeMapRequest{ + "string_test": { + Type: "invalid", + Value: "hello, world!", + }, + + "decimal_test": { + Type: "decimal", + Value: "100001.001", + }, + }, + } + s := Service{} + + // invalid attribute map + _, _, err := s.CreateEntity(ctx, req) + assert.Error(t, err) + assert.True(t, errors.IsOfType(documents.ErrNotValidAttrType, err)) + + // success + docSrv := new(testingdocuments.MockService) + m := new(testingdocuments.MockModel) + docSrv.On("CreateModel", ctx, mock.Anything).Return(m, jobs.NewJobID(), nil) + s.coreAPISrv = newCoreAPIService(docSrv) + strAttr := req.Attributes["string_test"] + strAttr.Type = "string" + req.Attributes["string_test"] = strAttr + _, _, err = s.CreateEntity(ctx, req) + assert.NoError(t, err) + docSrv.AssertExpectations(t) +} + +func TestService_UpdateEntity(t *testing.T) { + ctx := context.Background() + did := testingidentity.GenerateRandomDID() + req := CreateEntityRequest{ + WriteAccess: []identity.DID{did}, + Data: entity.Data{ + Identity: &did, + LegalName: "John Doe", + Addresses: []entity.Address{ + { + IsMain: true, + Country: "Germany", + Label: "home", + }, + }, + }, + Attributes: coreapi.AttributeMapRequest{ + "string_test": { + Type: "invalid", + Value: "hello, world!", + }, + + "decimal_test": { + Type: "decimal", + Value: "100001.001", + }, + }, + } + s := Service{} + + docID := utils.RandomSlice(32) + + // invalid attribute map + _, _, err := s.UpdateEntity(ctx, docID, req) + assert.Error(t, err) + assert.True(t, errors.IsOfType(documents.ErrNotValidAttrType, err)) + + // success + docSrv := new(testingdocuments.MockService) + m := new(testingdocuments.MockModel) + docSrv.On("UpdateModel", ctx, mock.Anything).Return(m, jobs.NewJobID(), nil) + s.coreAPISrv = newCoreAPIService(docSrv) + strAttr := req.Attributes["string_test"] + strAttr.Type = "string" + req.Attributes["string_test"] = strAttr + _, _, err = s.UpdateEntity(ctx, docID, req) + assert.NoError(t, err) + docSrv.AssertExpectations(t) +} + +func TestService_CreateInvoice(t *testing.T) { + ctx := context.Background() + did := testingidentity.GenerateRandomDID() + req := CreateInvoiceRequest{ + WriteAccess: []identity.DID{did}, + Data: invoice.Data{ + Number: "12345", + Status: "unpaid", + Recipient: &did, + Currency: "EUR", + Attachments: []*documents.BinaryAttachment{ + { + Name: "test", + FileType: "pdf", + Size: 1000202, + Data: byteutils.HexBytes(utils.RandomSlice(32)), + Checksum: byteutils.HexBytes(utils.RandomSlice(32)), + }, + }, + }, + Attributes: coreapi.AttributeMapRequest{ + "string_test": { + Type: "invalid", + Value: "hello, world!", + }, + + "decimal_test": { + Type: "decimal", + Value: "100001.001", + }, + }, + } + s := Service{} + + // invalid attribute map + _, _, err := s.CreateInvoice(ctx, req) + assert.Error(t, err) + assert.True(t, errors.IsOfType(documents.ErrNotValidAttrType, err)) + + // success + docSrv := new(testingdocuments.MockService) + m := new(testingdocuments.MockModel) + docSrv.On("CreateModel", ctx, mock.Anything).Return(m, jobs.NewJobID(), nil) + s.coreAPISrv = newCoreAPIService(docSrv) + strAttr := req.Attributes["string_test"] + strAttr.Type = "string" + req.Attributes["string_test"] = strAttr + _, _, err = s.CreateInvoice(ctx, req) + assert.NoError(t, err) + docSrv.AssertExpectations(t) +} + +func TestService_UpdateInvoice(t *testing.T) { + ctx := context.Background() + did := testingidentity.GenerateRandomDID() + req := CreateInvoiceRequest{ + WriteAccess: []identity.DID{did}, + Data: invoice.Data{ + Number: "12345", + Status: "unpaid", + Recipient: &did, + Currency: "EUR", + Attachments: []*documents.BinaryAttachment{ + { + Name: "test", + FileType: "pdf", + Size: 1000202, + Data: byteutils.HexBytes(utils.RandomSlice(32)), + Checksum: byteutils.HexBytes(utils.RandomSlice(32)), + }, + }, + }, + Attributes: coreapi.AttributeMapRequest{ + "string_test": { + Type: "invalid", + Value: "hello, world!", + }, + + "decimal_test": { + Type: "decimal", + Value: "100001.001", + }, + }, + } + s := Service{} + docID := utils.RandomSlice(32) + + // invalid attribute map + _, _, err := s.UpdateInvoice(ctx, docID, req) + assert.Error(t, err) + assert.True(t, errors.IsOfType(documents.ErrNotValidAttrType, err)) + + // success + docSrv := new(testingdocuments.MockService) + m := new(testingdocuments.MockModel) + docSrv.On("UpdateModel", ctx, mock.Anything).Return(m, jobs.NewJobID(), nil) + s.coreAPISrv = newCoreAPIService(docSrv) + strAttr := req.Attributes["string_test"] + strAttr.Type = "string" + req.Attributes["string_test"] = strAttr + _, _, err = s.UpdateInvoice(ctx, docID, req) + assert.NoError(t, err) + docSrv.AssertExpectations(t) +} + +func TestService_ShareEntity(t *testing.T) { + // failed to convert + ctx := context.Background() + s := Service{} + _, _, err := s.ShareEntity(ctx, nil, ShareEntityRequest{}) + assert.Error(t, err) + + // success + docSrv := new(testingdocuments.MockService) + m := new(testingdocuments.MockModel) + s.coreAPISrv = newCoreAPIService(docSrv) + did := testingidentity.GenerateRandomDID() + did1 := testingidentity.GenerateRandomDID() + ctx = context.WithValue(ctx, config.AccountHeaderKey, did.String()) + docSrv.On("CreateModel", ctx, mock.Anything).Return(m, jobs.NewJobID(), nil).Once() + docID := byteutils.HexBytes(utils.RandomSlice(32)) + m1, _, err := s.ShareEntity(ctx, docID, ShareEntityRequest{TargetIdentity: did1}) + assert.NoError(t, err) + assert.Equal(t, m, m1) + m.AssertExpectations(t) + docSrv.AssertExpectations(t) +} + +func TestService_RevokeRelationship(t *testing.T) { + // failed to convert + ctx := context.Background() + s := Service{} + _, _, err := s.RevokeRelationship(ctx, nil, ShareEntityRequest{}) + assert.Error(t, err) + + // success + docSrv := new(testingdocuments.MockService) + m := new(testingdocuments.MockModel) + s.coreAPISrv = newCoreAPIService(docSrv) + did := testingidentity.GenerateRandomDID() + did1 := testingidentity.GenerateRandomDID() + ctx = context.WithValue(ctx, config.AccountHeaderKey, did.String()) + docSrv.On("UpdateModel", ctx, mock.Anything).Return(m, jobs.NewJobID(), nil).Once() + docID := byteutils.HexBytes(utils.RandomSlice(32)) + m1, _, err := s.RevokeRelationship(ctx, docID, ShareEntityRequest{TargetIdentity: did1}) + assert.NoError(t, err) + assert.Equal(t, m, m1) + m.AssertExpectations(t) + docSrv.AssertExpectations(t) +} + +func TestService_GetRequiredInvoiceUnpaidProofFields(t *testing.T) { + //missing account in context + ctxh := context.Background() + proofList, err := getRequiredInvoiceUnpaidProofFields(ctxh) + assert.Error(t, err) + assert.Nil(t, proofList) + + //error identity keys + tc, err := configstore.NewAccount("main", cfg) + assert.Nil(t, err) + acc := tc.(*configstore.Account) + acc.EthereumAccount = &config.AccountConfig{ + Key: "blabla", + } + ctxh, err = contextutil.New(ctxh, acc) + assert.Nil(t, err) + proofList, err = getRequiredInvoiceUnpaidProofFields(ctxh) + assert.Error(t, err) + assert.Nil(t, proofList) + + //success assertions + tc, err = configstore.NewAccount("main", cfg) + assert.Nil(t, err) + ctxh, err = contextutil.New(ctxh, tc) + assert.Nil(t, err) + proofList, err = getRequiredInvoiceUnpaidProofFields(ctxh) + assert.NoError(t, err) + assert.Len(t, proofList, 8) + accDIDBytes := tc.GetIdentityID() + keys, err := tc.GetKeys() + assert.NoError(t, err) + signerID := hexutil.Encode(append(accDIDBytes, keys[identity.KeyPurposeSigning.Name].PublicKey...)) + signatureSender := fmt.Sprintf("%s.signatures[%s].signature", documents.SignaturesTreePrefix, signerID) + assert.Equal(t, signatureSender, proofList[6]) +} diff --git a/documents/generic/test_bootstrapper.go b/httpapi/userapi/test_boostrapper.go similarity index 79% rename from documents/generic/test_bootstrapper.go rename to httpapi/userapi/test_boostrapper.go index 33dac2995..d14b01f5a 100644 --- a/documents/generic/test_bootstrapper.go +++ b/httpapi/userapi/test_boostrapper.go @@ -1,6 +1,6 @@ -// +build integration unit +// +build unit integration -package generic +package userapi func (b Bootstrapper) TestBootstrap(context map[string]interface{}) error { return b.Bootstrap(context) diff --git a/httpapi/userapi/transfer_details_api.go b/httpapi/userapi/transfer_details_api.go index 2a13da0c9..bec6794f6 100644 --- a/httpapi/userapi/transfer_details_api.go +++ b/httpapi/userapi/transfer_details_api.go @@ -27,7 +27,7 @@ var log = logging.Logger("user-api") // @summary Creates a new transfer detail extension on a document and anchors it. // @description Creates a new transfer detail extension on a document and anchors it. // @id create_transfer_detail -// @tags TransferDetail +// @tags Transfer Details // @accept json // @param authorization header string true "Hex encoded centrifuge ID of the account for the intended API action" // @param body body userapi.CreateTransferDetailRequest true "Transfer Detail Create Request" @@ -36,7 +36,7 @@ var log = logging.Logger("user-api") // @Failure 500 {object} httputils.HTTPError // @Failure 400 {object} httputils.HTTPError // @Failure 403 {object} httputils.HTTPError -// @success 201 {object} userapi.TransferDetailResponse +// @success 202 {object} userapi.TransferDetailResponse // @router /v1/documents/{document_id}/transfer_details [post] func (h handler) CreateTransferDetail(w http.ResponseWriter, r *http.Request) { var err error @@ -62,7 +62,7 @@ func (h handler) CreateTransferDetail(w http.ResponseWriter, r *http.Request) { if request.Data.TransferID == "" { request.Data.TransferID = extensions.NewAttributeSetID() } - request.DocumentID = chi.URLParam(r, documentIDParam) + request.DocumentID = chi.URLParam(r, coreapi.DocumentIDParam) payload, err := toTransferDetailCreatePayload(request) if err != nil { @@ -90,7 +90,7 @@ func (h handler) CreateTransferDetail(w http.ResponseWriter, r *http.Request) { Data: payload.Data, } - render.Status(r, http.StatusCreated) + render.Status(r, http.StatusAccepted) render.JSON(w, r, resp) } @@ -98,7 +98,7 @@ func (h handler) CreateTransferDetail(w http.ResponseWriter, r *http.Request) { // @summary Updates a new transfer detail extension on a document and anchors it. // @description Updates a new transfer detail extension on a document and anchors it. // @id update_transfer_detail -// @tags TransferDetail +// @tags Transfer Details // @accept json // @param authorization header string true "Hex encoded centrifuge ID of the account for the intended API action" // @param body body userapi.UpdateTransferDetailRequest true "Transfer Detail Update Request" @@ -108,7 +108,7 @@ func (h handler) CreateTransferDetail(w http.ResponseWriter, r *http.Request) { // @Failure 500 {object} httputils.HTTPError // @Failure 400 {object} httputils.HTTPError // @Failure 403 {object} httputils.HTTPError -// @success 201 {object} userapi.TransferDetailResponse +// @success 202 {object} userapi.TransferDetailResponse // @router /v1/documents/{document_id}/transfer_details/{transfer_id} [put] func (h handler) UpdateTransferDetail(w http.ResponseWriter, r *http.Request) { var err error @@ -132,7 +132,7 @@ func (h handler) UpdateTransferDetail(w http.ResponseWriter, r *http.Request) { } request.TransferID = chi.URLParam(r, transferIDParam) - request.DocumentID = chi.URLParam(r, documentIDParam) + request.DocumentID = chi.URLParam(r, coreapi.DocumentIDParam) payload, err := toTransferDetailUpdatePayload(request) if err != nil { code = http.StatusBadRequest @@ -159,19 +159,20 @@ func (h handler) UpdateTransferDetail(w http.ResponseWriter, r *http.Request) { Data: payload.Data, } - render.Status(r, http.StatusCreated) + render.Status(r, http.StatusAccepted) render.JSON(w, r, resp) } // GetTransferDetail returns the latest version of transfer detail. // @summary Returns the latest version of the transfer detail. // @description Returns the latest version of the transfer detail. -// @id get_transfer -// @tags Documents +// @id get_transfer_detail +// @tags Transfer Details // @param authorization header string true "Hex encoded centrifuge ID of the account for the intended API action" // @param document_id path string true "Document Identifier" // @param transfer_id path string true "Transfer Detail Identifier" // @produce json +// @Failure 403 {object} httputils.HTTPError // @Failure 400 {object} httputils.HTTPError // @Failure 404 {object} httputils.HTTPError // @Failure 500 {object} httputils.HTTPError @@ -182,7 +183,7 @@ func (h handler) GetTransferDetail(w http.ResponseWriter, r *http.Request) { var code int defer httputils.RespondIfError(&code, &err, w, r) - docID, err := hexutil.Decode(chi.URLParam(r, documentIDParam)) + docID, err := hexutil.Decode(chi.URLParam(r, coreapi.DocumentIDParam)) if err != nil { code = http.StatusBadRequest log.Error(err) @@ -225,11 +226,12 @@ func (h handler) GetTransferDetail(w http.ResponseWriter, r *http.Request) { // GetTransferDetailList returns a list of the latest versions all transfer details on the document. // @summary Returns a list of the latest versions of all transfer details on the document. // @description Returns a list of the latest versions of all transfer details on the document. -// @id get_transfer -// @tags Documents +// @id list_transfer_details +// @tags Transfer Details // @param authorization header string true "Hex encoded centrifuge ID of the account for the intended API action" // @param document_id path string true "Document Identifier" // @produce json +// @Failure 403 {object} httputils.HTTPError // @Failure 400 {object} httputils.HTTPError // @Failure 404 {object} httputils.HTTPError // @Failure 500 {object} httputils.HTTPError @@ -240,7 +242,7 @@ func (h handler) GetTransferDetailList(w http.ResponseWriter, r *http.Request) { var code int defer httputils.RespondIfError(&code, &err, w, r) - docID, err := hexutil.Decode(chi.URLParam(r, documentIDParam)) + docID, err := hexutil.Decode(chi.URLParam(r, coreapi.DocumentIDParam)) if err != nil { code = http.StatusBadRequest log.Error(err) diff --git a/httpapi/userapi/transfer_details_api_test.go b/httpapi/userapi/transfer_details_api_test.go index 22705140e..3bd49e2d4 100644 --- a/httpapi/userapi/transfer_details_api_test.go +++ b/httpapi/userapi/transfer_details_api_test.go @@ -28,7 +28,7 @@ import ( func TestHandler_CreateTransferDetail(t *testing.T) { docSrv := new(testingdocuments.MockService) transferSrv := new(MockTransferService) - srv := Service{docSrv: docSrv, transferDetailsService: transferSrv} + srv := Service{coreAPISrv: newCoreAPIService(docSrv), transferDetailsService: transferSrv} h := handler{srv: srv} docID := hexutil.Encode(utils.RandomSlice(32)) @@ -75,17 +75,17 @@ func TestHandler_CreateTransferDetail(t *testing.T) { w, r = getHTTPReqAndResp(ctx, bytes.NewReader(d)) transferSrv = new(MockTransferService) - srv = Service{docSrv: docSrv, transferDetailsService: transferSrv} + srv = Service{coreAPISrv: newCoreAPIService(docSrv), transferDetailsService: transferSrv} h = handler{srv: srv} transferSrv.On("CreateTransferDetail", mock.Anything, mock.Anything).Return(m, jobs.NewJobID(), nil) h.CreateTransferDetail(w, r) - assert.Equal(t, http.StatusCreated, w.Code) + assert.Equal(t, http.StatusAccepted, w.Code) } func TestHandler_GetTransferDetail(t *testing.T) { docSrv := new(testingdocuments.MockService) transferSrv := new(MockTransferService) - srv := Service{docSrv: docSrv, transferDetailsService: transferSrv} + srv := Service{coreAPISrv: newCoreAPIService(docSrv), transferDetailsService: transferSrv} h := handler{srv: srv} data := map[string]interface{}{ @@ -126,7 +126,7 @@ func TestHandler_GetTransferDetail(t *testing.T) { rctx.URLParams.Values[0] = hexutil.Encode(id) docSrv = new(testingdocuments.MockService) docSrv.On("GetCurrentVersion", id).Return(nil, errors.New("document not found")) - srv = Service{docSrv: docSrv, transferDetailsService: transferSrv} + srv = Service{coreAPISrv: newCoreAPIService(docSrv), transferDetailsService: transferSrv} h = handler{srv: srv} ctx = context.WithValue(context.Background(), chi.RouteCtxKey, rctx) w, r = getHTTPReqAndResp(ctx) @@ -149,14 +149,14 @@ func TestHandler_GetTransferDetail(t *testing.T) { w, r = getHTTPReqAndResp(ctx) d := &transferdetails.TransferDetail{ - Data: transferdetails.TransferDetailData{ + Data: transferdetails.Data{ TransferID: transferID, }, } docSrv = new(testingdocuments.MockService) docSrv.On("GetCurrentVersion", id).Return(m, nil) transferSrv = new(MockTransferService) - srv = Service{docSrv: docSrv, transferDetailsService: transferSrv} + srv = Service{coreAPISrv: newCoreAPIService(docSrv), transferDetailsService: transferSrv} h = handler{srv: srv} transferSrv.On("DeriveTransferDetail", mock.Anything, m, mock.Anything).Return(d, m, nil) h.GetTransferDetail(w, r) @@ -166,7 +166,7 @@ func TestHandler_GetTransferDetail(t *testing.T) { func TestHandler_UpdateTransferDetail(t *testing.T) { docSrv := new(testingdocuments.MockService) transferSrv := new(MockTransferService) - srv := Service{docSrv: docSrv, transferDetailsService: transferSrv} + srv := Service{coreAPISrv: newCoreAPIService(docSrv), transferDetailsService: transferSrv} h := handler{srv: srv} docID := hexutil.Encode(utils.RandomSlice(32)) @@ -213,9 +213,9 @@ func TestHandler_UpdateTransferDetail(t *testing.T) { w, r = getHTTPReqAndResp(ctx, bytes.NewReader(d)) transferSrv = new(MockTransferService) - srv = Service{docSrv: docSrv, transferDetailsService: transferSrv} + srv = Service{coreAPISrv: newCoreAPIService(docSrv), transferDetailsService: transferSrv} h = handler{srv: srv} transferSrv.On("UpdateTransferDetail", mock.Anything, mock.Anything).Return(m, jobs.NewJobID(), nil) h.UpdateTransferDetail(w, r) - assert.Equal(t, http.StatusCreated, w.Code) + assert.Equal(t, http.StatusAccepted, w.Code) } diff --git a/httpapi/userapi/types.go b/httpapi/userapi/types.go index 11bcf6b87..14ce0bc73 100644 --- a/httpapi/userapi/types.go +++ b/httpapi/userapi/types.go @@ -2,35 +2,67 @@ package userapi import ( + "context" + "encoding/json" + + "github.com/centrifuge/go-centrifuge/contextutil" + "github.com/centrifuge/go-centrifuge/documents" + "github.com/centrifuge/go-centrifuge/documents/entity" + "github.com/centrifuge/go-centrifuge/documents/entityrelationship" + "github.com/centrifuge/go-centrifuge/documents/invoice" + "github.com/centrifuge/go-centrifuge/errors" + "github.com/centrifuge/go-centrifuge/extensions" + "github.com/centrifuge/go-centrifuge/extensions/funding" "github.com/centrifuge/go-centrifuge/extensions/transferdetails" "github.com/centrifuge/go-centrifuge/httpapi/coreapi" + "github.com/centrifuge/go-centrifuge/identity" + "github.com/centrifuge/go-centrifuge/jobs" + "github.com/centrifuge/go-centrifuge/nft" + "github.com/centrifuge/go-centrifuge/utils/byteutils" + "github.com/ethereum/go-ethereum/common" ) // TODO: think: generic custom attribute set creation? //CreateTransferDetailRequest is the request body for creating a Transfer Detail type CreateTransferDetailRequest struct { - DocumentID string `json:"document_id"` - Data transferdetails.TransferDetailData `json:"data"` + DocumentID string `json:"document_id"` + Data transferdetails.Data `json:"data"` } // UpdateTransferDetailRequest is the request body for updating a Transfer Detail type UpdateTransferDetailRequest struct { - DocumentID string `json:"document_id"` - TransferID string `json:"transfer_id"` - Data transferdetails.TransferDetailData `json:"data"` + DocumentID string `json:"document_id"` + TransferID string `json:"transfer_id"` + Data transferdetails.Data `json:"data"` } // TransferDetailResponse is the response body when fetching a Transfer Detail type TransferDetailResponse struct { - Header coreapi.ResponseHeader `json:"header"` - Data transferdetails.TransferDetailData `json:"data"` + Header coreapi.ResponseHeader `json:"header"` + Data transferdetails.Data `json:"data"` } // TransferDetailListResponse is the response body when fetching a list of Transfer Details type TransferDetailListResponse struct { - Header coreapi.ResponseHeader `json:"header"` - Data []transferdetails.TransferDetailData `json:"data"` + Header coreapi.ResponseHeader `json:"header"` + Data []transferdetails.Data `json:"data"` +} + +// NFTMintInvoiceUnpaidRequest is the request for minting an NFT for an unpaid NFT +type NFTMintInvoiceUnpaidRequest struct { + // Deposit address for NFT Token created + DepositAddress common.Address `json:"deposit_address" swaggertype:"primitive,string"` +} + +// ResponseHeader header with job id +type ResponseHeader struct { + JobID string `json:"job_id"` +} + +// NFTMintResponse is response from user api NFT minting +type NFTMintResponse struct { + Header *ResponseHeader `json:"header"` } func toTransferDetailCreatePayload(request CreateTransferDetailRequest) (*transferdetails.CreateTransferDetailRequest, error) { @@ -49,3 +81,324 @@ func toTransferDetailUpdatePayload(request UpdateTransferDetailRequest) (*transf } return &payload, nil } + +// CreateInvoiceRequest defines the payload for creating documents. +type CreateInvoiceRequest struct { + ReadAccess []identity.DID `json:"read_access" swaggertype:"array,string"` + WriteAccess []identity.DID `json:"write_access" swaggertype:"array,string"` + Data invoice.Data `json:"data"` + Attributes coreapi.AttributeMapRequest `json:"attributes"` +} + +// InvoiceResponse represents the invoice in client API format. +type InvoiceResponse struct { + Header coreapi.ResponseHeader `json:"header"` + Data invoice.Data `json:"data"` + Attributes coreapi.AttributeMapResponse `json:"attributes"` +} + +func toInvoiceResponse(model documents.Model, tokenRegistry documents.TokenRegistry, jobID jobs.JobID) (resp InvoiceResponse, err error) { + docResp, err := coreapi.GetDocumentResponse(model, tokenRegistry, jobID) + if err != nil { + return resp, err + } + + return InvoiceResponse{ + Header: docResp.Header, + Attributes: docResp.Attributes, + Data: docResp.Data.(invoice.Data), + }, nil +} + +// CreateEntityRequest holds details for creating Entity Document. +type CreateEntityRequest struct { + ReadAccess []identity.DID `json:"read_access" swaggertype:"array,string"` + WriteAccess []identity.DID `json:"write_access" swaggertype:"array,string"` + Data entity.Data `json:"data"` + Attributes coreapi.AttributeMapRequest `json:"attributes"` +} + +// EntityResponse represents the entity in client API format. +type EntityResponse struct { + Header coreapi.ResponseHeader `json:"header"` + Data EntityDataResponse `json:"data"` + Attributes coreapi.AttributeMapResponse `json:"attributes"` +} + +// EntityDataResponse holds the entity data and Relationships +type EntityDataResponse struct { + Entity entity.Data `json:"entity"` + Relationships []Relationship `json:"relationships"` +} + +// Relationship holds the identity and status of the relationship +type Relationship struct { + TargetIdentity identity.DID `json:"target_identity" swaggertype:"primitive,string"` + OwnerIdentity identity.DID `json:"owner_identity" swaggertype:"primitive,string"` + EntityIdentifier byteutils.HexBytes `json:"entity_identifier" swaggertype:"primitive,string"` + Active bool `json:"active"` +} + +// ShareEntityRequest holds the documentID and target identity to share entity with. +type ShareEntityRequest struct { + TargetIdentity identity.DID `json:"target_identity" swaggertype:"primitive,string"` +} + +// ShareEntityResponse holds the response for entity share. +type ShareEntityResponse struct { + Header coreapi.ResponseHeader `json:"header"` + Relationship Relationship `json:"relationship"` +} + +func toEntityShareResponse(model documents.Model, tokenRegistry documents.TokenRegistry, jobID jobs.JobID) (resp ShareEntityResponse, err error) { + header, err := coreapi.DeriveResponseHeader(tokenRegistry, model, jobID) + if err != nil { + return resp, err + } + + d := model.GetData().(entityrelationship.Data) + return ShareEntityResponse{ + Header: header, + Relationship: Relationship{ + TargetIdentity: *d.TargetIdentity, + EntityIdentifier: d.EntityIdentifier, + OwnerIdentity: *d.OwnerIdentity, + Active: true, + }, + }, nil +} + +func convertShareEntityRequest(ctx context.Context, docID byteutils.HexBytes, targetID identity.DID) (req documents.CreatePayload, err error) { + self, err := contextutil.DIDFromContext(ctx) + if err != nil { + return req, err + } + + d, err := json.Marshal(entityrelationship.Data{ + TargetIdentity: &targetID, + OwnerIdentity: &self, + EntityIdentifier: docID, + }) + if err != nil { + return req, err + } + + return documents.CreatePayload{ + Scheme: entityrelationship.Scheme, + Data: d, + }, nil +} + +func getEntityRelationships(ctx context.Context, erSrv entityrelationship.Service, entity documents.Model) (relationships []Relationship, err error) { + selfDID, err := contextutil.DIDFromContext(ctx) + if err != nil { + return nil, errors.New("failed to get self ID") + } + + isCollaborator, err := entity.IsDIDCollaborator(selfDID) + if err != nil { + return nil, err + } + + if !isCollaborator { + return nil, nil + } + + rs, err := erSrv.GetEntityRelationships(ctx, entity.ID()) + if err != nil { + return nil, err + } + + //list the relationships associated with the entity + for _, r := range rs { + tokens, err := r.GetAccessTokens() + if err != nil { + return nil, err + } + + d := r.GetData().(entityrelationship.Data) + relationships = append(relationships, Relationship{ + TargetIdentity: *d.TargetIdentity, + OwnerIdentity: *d.OwnerIdentity, + EntityIdentifier: d.EntityIdentifier, + Active: len(tokens) != 0, + }) + } + + return relationships, nil +} + +func toEntityResponse(ctx context.Context, erSrv entityrelationship.Service, model documents.Model, tokenRegistry documents.TokenRegistry, jobID jobs.JobID) (resp EntityResponse, err error) { + docResp, err := coreapi.GetDocumentResponse(model, tokenRegistry, jobID) + if err != nil { + return resp, err + } + + rs, err := getEntityRelationships(ctx, erSrv, model) + if err != nil { + return resp, err + } + + return EntityResponse{ + Header: docResp.Header, + Attributes: docResp.Attributes, + Data: EntityDataResponse{ + Entity: docResp.Data.(entity.Data), + Relationships: rs, + }, + }, nil +} + +// FundingRequest is the request payload for funding operations. +type FundingRequest struct { + Data funding.Data `json:"data"` +} + +// FundingDataResponse holds funding data and the signatures. +type FundingDataResponse struct { + Funding funding.Data `json:"funding"` + Signatures []funding.Signature `json:"signatures"` +} + +// FundingResponse holds the response for funding operations. +type FundingResponse struct { + Header coreapi.ResponseHeader `json:"header"` + Data FundingDataResponse `json:"data"` +} + +// FundingListResponse holds the response for funding agreements. +type FundingListResponse struct { + Header coreapi.ResponseHeader `json:"header"` + Data []FundingDataResponse `json:"data"` +} + +func toFundingAgreementResponse( + ctx context.Context, + fundingSrv funding.Service, + doc documents.Model, + fundingID string, + tokenRegistry documents.TokenRegistry, + jobID jobs.JobID) (resp FundingResponse, err error) { + + header, err := coreapi.DeriveResponseHeader(tokenRegistry, doc, jobID) + if err != nil { + return resp, err + } + + data, sigs, err := fundingSrv.GetDataAndSignatures(ctx, doc, fundingID, "") + if err != nil { + return resp, err + } + + return FundingResponse{ + Header: header, + Data: FundingDataResponse{ + Funding: data, + Signatures: sigs, + }, + }, nil +} + +func toFundingAgreementListResponse(ctx context.Context, + fundingSrv funding.Service, + doc documents.Model, + tokenRegistry documents.TokenRegistry) (resp FundingListResponse, err error) { + + header, err := coreapi.DeriveResponseHeader(tokenRegistry, doc, jobs.NilJobID()) + if err != nil { + return resp, err + } + resp.Header = header + + fl, err := documents.AttrKeyFromLabel(funding.AttrFundingLabel) + if err != nil { + return resp, err + } + + if !doc.AttributeExists(fl) { + return resp, nil + } + + lastIdx, err := extensions.GetArrayLatestIDX(doc, funding.AttrFundingLabel) + if err != nil { + return resp, err + } + + i, err := documents.NewInt256("0") + if err != nil { + return resp, err + } + + for i.Cmp(lastIdx) != 1 { + data, sigs, err := fundingSrv.GetDataAndSignatures(ctx, doc, "", i.String()) + if err != nil { + return resp, err + } + + resp.Data = append(resp.Data, FundingDataResponse{ + Funding: data, + Signatures: sigs, + }) + + i, err = i.Inc() + if err != nil { + return resp, err + } + } + + return resp, nil +} + +// MintNFTRequest holds required fields for minting NFT +type MintNFTRequest struct { + DocumentID byteutils.HexBytes `json:"document_id" swaggertype:"primitive,string"` + DepositAddress common.Address `json:"deposit_address" swaggertype:"primitive,string"` + ProofFields []string `json:"proof_fields"` +} + +// NFTResponseHeader holds the NFT mint job ID. +type NFTResponseHeader struct { + JobID string `json:"job_id"` +} + +// MintNFTResponse holds the details of the minted NFT. +type MintNFTResponse struct { + Header NFTResponseHeader `json:"header"` + DocumentID byteutils.HexBytes `json:"document_id" swaggertype:"primitive,string"` + TokenID string `json:"token_id"` + RegistryAddress common.Address `json:"registry_address" swaggertype:"primitive,string"` + DepositAddress common.Address `json:"deposit_address" swaggertype:"primitive,string"` +} + +func toNFTMintRequest(req MintNFTRequest, registryAddress common.Address) nft.MintNFTRequest { + return nft.MintNFTRequest{ + DocumentID: req.DocumentID, + DepositAddress: req.DepositAddress, + GrantNFTReadAccess: false, + ProofFields: req.ProofFields, + RegistryAddress: registryAddress, + SubmitNFTReadAccessProof: false, + SubmitTokenProof: true, + UseGeneric: true, + } +} + +// TransferNFTRequest holds Registry Address and To address for NFT transfer +type TransferNFTRequest struct { + To common.Address `json:"to" swaggertype:"primitive,string"` +} + +// TransferNFTResponse is the response for NFT transfer. +type TransferNFTResponse struct { + Header NFTResponseHeader `json:"header"` + TokenID string `json:"token_id"` + RegistryAddress common.Address `json:"registry_address" swaggertype:"primitive,string"` + To common.Address `json:"to" swaggertype:"primitive,string"` +} + +// NFTOwnerResponse is the response for NFT owner request. +type NFTOwnerResponse struct { + TokenID string `json:"token_id"` + RegistryAddress common.Address `json:"registry_address" swaggertype:"primitive,string"` + Owner common.Address `json:"owner" swaggertype:"primitive,string"` +} diff --git a/httpapi/userapi/types_test.go b/httpapi/userapi/types_test.go index 53711f413..59c1dda31 100644 --- a/httpapi/userapi/types_test.go +++ b/httpapi/userapi/types_test.go @@ -3,16 +3,35 @@ package userapi import ( + "context" + "encoding/json" "testing" + coredocumentpb "github.com/centrifuge/centrifuge-protobufs/gen/go/coredocument" + "github.com/centrifuge/go-centrifuge/config" + "github.com/centrifuge/go-centrifuge/documents" + "github.com/centrifuge/go-centrifuge/documents/entity" + "github.com/centrifuge/go-centrifuge/documents/entityrelationship" + "github.com/centrifuge/go-centrifuge/documents/invoice" + "github.com/centrifuge/go-centrifuge/errors" + "github.com/centrifuge/go-centrifuge/extensions" + "github.com/centrifuge/go-centrifuge/extensions/funding" "github.com/centrifuge/go-centrifuge/extensions/transferdetails" + "github.com/centrifuge/go-centrifuge/identity" + "github.com/centrifuge/go-centrifuge/jobs" + testingconfig "github.com/centrifuge/go-centrifuge/testingutils/config" + testingdocuments "github.com/centrifuge/go-centrifuge/testingutils/documents" + testingidentity "github.com/centrifuge/go-centrifuge/testingutils/identity" + "github.com/centrifuge/go-centrifuge/utils" + "github.com/centrifuge/go-centrifuge/utils/byteutils" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" ) func TestTypes_toTransferDetailCreatePayload(t *testing.T) { req := CreateTransferDetailRequest{ DocumentID: "test_id", - Data: transferdetails.TransferDetailData{ + Data: transferdetails.Data{ TransferID: "test", }, } @@ -28,7 +47,7 @@ func TestTypes_toTransferDetailUpdatePayload(t *testing.T) { req := UpdateTransferDetailRequest{ DocumentID: "test_id", TransferID: "transfer_id", - Data: transferdetails.TransferDetailData{ + Data: transferdetails.Data{ TransferID: "test", }, } @@ -41,13 +60,183 @@ func TestTypes_toTransferDetailUpdatePayload(t *testing.T) { assert.Equal(t, payload.Data.TransferID, req.Data.TransferID) } -func transferData() map[string]interface{} { - return map[string]interface{}{ - "status": "unpaid", - "amount": "300", - "sender_id": "0xBAEb33a61f05e6F269f1c4b4CFF91A901B54DaF7", - "recipient_id": "0xBAEb33a61f05e6F269f1c4b4CFF91A901B54DaF7", - "date_due": "2019-05-24T14:48:44.308854Z", // rfc3339nano - "currency": "EUR", +func Test_getEntityRelationships(t *testing.T) { + // missing did + ctx := context.Background() + _, err := getEntityRelationships(ctx, nil, nil) + assert.Error(t, err) + assert.Contains(t, err.Error(), "failed to get self ID") + + // failed to check collaborator + m := new(testingdocuments.MockModel) + collab := testingidentity.GenerateRandomDID() + ctx = context.WithValue(ctx, config.AccountHeaderKey, collab.String()) + m.On("IsDIDCollaborator", collab).Return(false, errors.New("failed to check DID")).Once() + _, err = getEntityRelationships(ctx, nil, m) + assert.Error(t, err) + assert.Contains(t, err.Error(), "failed to check DID") + + // not a collaborator + m.On("IsDIDCollaborator", collab).Return(false, nil).Once() + rs, err := getEntityRelationships(ctx, nil, m) + assert.NoError(t, err) + assert.Nil(t, rs) + + // failed to get relationships + eid := utils.RandomSlice(32) + m.On("IsDIDCollaborator", collab).Return(true, nil) + m.On("ID").Return(eid) + erSrv := new(entity.MockEntityRelationService) + erSrv.On("GetEntityRelationships", ctx, eid).Return(nil, errors.New("failed to get relationships")).Once() + _, err = getEntityRelationships(ctx, erSrv, m) + assert.Error(t, err) + assert.Contains(t, err.Error(), "failed to get relationships") + + // failed to get access tokens + m.On("GetAccessTokens").Return(nil, errors.New("failed to get access tokens")).Once() + erSrv.On("GetEntityRelationships", ctx, eid).Return([]documents.Model{m}, nil).Once() + _, err = getEntityRelationships(ctx, erSrv, m) + assert.Error(t, err) + assert.Contains(t, err.Error(), "failed to get access tokens") + + //success + er := &entityrelationship.EntityRelationship{ + CoreDocument: &documents.CoreDocument{ + Document: coredocumentpb.CoreDocument{}, + }, + + Data: entityrelationship.Data{ + TargetIdentity: &collab, + OwnerIdentity: &collab, + EntityIdentifier: eid, + }, } + erSrv.On("GetEntityRelationships", ctx, eid).Return([]documents.Model{er}, nil).Once() + rs, err = getEntityRelationships(ctx, erSrv, m) + assert.NoError(t, err) + assert.Len(t, rs, 1) + assert.Equal(t, rs[0].TargetIdentity, collab) + assert.False(t, rs[0].Active) + m.AssertExpectations(t) + erSrv.AssertExpectations(t) +} + +func TestTypes_toEntityShareResponse(t *testing.T) { + // failed to derive header + model := new(testingdocuments.MockModel) + model.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, errors.New("error fetching collaborators")).Once() + _, err := toEntityShareResponse(model, nil, jobs.NewJobID()) + assert.Error(t, err) + assert.Contains(t, err.Error(), "error fetching collaborators") + model.AssertExpectations(t) + + // success + id := byteutils.HexBytes(utils.RandomSlice(32)) + did1 := testingidentity.GenerateRandomDID() + did2 := testingidentity.GenerateRandomDID() + er := &entityrelationship.EntityRelationship{ + CoreDocument: &documents.CoreDocument{ + Document: coredocumentpb.CoreDocument{}, + }, + + Data: entityrelationship.Data{ + TargetIdentity: &did1, + OwnerIdentity: &did2, + EntityIdentifier: id, + }, + } + + resp, err := toEntityShareResponse(er, nil, jobs.NewJobID()) + assert.NoError(t, err) + assert.Equal(t, resp.Relationship.EntityIdentifier, id) + assert.Equal(t, resp.Relationship.OwnerIdentity, did2) + assert.Equal(t, resp.Relationship.TargetIdentity, did1) + assert.True(t, resp.Relationship.Active) +} + +func TestTypes_convertEntityShareRequest(t *testing.T) { + // failed context + ctx := context.Background() + _, err := convertShareEntityRequest(ctx, nil, identity.DID{}) + assert.Error(t, err) + + // success + did := testingidentity.GenerateRandomDID() + did1 := testingidentity.GenerateRandomDID() + ctx = context.WithValue(ctx, config.AccountHeaderKey, did.String()) + docID := byteutils.HexBytes(utils.RandomSlice(32)) + req, err := convertShareEntityRequest(ctx, docID, did1) + assert.NoError(t, err) + assert.Equal(t, req.Scheme, entityrelationship.Scheme) + var r entityrelationship.Data + assert.NoError(t, json.Unmarshal(req.Data, &r)) + assert.Equal(t, r.EntityIdentifier, docID) + assert.Equal(t, r.OwnerIdentity, &did) + assert.Equal(t, r.TargetIdentity, &did1) +} + +func TestTypes_toFundingResponse(t *testing.T) { + // failed to derive header + model := new(testingdocuments.MockModel) + model.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, errors.New("error fetching collaborators")).Once() + fundingSrv := new(funding.MockService) + ctx := context.Background() + fundingID := byteutils.HexBytes(utils.RandomSlice(32)).String() + _, err := toFundingAgreementResponse(ctx, fundingSrv, model, fundingID, nil, jobs.NilJobID()) + assert.Error(t, err) + assert.Contains(t, err.Error(), "error fetching collaborators") + + // failed to get data and signatures + id := utils.RandomSlice(32) + model.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, nil) + model.On("ID").Return(id) + model.On("CurrentVersion").Return(id) + model.On("Author").Return(nil, errors.New("somerror")) + model.On("Timestamp").Return(nil, errors.New("somerror")) + model.On("NFTs").Return(nil) + fundingSrv.On("GetDataAndSignatures", ctx, model, fundingID).Return(nil, nil, errors.New("failed to get data and sigs")).Once() + _, err = toFundingAgreementResponse(ctx, fundingSrv, model, fundingID, nil, jobs.NilJobID()) + assert.Error(t, err) + assert.Contains(t, err.Error(), "failed to get data and sigs") + + // success + fundingSrv.On("GetDataAndSignatures", ctx, model, fundingID).Return(funding.Data{}, nil, nil) + _, err = toFundingAgreementResponse(ctx, fundingSrv, model, fundingID, nil, jobs.NilJobID()) + assert.NoError(t, err) + model.AssertExpectations(t) + fundingSrv.AssertExpectations(t) +} + +func TestTypes_toFundingListResponse(t *testing.T) { + // failed to derive header + model := new(testingdocuments.MockModel) + model.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, errors.New("error fetching collaborators")).Once() + fundingSrv := new(funding.MockService) + ctx := context.Background() + _, err := toFundingAgreementListResponse(ctx, fundingSrv, model, nil) + assert.Error(t, err) + assert.Contains(t, err.Error(), "error fetching collaborators") + + // no agreements exists + inv, _ := invoice.CreateInvoiceWithEmbedCD(t, testingconfig.CreateAccountContext(t, cfg), did, nil) + resp, err := toFundingAgreementListResponse(ctx, fundingSrv, inv, nil) + assert.NoError(t, err) + assert.Len(t, resp.Data, 0) + + // failed conversion + data := funding.CreateData() + attrs, err := extensions.CreateAttributesList(inv, data, "funding_agreement[{IDX}].", funding.AttrFundingLabel) + assert.NoError(t, err) + err = inv.AddAttributes(documents.CollaboratorsAccess{}, false, attrs...) + assert.NoError(t, err) + fundingSrv.On("GetDataAndSignatures", mock.Anything, mock.Anything, mock.Anything).Return(nil, nil, errors.New("error")).Once() + _, err = toFundingAgreementListResponse(ctx, fundingSrv, inv, nil) + assert.Error(t, err) + + // success + fundingSrv.On("GetDataAndSignatures", mock.Anything, mock.Anything, mock.Anything).Return(nil, nil, nil) + _, err = toFundingAgreementListResponse(ctx, fundingSrv, inv, nil) + assert.NoError(t, err) + model.AssertExpectations(t) + fundingSrv.AssertExpectations(t) } diff --git a/httpapi/userapi/userapi.go b/httpapi/userapi/userapi.go index 9ebd9b74f..b4d99afe2 100644 --- a/httpapi/userapi/userapi.go +++ b/httpapi/userapi/userapi.go @@ -1,28 +1,70 @@ package userapi import ( + "github.com/centrifuge/go-centrifuge/bootstrap" "github.com/centrifuge/go-centrifuge/documents" - "github.com/centrifuge/go-centrifuge/extensions/transferdetails" - "github.com/centrifuge/go-centrifuge/nft" + "github.com/centrifuge/go-centrifuge/httpapi/coreapi" "github.com/go-chi/chi" ) const ( - documentIDParam = "document_id" - transferIDParam = "transfer_id" + transferIDParam = "transfer_id" + agreementIDParam = "agreement_id" + registryAddressParam = "registry_address" + tokenIDParam = "token_id" ) // Register registers the core apis to the router. -func Register(r chi.Router, - docSrv documents.Service, - nftSrv nft.Service, - transferSrv transferdetails.Service) { +func Register(ctx map[string]interface{}, r chi.Router) { + tokenRegistry := ctx[bootstrap.BootstrappedInvoiceUnpaid].(documents.TokenRegistry) + userAPISrv := ctx[BootstrappedUserAPIService].(Service) h := handler{ - tokenRegistry: nftSrv.(documents.TokenRegistry), - srv: Service{docSrv: docSrv, transferDetailsService: transferSrv}, + tokenRegistry: tokenRegistry, + srv: userAPISrv, } - r.Post("/documents/{"+documentIDParam+"}/transfer_details", h.CreateTransferDetail) - r.Put("/documents/{"+documentIDParam+"}/transfer_details/{"+transferIDParam+"}", h.UpdateTransferDetail) - r.Get("/documents/{"+documentIDParam+"}/transfer_details", h.GetTransferDetailList) - r.Get("/documents/{"+documentIDParam+"}/transfer_details/{"+transferIDParam+"}", h.GetTransferDetail) + + // transfer details api + r.Post("/documents/{"+coreapi.DocumentIDParam+"}/transfer_details", h.CreateTransferDetail) + r.Put("/documents/{"+coreapi.DocumentIDParam+"}/transfer_details/{"+transferIDParam+"}", h.UpdateTransferDetail) + r.Get("/documents/{"+coreapi.DocumentIDParam+"}/transfer_details", h.GetTransferDetailList) + r.Get("/documents/{"+coreapi.DocumentIDParam+"}/transfer_details/{"+transferIDParam+"}", h.GetTransferDetail) + + // invoice apis + r.Post("/invoices", h.CreateInvoice) + r.Get("/invoices/{"+coreapi.DocumentIDParam+"}", h.GetInvoice) + r.Put("/invoices/{"+coreapi.DocumentIDParam+"}", h.UpdateInvoice) + r.Get("/invoices/{"+coreapi.DocumentIDParam+"}/versions/{"+coreapi.VersionIDParam+"}", h.GetInvoiceVersion) + r.Post("/invoices/{"+coreapi.DocumentIDParam+"}/mint/unpaid", h.MintInvoiceUnpaidNFT) + + // entity api + r.Post("/entities", h.CreateEntity) + r.Put("/entities/{"+coreapi.DocumentIDParam+"}", h.UpdateEntity) + r.Get("/entities/{"+coreapi.DocumentIDParam+"}", h.GetEntity) + r.Post("/entities/{"+coreapi.DocumentIDParam+"}/share", h.ShareEntity) + r.Post("/entities/{"+coreapi.DocumentIDParam+"}/revoke", h.RevokeEntity) + r.Get("/relationships/{"+coreapi.DocumentIDParam+"}/entity", h.GetEntityThroughRelationship) + + // funding api + r.Post("/documents/{"+coreapi.DocumentIDParam+"}/funding_agreements", h.CreateFundingAgreement) + r.Get("/documents/{"+coreapi.DocumentIDParam+"}/funding_agreements", h.GetFundingAgreements) + r.Get("/documents/{"+coreapi.DocumentIDParam+"}/funding_agreements/{"+agreementIDParam+"}", h.GetFundingAgreement) + r.Put("/documents/{"+coreapi.DocumentIDParam+"}/funding_agreements/{"+agreementIDParam+"}", h.UpdateFundingAgreement) + r.Post("/documents/{"+coreapi.DocumentIDParam+"}/funding_agreements/{"+agreementIDParam+"}/sign", h.SignFundingAgreement) + r.Get("/documents/{"+coreapi.DocumentIDParam+"}/versions/{"+coreapi.VersionIDParam+"}/funding_agreements/{"+agreementIDParam+"}", h.GetFundingAgreementFromVersion) + r.Get("/documents/{"+coreapi.DocumentIDParam+"}/versions/{"+coreapi.VersionIDParam+"}/funding_agreements", h.GetFundingAgreementsFromVersion) +} + +// RegisterBeta registers the core apis to the router that are not production ready +func RegisterBeta(ctx map[string]interface{}, r chi.Router) { + tokenRegistry := ctx[bootstrap.BootstrappedInvoiceUnpaid].(documents.TokenRegistry) + userAPISrv := ctx[BootstrappedUserAPIService].(Service) + h := handler{ + tokenRegistry: tokenRegistry, + srv: userAPISrv, + } + + // beta + r.Post("/nfts/registries/{"+registryAddressParam+"}/mint", h.MintNFT) + r.Post("/nfts/registries/{"+registryAddressParam+"}/tokens/{"+tokenIDParam+"}/transfer", h.TransferNFT) + r.Get("/nfts/registries/{"+registryAddressParam+"}/tokens/{"+tokenIDParam+"}/owner", h.OwnerOfNFT) } diff --git a/httpapi/userapi/userapi_test.go b/httpapi/userapi/userapi_test.go index 9726cf16a..3a990a532 100644 --- a/httpapi/userapi/userapi_test.go +++ b/httpapi/userapi/userapi_test.go @@ -5,6 +5,7 @@ package userapi import ( "testing" + "github.com/centrifuge/go-centrifuge/bootstrap" "github.com/centrifuge/go-centrifuge/testingutils/nfts" "github.com/go-chi/chi" "github.com/stretchr/testify/assert" @@ -12,14 +13,77 @@ import ( func TestRegister(t *testing.T) { r := chi.NewRouter() - Register(r, nil, new(testingnfts.MockNFTService), nil) - assert.Len(t, r.Routes(), 2) - assert.Equal(t, r.Routes()[0].Pattern, "/documents/{document_id}/transfer_details") + ctx := map[string]interface{}{ + BootstrappedUserAPIService: Service{}, + bootstrap.BootstrappedInvoiceUnpaid: new(testingnfts.MockNFTService), + } + Register(ctx, r) + assert.Len(t, r.Routes(), 16) + assert.Equal(t, r.Routes()[0].Pattern, "/documents/{document_id}/funding_agreements") assert.Len(t, r.Routes()[0].Handlers, 2) assert.NotNil(t, r.Routes()[0].Handlers["POST"]) assert.NotNil(t, r.Routes()[0].Handlers["GET"]) - assert.Equal(t, r.Routes()[1].Pattern, "/documents/{document_id}/transfer_details/{transfer_id}") + assert.Equal(t, r.Routes()[1].Pattern, "/documents/{document_id}/funding_agreements/{agreement_id}") assert.Len(t, r.Routes()[1].Handlers, 2) + assert.NotNil(t, r.Routes()[1].Handlers["GET"]) assert.NotNil(t, r.Routes()[1].Handlers["PUT"]) + assert.Equal(t, r.Routes()[2].Pattern, "/documents/{document_id}/funding_agreements/{agreement_id}/sign") + assert.Len(t, r.Routes()[2].Handlers, 1) + assert.NotNil(t, r.Routes()[2].Handlers["POST"]) + assert.Equal(t, r.Routes()[3].Pattern, "/documents/{document_id}/transfer_details") + assert.Len(t, r.Routes()[3].Handlers, 2) + assert.NotNil(t, r.Routes()[3].Handlers["POST"]) + assert.NotNil(t, r.Routes()[3].Handlers["GET"]) + assert.Equal(t, r.Routes()[4].Pattern, "/documents/{document_id}/transfer_details/{transfer_id}") + assert.Len(t, r.Routes()[4].Handlers, 2) + assert.NotNil(t, r.Routes()[4].Handlers["PUT"]) + assert.NotNil(t, r.Routes()[4].Handlers["GET"]) + assert.Equal(t, r.Routes()[5].Pattern, "/documents/{document_id}/versions/{version_id}/funding_agreements") + assert.NotNil(t, r.Routes()[5].Handlers["GET"]) + assert.Equal(t, r.Routes()[6].Pattern, "/documents/{document_id}/versions/{version_id}/funding_agreements/{agreement_id}") + assert.NotNil(t, r.Routes()[6].Handlers["GET"]) + assert.Equal(t, r.Routes()[7].Pattern, "/entities") + assert.Len(t, r.Routes()[7].Handlers, 1) + assert.NotNil(t, r.Routes()[7].Handlers["POST"]) + assert.Equal(t, r.Routes()[8].Pattern, "/entities/{document_id}") + assert.Len(t, r.Routes()[8].Handlers, 2) + assert.NotNil(t, r.Routes()[8].Handlers["PUT"]) + assert.NotNil(t, r.Routes()[8].Handlers["GET"]) + assert.Equal(t, r.Routes()[9].Pattern, "/entities/{document_id}/revoke") + assert.Len(t, r.Routes()[9].Handlers, 1) + assert.NotNil(t, r.Routes()[9].Handlers["POST"]) + assert.Equal(t, r.Routes()[10].Pattern, "/entities/{document_id}/share") + assert.NotNil(t, r.Routes()[10].Handlers["POST"]) + assert.Equal(t, r.Routes()[11].Pattern, "/invoices") + assert.Len(t, r.Routes()[11].Handlers, 1) + assert.NotNil(t, r.Routes()[11].Handlers["POST"]) + assert.Equal(t, r.Routes()[12].Pattern, "/invoices/{document_id}") + assert.Len(t, r.Routes()[12].Handlers, 2) + assert.NotNil(t, r.Routes()[12].Handlers["GET"]) + assert.NotNil(t, r.Routes()[12].Handlers["PUT"]) + assert.Equal(t, r.Routes()[13].Pattern, "/invoices/{document_id}/mint/unpaid") + assert.NotNil(t, r.Routes()[13].Handlers["POST"]) + assert.Equal(t, r.Routes()[14].Pattern, "/invoices/{document_id}/versions/{version_id}") + assert.NotNil(t, r.Routes()[14].Handlers["GET"]) + assert.Equal(t, r.Routes()[15].Pattern, "/relationships/{document_id}/entity") + assert.NotNil(t, r.Routes()[15].Handlers["GET"]) +} + +func TestRegisterBeta(t *testing.T) { + r := chi.NewRouter() + ctx := map[string]interface{}{ + BootstrappedUserAPIService: Service{}, + bootstrap.BootstrappedInvoiceUnpaid: new(testingnfts.MockNFTService), + } + RegisterBeta(ctx, r) + assert.Len(t, r.Routes(), 3) + assert.Equal(t, r.Routes()[0].Pattern, "/nfts/registries/{registry_address}/mint") + assert.Len(t, r.Routes()[0].Handlers, 1) + assert.NotNil(t, r.Routes()[0].Handlers["POST"]) + assert.Equal(t, r.Routes()[1].Pattern, "/nfts/registries/{registry_address}/tokens/{token_id}/owner") + assert.Len(t, r.Routes()[1].Handlers, 1) assert.NotNil(t, r.Routes()[1].Handlers["GET"]) + assert.Equal(t, r.Routes()[2].Pattern, "/nfts/registries/{registry_address}/tokens/{token_id}/transfer") + assert.Len(t, r.Routes()[2].Handlers, 1) + assert.NotNil(t, r.Routes()[2].Handlers["POST"]) } diff --git a/httpapi/userapi/webhook.go b/httpapi/userapi/webhook.go new file mode 100644 index 000000000..ac0208e0d --- /dev/null +++ b/httpapi/userapi/webhook.go @@ -0,0 +1,14 @@ +package userapi + +import "net/http" + +// Webhook is a place holder to describe webhook response in swagger +// @summary Webhook is a place holder to describe webhook response in swagger. +// @description Webhook is a place holder to describe webhook response in swagger. +// @id webhook +// @tags Webhook +// @accept json +// @produce json +// @success 200 {object} notification.Message +// @router /webhook [post] +func Webhook(http.ResponseWriter, *http.Request) {} diff --git a/httpapi/v2/bootstrapper.go b/httpapi/v2/bootstrapper.go new file mode 100644 index 000000000..7fcafc8ef --- /dev/null +++ b/httpapi/v2/bootstrapper.go @@ -0,0 +1,33 @@ +package v2 + +import ( + "github.com/centrifuge/go-centrifuge/bootstrap" + "github.com/centrifuge/go-centrifuge/documents" + "github.com/centrifuge/go-centrifuge/errors" + "github.com/centrifuge/go-centrifuge/pending" +) + +// BootstrappedService key maps to the Service implementation in Bootstrap context. +const BootstrappedService = "V2 Service" + +// Bootstrapper implements bootstrap.Bootstrapper. +type Bootstrapper struct{} + +// Bootstrap adds transaction.Repository into context. +func (b Bootstrapper) Bootstrap(ctx map[string]interface{}) error { + pendingDocSrv, ok := ctx[pending.BootstrappedPendingDocumentService].(pending.Service) + if !ok { + return errors.New("failed to get %s", pending.BootstrappedPendingDocumentService) + } + + nftSrv, ok := ctx[bootstrap.BootstrappedInvoiceUnpaid].(documents.TokenRegistry) + if !ok { + return errors.New("failed to get %s", bootstrap.BootstrappedInvoiceUnpaid) + } + + ctx[BootstrappedService] = Service{ + pendingDocSrv: pendingDocSrv, + tokenRegistry: nftSrv, + } + return nil +} diff --git a/httpapi/v2/bootstrapper_test.go b/httpapi/v2/bootstrapper_test.go new file mode 100644 index 000000000..6b9d47e5e --- /dev/null +++ b/httpapi/v2/bootstrapper_test.go @@ -0,0 +1,34 @@ +// +build unit + +package v2 + +import ( + "testing" + + "github.com/centrifuge/go-centrifuge/bootstrap" + "github.com/centrifuge/go-centrifuge/pending" + testingnfts "github.com/centrifuge/go-centrifuge/testingutils/nfts" + "github.com/stretchr/testify/assert" +) + +func TestBootstrapper_Bootstrap(t *testing.T) { + ctx := make(map[string]interface{}) + b := Bootstrapper{} + + // missing pending doc service + err := b.Bootstrap(ctx) + assert.Error(t, err) + assert.Contains(t, err.Error(), pending.BootstrappedPendingDocumentService) + + // missing nft service + ctx[pending.BootstrappedPendingDocumentService] = new(pending.MockService) + err = b.Bootstrap(ctx) + assert.Error(t, err) + assert.Contains(t, err.Error(), bootstrap.BootstrappedInvoiceUnpaid) + + // success + ctx[bootstrap.BootstrappedInvoiceUnpaid] = new(testingnfts.MockNFTService) + err = b.Bootstrap(ctx) + assert.NoError(t, b.Bootstrap(ctx)) + assert.NotNil(t, ctx[BootstrappedService]) +} diff --git a/httpapi/v2/converters.go b/httpapi/v2/converters.go new file mode 100644 index 000000000..f3f60ff0d --- /dev/null +++ b/httpapi/v2/converters.go @@ -0,0 +1,41 @@ +package v2 + +import ( + "encoding/json" + "io/ioutil" + "net/http" + + "github.com/centrifuge/go-centrifuge/documents" + "github.com/centrifuge/go-centrifuge/httpapi/coreapi" + "github.com/centrifuge/go-centrifuge/jobs" +) + +func toDocumentsPayload(req DocumentRequest, docID []byte) (payload documents.UpdatePayload, err error) { + cp, err := coreapi.ToDocumentsCreatePayload(req) + if err != nil { + return payload, err + } + + return documents.UpdatePayload{CreatePayload: cp, DocumentID: docID}, nil +} + +func toDocumentResponse(doc documents.Model, tokenRegistry documents.TokenRegistry, jobID jobs.JobID) (coreapi.DocumentResponse, error) { + resp, err := coreapi.GetDocumentResponse(doc, tokenRegistry, jobID) + if err != nil { + return resp, err + } + + resp.Header.Status = string(doc.GetStatus()) + return resp, err +} + +// unmarshalBody unmarshals req.Body to val. +// val should always be a pointer to the struct. +func unmarshalBody(r *http.Request, val interface{}) error { + data, err := ioutil.ReadAll(r.Body) + if err != nil { + return err + } + + return json.Unmarshal(data, val) +} diff --git a/httpapi/v2/documents_api.go b/httpapi/v2/documents_api.go new file mode 100644 index 000000000..61bc0b763 --- /dev/null +++ b/httpapi/v2/documents_api.go @@ -0,0 +1,312 @@ +package v2 + +import ( + "net/http" + + "github.com/centrifuge/go-centrifuge/documents" + "github.com/centrifuge/go-centrifuge/httpapi/coreapi" + "github.com/centrifuge/go-centrifuge/jobs" + "github.com/centrifuge/go-centrifuge/utils/byteutils" + "github.com/centrifuge/go-centrifuge/utils/httputils" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/go-chi/chi" + "github.com/go-chi/render" +) + +// DocumentRequest is an alias to coreapi Document request. +// Aliased here to fix the swagger generation issues. +type DocumentRequest = coreapi.CreateDocumentRequest + +// CreateDocumentRequest defines the payload for creating documents. +type CreateDocumentRequest struct { + DocumentRequest + DocumentID byteutils.OptionalHex `json:"document_id" swaggertype:"primitive,string"` // if provided, creates the next version of the document. +} + +// UpdateDocumentRequest defines the payload to patch an existing document. +type UpdateDocumentRequest struct { + DocumentRequest +} + +// CreateDocument creates a document. +// @summary Creates a new document. +// @description Creates a new document. +// @id create_document_v2 +// @tags Documents +// @accept json +// @param authorization header string true "Hex encoded centrifuge ID of the account for the intended API action" +// @param body body v2.CreateDocumentRequest true "Document Create request" +// @produce json +// @Failure 400 {object} httputils.HTTPError +// @Failure 500 {object} httputils.HTTPError +// @Failure 403 {object} httputils.HTTPError +// @success 201 {object} coreapi.DocumentResponse +// @router /v2/documents [post] +func (h handler) CreateDocument(w http.ResponseWriter, r *http.Request) { + var err error + var code int + defer httputils.RespondIfError(&code, &err, w, r) + + ctx := r.Context() + var req CreateDocumentRequest + err = unmarshalBody(r, &req) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + return + } + + payload, err := toDocumentsPayload(req.DocumentRequest, req.DocumentID.Bytes()) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + return + } + + doc, err := h.srv.CreateDocument(ctx, payload) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + return + } + + resp, err := toDocumentResponse(doc, h.srv.tokenRegistry, jobs.NilJobID()) + if err != nil { + code = http.StatusInternalServerError + log.Error(err) + return + } + + render.Status(r, http.StatusCreated) + render.JSON(w, r, resp) +} + +// Update updates a pending document. +// @summary Updates a pending document. +// @description Updates a pending document. +// @id update_document_v2 +// @tags Documents +// @accept json +// @param authorization header string true "Hex encoded centrifuge ID of the account for the intended API action" +// @param body body v2.UpdateDocumentRequest true "Document Update request" +// @param document_id path string true "Document Identifier" +// @produce json +// @Failure 400 {object} httputils.HTTPError +// @Failure 500 {object} httputils.HTTPError +// @Failure 403 {object} httputils.HTTPError +// @success 200 {object} coreapi.DocumentResponse +// @router /v2/documents/{document_id} [patch] +func (h handler) UpdateDocument(w http.ResponseWriter, r *http.Request) { + var err error + var code int + defer httputils.RespondIfError(&code, &err, w, r) + + docID, err := hexutil.Decode(chi.URLParam(r, coreapi.DocumentIDParam)) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + err = coreapi.ErrInvalidDocumentID + return + } + + ctx := r.Context() + var req UpdateDocumentRequest + err = unmarshalBody(r, &req) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + return + } + + payload, err := toDocumentsPayload(req.DocumentRequest, docID) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + return + } + + doc, err := h.srv.UpdateDocument(ctx, payload) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + return + } + + resp, err := toDocumentResponse(doc, h.srv.tokenRegistry, jobs.NilJobID()) + if err != nil { + code = http.StatusInternalServerError + log.Error(err) + return + } + + render.Status(r, http.StatusOK) + render.JSON(w, r, resp) +} + +// Commit creates a document. +// @summary Commits a pending document. +// @description Commits a pending document. +// @id commit_document_v2 +// @tags Documents +// @accept json +// @param authorization header string true "Hex encoded centrifuge ID of the account for the intended API action" +// @param document_id path string true "Document Identifier" +// @produce json +// @Failure 400 {object} httputils.HTTPError +// @Failure 500 {object} httputils.HTTPError +// @Failure 403 {object} httputils.HTTPError +// @success 202 {object} coreapi.DocumentResponse +// @router /v2/documents/{document_id}/commit [post] +func (h handler) Commit(w http.ResponseWriter, r *http.Request) { + var err error + var code int + defer httputils.RespondIfError(&code, &err, w, r) + + docID, err := hexutil.Decode(chi.URLParam(r, coreapi.DocumentIDParam)) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + err = coreapi.ErrInvalidDocumentID + return + } + + ctx := r.Context() + doc, jobID, err := h.srv.Commit(ctx, docID) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + return + } + + resp, err := toDocumentResponse(doc, h.srv.tokenRegistry, jobID) + if err != nil { + code = http.StatusInternalServerError + log.Error(err) + return + } + + render.Status(r, http.StatusAccepted) + render.JSON(w, r, resp) +} + +func (h handler) getDocumentWithStatus(w http.ResponseWriter, r *http.Request, st documents.Status) { + var err error + var code int + defer httputils.RespondIfError(&code, &err, w, r) + + docID, err := hexutil.Decode(chi.URLParam(r, coreapi.DocumentIDParam)) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + err = coreapi.ErrInvalidDocumentID + return + } + + ctx := r.Context() + doc, err := h.srv.GetDocument(ctx, docID, st) + if err != nil { + code = http.StatusNotFound + log.Error(err) + err = coreapi.ErrDocumentNotFound + return + } + + resp, err := toDocumentResponse(doc, h.srv.tokenRegistry, jobs.NilJobID()) + if err != nil { + code = http.StatusInternalServerError + log.Error(err) + return + } + + render.Status(r, http.StatusOK) + render.JSON(w, r, resp) +} + +// GetPendingDocument returns the pending document associated with docID. +// @summary Returns the pending document associated with docID. +// @description Returns the pending document associated with docID. +// @id get_pending_document +// @tags Documents +// @param authorization header string true "Hex encoded centrifuge ID of the account for the intended API action" +// @param document_id path string true "Document Identifier" +// @produce json +// @Failure 403 {object} httputils.HTTPError +// @Failure 400 {object} httputils.HTTPError +// @Failure 404 {object} httputils.HTTPError +// @Failure 500 {object} httputils.HTTPError +// @success 200 {object} coreapi.DocumentResponse +// @router /v2/documents/{document_id}/pending [get] +func (h handler) GetPendingDocument(w http.ResponseWriter, r *http.Request) { + h.getDocumentWithStatus(w, r, documents.Pending) +} + +// GetCommittedDocument returns the latest committed document associated with docID. +// @summary Returns the latest committed document associated with docID. +// @description Returns the latest committed document associated with docID. +// @id get_committed_document +// @tags Documents +// @param authorization header string true "Hex encoded centrifuge ID of the account for the intended API action" +// @param document_id path string true "Document Identifier" +// @produce json +// @Failure 403 {object} httputils.HTTPError +// @Failure 400 {object} httputils.HTTPError +// @Failure 404 {object} httputils.HTTPError +// @Failure 500 {object} httputils.HTTPError +// @success 200 {object} coreapi.DocumentResponse +// @router /v2/documents/{document_id}/committed [get] +func (h handler) GetCommittedDocument(w http.ResponseWriter, r *http.Request) { + h.getDocumentWithStatus(w, r, documents.Committed) +} + +// GetDocumentVersion returns the specific version of the document. +// @summary Returns the specific version of the document. +// @description Returns the specific version of the document. +// @id get_document_version_v2 +// @tags Documents +// @param authorization header string true "Hex encoded centrifuge ID of the account for the intended API action" +// @param document_id path string true "Document Identifier" +// @param version_id path string true "Document Version Identifier" +// @produce json +// @Failure 403 {object} httputils.HTTPError +// @Failure 400 {object} httputils.HTTPError +// @Failure 404 {object} httputils.HTTPError +// @Failure 500 {object} httputils.HTTPError +// @success 200 {object} coreapi.DocumentResponse +// @router /v2/documents/{document_id}/versions/{version_id} [get] +func (h handler) GetDocumentVersion(w http.ResponseWriter, r *http.Request) { + var err error + var code int + defer httputils.RespondIfError(&code, &err, w, r) + + ids := make([][]byte, 2, 2) + for i, idStr := range []string{chi.URLParam(r, coreapi.DocumentIDParam), chi.URLParam(r, coreapi.VersionIDParam)} { + var id []byte + id, err = hexutil.Decode(idStr) + if err != nil { + code = http.StatusBadRequest + log.Error(err) + err = coreapi.ErrInvalidDocumentID + return + } + + ids[i] = id + } + + doc, err := h.srv.GetDocumentVersion(r.Context(), ids[0], ids[1]) + if err != nil { + code = http.StatusNotFound + log.Error(err) + err = coreapi.ErrDocumentNotFound + return + } + + resp, err := toDocumentResponse(doc, h.srv.tokenRegistry, jobs.NilJobID()) + if err != nil { + code = http.StatusInternalServerError + log.Error(err) + return + } + + render.Status(r, http.StatusOK) + render.JSON(w, r, resp) +} diff --git a/httpapi/v2/documents_api_test.go b/httpapi/v2/documents_api_test.go new file mode 100644 index 000000000..f4410106c --- /dev/null +++ b/httpapi/v2/documents_api_test.go @@ -0,0 +1,414 @@ +// +build unit + +package v2 + +import ( + "bytes" + "context" + "encoding/json" + "io" + "net/http" + "net/http/httptest" + "testing" + + "github.com/centrifuge/go-centrifuge/documents" + "github.com/centrifuge/go-centrifuge/documents/invoice" + "github.com/centrifuge/go-centrifuge/errors" + "github.com/centrifuge/go-centrifuge/httpapi/coreapi" + "github.com/centrifuge/go-centrifuge/pending" + testingdocuments "github.com/centrifuge/go-centrifuge/testingutils/documents" + "github.com/centrifuge/go-centrifuge/utils" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/go-chi/chi" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func invoiceData() map[string]interface{} { + return map[string]interface{}{ + "number": "12345", + "status": "unpaid", + "gross_amount": "12.345", + "recipient": "0xBAEb33a61f05e6F269f1c4b4CFF91A901B54DaF7", + "date_due": "2019-05-24T14:48:44.308854Z", // rfc3339nano + "date_paid": "2019-05-24T14:48:44Z", // rfc3339 + "currency": "EUR", + "attachments": []map[string]interface{}{ + { + "name": "test", + "file_type": "pdf", + "size": 1000202, + "data": "0xBAEb33a61f05e6F269f1c4b4CFF91A901B54DaF7", + "checksum": "0xBAEb33a61f05e6F269f1c4b4CFF91A901B54DaF3", + }, + }, + } +} + +func invalidDocIDPayload(t *testing.T) io.Reader { + p := map[string]interface{}{ + "scheme": "invoice", + "data": invoiceData(), + "document_id": "invalid", + } + + d, err := json.Marshal(p) + assert.NoError(t, err) + return bytes.NewReader(d) +} + +func validPayload(t *testing.T) io.Reader { + p := map[string]interface{}{ + "scheme": "invoice", + "data": invoiceData(), + "document_id": hexutil.Encode(utils.RandomSlice(32)), + "attributes": map[string]map[string]string{ + "string_test": { + "type": "string", + "value": "hello, world", + }, + }, + } + + d, err := json.Marshal(p) + assert.NoError(t, err) + return bytes.NewReader(d) +} + +func invalidAttrPayload(t *testing.T) io.Reader { + p := map[string]interface{}{ + "scheme": "invoice", + "data": invoiceData(), + "attributes": map[string]map[string]string{ + "string_test": { + "type": "invalid", + "value": "hello, world", + }, + }, + } + + d, err := json.Marshal(p) + assert.NoError(t, err) + return bytes.NewReader(d) +} + +func TestHandler_CreateDocument(t *testing.T) { + getHTTPReqAndResp := func(ctx context.Context, b io.Reader) (*httptest.ResponseRecorder, *http.Request) { + return httptest.NewRecorder(), httptest.NewRequest("POST", "/documents", b).WithContext(ctx) + } + + // failed unmarshal empty body + ctx := context.Background() + w, r := getHTTPReqAndResp(ctx, nil) + pendingSrv := new(pending.MockService) + h := handler{srv: Service{pendingDocSrv: pendingSrv}} + h.CreateDocument(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), "unexpected end of JSON input") + + // failed unmarshal invalid doc_id + w, r = getHTTPReqAndResp(ctx, invalidDocIDPayload(t)) + h.CreateDocument(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), "hex string without 0x prefix") + + // failed payloadConversion + w, r = getHTTPReqAndResp(ctx, invalidAttrPayload(t)) + h.CreateDocument(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), "not a valid attribute type") + + // failed to create document + pendingSrv.On("Create", ctx, mock.Anything).Return(nil, errors.New("Failed to create document")).Once() + w, r = getHTTPReqAndResp(ctx, validPayload(t)) + h.CreateDocument(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), "Failed to create document") + + // failed document conversion + doc := new(testingdocuments.MockModel) + doc.On("GetData").Return(invoice.Data{}).Twice() + doc.On("Scheme").Return("invoice").Twice() + doc.On("GetAttributes").Return(nil).Twice() + doc.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, errors.New("failed to get collaborators")).Once() + pendingSrv.On("Create", ctx, mock.Anything).Return(doc, nil) + w, r = getHTTPReqAndResp(ctx, validPayload(t)) + h.CreateDocument(w, r) + assert.Equal(t, w.Code, http.StatusInternalServerError) + assert.Contains(t, w.Body.String(), "failed to get collaborators") + + // success + doc.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, nil).Once() + doc.On("ID").Return(utils.RandomSlice(32)).Once() + doc.On("CurrentVersion").Return(utils.RandomSlice(32)).Once() + doc.On("Author").Return(nil, errors.New("somerror")).Once() + doc.On("Timestamp").Return(nil, errors.New("somerror")).Once() + doc.On("NFTs").Return(nil).Once() + doc.On("GetStatus").Return(documents.Pending).Once() + w, r = getHTTPReqAndResp(ctx, validPayload(t)) + h.CreateDocument(w, r) + assert.Equal(t, w.Code, http.StatusCreated) + assert.Contains(t, w.Body.String(), "\"status\":\"pending\"") + pendingSrv.AssertExpectations(t) + doc.AssertExpectations(t) +} + +func TestHandler_UpdateDocument(t *testing.T) { + getHTTPReqAndResp := func(ctx context.Context, b io.Reader) (*httptest.ResponseRecorder, *http.Request) { + return httptest.NewRecorder(), httptest.NewRequest("PATCH", "/documents/{document_id}", b).WithContext(ctx) + } + + // empty document_id + rctx := chi.NewRouteContext() + rctx.URLParams.Keys = make([]string, 1, 1) + rctx.URLParams.Values = make([]string, 1, 1) + rctx.URLParams.Keys[0] = "document_id" + rctx.URLParams.Values[0] = "" + ctx := context.WithValue(context.Background(), chi.RouteCtxKey, rctx) + w, r := getHTTPReqAndResp(ctx, nil) + h := handler{} + h.UpdateDocument(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), coreapi.ErrInvalidDocumentID.Error()) + + // invalid id + rctx.URLParams.Values[0] = "some invalid id" + w, r = getHTTPReqAndResp(ctx, nil) + h.UpdateDocument(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), coreapi.ErrInvalidDocumentID.Error()) + + // failed unmarshal empty body + rctx.URLParams.Values[0] = hexutil.Encode(utils.RandomSlice(32)) + w, r = getHTTPReqAndResp(ctx, nil) + pendingSrv := new(pending.MockService) + h = handler{srv: Service{pendingDocSrv: pendingSrv}} + h.UpdateDocument(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), "unexpected end of JSON input") + + // failed payloadConversion + w, r = getHTTPReqAndResp(ctx, invalidAttrPayload(t)) + h.UpdateDocument(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), "not a valid attribute type") + + // failed to update document + pendingSrv.On("Update", ctx, mock.Anything).Return(nil, errors.New("Failed to update document")).Once() + w, r = getHTTPReqAndResp(ctx, validPayload(t)) + h.UpdateDocument(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), "Failed to update document") + + // failed document conversion + doc := new(testingdocuments.MockModel) + doc.On("GetData").Return(invoice.Data{}).Twice() + doc.On("Scheme").Return("invoice").Twice() + doc.On("GetAttributes").Return(nil).Twice() + doc.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, errors.New("failed to get collaborators")).Once() + pendingSrv.On("Update", ctx, mock.Anything).Return(doc, nil) + w, r = getHTTPReqAndResp(ctx, validPayload(t)) + h.UpdateDocument(w, r) + assert.Equal(t, w.Code, http.StatusInternalServerError) + assert.Contains(t, w.Body.String(), "failed to get collaborators") + + // success + doc.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, nil).Once() + doc.On("ID").Return(utils.RandomSlice(32)).Once() + doc.On("CurrentVersion").Return(utils.RandomSlice(32)).Once() + doc.On("Author").Return(nil, errors.New("somerror")).Once() + doc.On("Timestamp").Return(nil, errors.New("somerror")).Once() + doc.On("NFTs").Return(nil).Once() + doc.On("GetStatus").Return(documents.Pending).Once() + w, r = getHTTPReqAndResp(ctx, validPayload(t)) + h.UpdateDocument(w, r) + assert.Equal(t, w.Code, http.StatusOK) + assert.Contains(t, w.Body.String(), "\"status\":\"pending\"") + pendingSrv.AssertExpectations(t) + doc.AssertExpectations(t) +} + +func TestHandler_Commit(t *testing.T) { + getHTTPReqAndResp := func(ctx context.Context, b io.Reader) (*httptest.ResponseRecorder, *http.Request) { + return httptest.NewRecorder(), httptest.NewRequest("POST", "/documents/{document_id}/commit", b).WithContext(ctx) + } + + // empty document_id + rctx := chi.NewRouteContext() + rctx.URLParams.Keys = make([]string, 1, 1) + rctx.URLParams.Values = make([]string, 1, 1) + rctx.URLParams.Keys[0] = "document_id" + rctx.URLParams.Values[0] = "" + ctx := context.WithValue(context.Background(), chi.RouteCtxKey, rctx) + w, r := getHTTPReqAndResp(ctx, nil) + h := handler{} + h.Commit(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), coreapi.ErrInvalidDocumentID.Error()) + + // invalid hex + rctx.URLParams.Values[0] = "invalid hex" + w, r = getHTTPReqAndResp(ctx, nil) + h.Commit(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), coreapi.ErrInvalidDocumentID.Error()) + + // commit error + rctx.URLParams.Values[0] = hexutil.Encode(utils.RandomSlice(32)) + srv := new(pending.MockService) + h = handler{srv: Service{pendingDocSrv: srv}} + srv.On("Commit", ctx, mock.Anything).Return(nil, nil, errors.New("Failed to commit document")).Once() + w, r = getHTTPReqAndResp(ctx, nil) + h.Commit(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), "Failed to commit document") + + // failed to convert collaborators in document + doc := new(testingdocuments.MockModel) + doc.On("GetData").Return(invoice.Data{}).Twice() + doc.On("Scheme").Return("invoice").Twice() + doc.On("GetAttributes").Return(nil).Twice() + doc.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, errors.New("failed to get collaborators")).Once() + srv.On("Commit", ctx, mock.Anything).Return(doc, nil, nil) + w, r = getHTTPReqAndResp(ctx, nil) + h.Commit(w, r) + assert.Equal(t, w.Code, http.StatusInternalServerError) + assert.Contains(t, w.Body.String(), "failed to get collaborators") + + // success + doc.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, nil).Once() + doc.On("ID").Return(utils.RandomSlice(32)).Once() + doc.On("CurrentVersion").Return(utils.RandomSlice(32)).Once() + doc.On("Author").Return(nil, errors.New("somerror")).Once() + doc.On("Timestamp").Return(nil, errors.New("somerror")).Once() + doc.On("NFTs").Return(nil).Once() + doc.On("GetStatus").Return(documents.Committing).Once() + w, r = getHTTPReqAndResp(ctx, validPayload(t)) + h.Commit(w, r) + assert.Equal(t, http.StatusAccepted, w.Code) + assert.Contains(t, w.Body.String(), "\"status\":\"committing\"") + srv.AssertExpectations(t) + doc.AssertExpectations(t) +} + +func TestHandler_GetDocument(t *testing.T) { + getHTTPReqAndResp := func(ctx context.Context, b io.Reader) (*httptest.ResponseRecorder, *http.Request) { + return httptest.NewRecorder(), httptest.NewRequest("GET", "/documents/{document_id}/pending", b).WithContext(ctx) + } + + // invalid id + rctx := chi.NewRouteContext() + rctx.URLParams.Keys = make([]string, 1, 1) + rctx.URLParams.Values = make([]string, 1, 1) + rctx.URLParams.Keys[0] = "document_id" + rctx.URLParams.Values[0] = "some invalid id" + ctx := context.WithValue(context.Background(), chi.RouteCtxKey, rctx) + w, r := getHTTPReqAndResp(ctx, nil) + h := handler{} + h.getDocumentWithStatus(w, r, documents.Pending) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), coreapi.ErrInvalidDocumentID.Error()) + + // missing document + docID := utils.RandomSlice(32) + rctx.URLParams.Values[0] = hexutil.Encode(docID) + pendingSrv := new(pending.MockService) + pendingSrv.On("Get", ctx, docID, documents.Pending).Return(nil, coreapi.ErrDocumentNotFound).Once() + h.srv.pendingDocSrv = pendingSrv + w, r = getHTTPReqAndResp(ctx, nil) + h.getDocumentWithStatus(w, r, documents.Pending) + assert.Equal(t, w.Code, http.StatusNotFound) + assert.Contains(t, w.Body.String(), coreapi.ErrDocumentNotFound.Error()) + + // failed conversion + doc := new(testingdocuments.MockModel) + doc.On("GetData").Return(invoice.Data{}).Times(3) + doc.On("Scheme").Return("invoice").Times(3) + doc.On("GetAttributes").Return(nil).Times(3) + doc.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, errors.New("failed to get collaborators")).Once() + pendingSrv.On("Get", ctx, docID, mock.Anything).Return(doc, nil) + w, r = getHTTPReqAndResp(ctx, nil) + h.getDocumentWithStatus(w, r, documents.Pending) + assert.Equal(t, w.Code, http.StatusInternalServerError) + assert.Contains(t, w.Body.String(), "failed to get collaborators") + + // success pending + doc.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, nil).Twice() + doc.On("ID").Return(utils.RandomSlice(32)).Twice() + doc.On("CurrentVersion").Return(utils.RandomSlice(32)).Twice() + doc.On("Author").Return(nil, errors.New("somerror")).Twice() + doc.On("Timestamp").Return(nil, errors.New("somerror")).Twice() + doc.On("NFTs").Return(nil).Twice() + doc.On("GetStatus").Return(documents.Pending).Twice() + w, r = getHTTPReqAndResp(ctx, nil) + h.GetPendingDocument(w, r) + assert.Equal(t, http.StatusOK, w.Code) + w, r = getHTTPReqAndResp(ctx, nil) + h.GetCommittedDocument(w, r) + assert.Equal(t, http.StatusOK, w.Code) + pendingSrv.AssertExpectations(t) + doc.AssertExpectations(t) +} + +func TestHandler_GetDocumentVersion(t *testing.T) { + getHTTPReqAndResp := func(ctx context.Context) (*httptest.ResponseRecorder, *http.Request) { + return httptest.NewRecorder(), httptest.NewRequest("GET", "/documents/{document_id}/versions/{version_id}", nil).WithContext(ctx) + } + + // empty document_id and invalid + rctx := chi.NewRouteContext() + rctx.URLParams.Keys = make([]string, 2, 2) + rctx.URLParams.Values = make([]string, 2, 2) + rctx.URLParams.Keys[0] = "document_id" + rctx.URLParams.Keys[1] = "version_id" + ctx := context.WithValue(context.Background(), chi.RouteCtxKey, rctx) + h := handler{} + + for _, id := range []string{"", "invalid"} { + rctx.URLParams.Values[0] = id + rctx.URLParams.Values[1] = id + w, r := getHTTPReqAndResp(ctx) + h.GetDocumentVersion(w, r) + assert.Equal(t, w.Code, http.StatusBadRequest) + assert.Contains(t, w.Body.String(), coreapi.ErrInvalidDocumentID.Error()) + } + + // missing document + docID := utils.RandomSlice(32) + versionID := utils.RandomSlice(32) + rctx.URLParams.Values[0] = hexutil.Encode(docID) + rctx.URLParams.Values[1] = hexutil.Encode(versionID) + pendingSrv := new(pending.MockService) + pendingSrv.On("GetVersion", ctx, docID, versionID).Return(nil, coreapi.ErrDocumentNotFound).Once() + h.srv.pendingDocSrv = pendingSrv + w, r := getHTTPReqAndResp(ctx) + h.GetDocumentVersion(w, r) + assert.Equal(t, w.Code, http.StatusNotFound) + assert.Contains(t, w.Body.String(), coreapi.ErrDocumentNotFound.Error()) + + // failed conversion + doc := new(testingdocuments.MockModel) + doc.On("GetData").Return(invoice.Data{}).Times(2) + doc.On("Scheme").Return("invoice").Times(2) + doc.On("GetAttributes").Return(nil).Times(2) + doc.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, errors.New("failed to get collaborators")).Once() + pendingSrv.On("GetVersion", ctx, docID, versionID).Return(doc, nil) + w, r = getHTTPReqAndResp(ctx) + h.GetDocumentVersion(w, r) + assert.Equal(t, w.Code, http.StatusInternalServerError) + assert.Contains(t, w.Body.String(), "failed to get collaborators") + + // success pending + doc.On("GetCollaborators", mock.Anything).Return(documents.CollaboratorsAccess{}, nil).Once() + doc.On("ID").Return(utils.RandomSlice(32)).Once() + doc.On("CurrentVersion").Return(utils.RandomSlice(32)).Once() + doc.On("Author").Return(nil, errors.New("somerror")).Once() + doc.On("Timestamp").Return(nil, errors.New("somerror")).Once() + doc.On("NFTs").Return(nil).Once() + doc.On("GetStatus").Return(documents.Pending).Once() + w, r = getHTTPReqAndResp(ctx) + h.GetDocumentVersion(w, r) + assert.Equal(t, http.StatusOK, w.Code) + pendingSrv.AssertExpectations(t) + doc.AssertExpectations(t) +} diff --git a/httpapi/v2/handler.go b/httpapi/v2/handler.go new file mode 100644 index 000000000..900f80e77 --- /dev/null +++ b/httpapi/v2/handler.go @@ -0,0 +1,27 @@ +package v2 + +import ( + "github.com/centrifuge/go-centrifuge/httpapi/coreapi" + "github.com/go-chi/chi" + logging "github.com/ipfs/go-log" +) + +// handler implements the API handlers. +type handler struct { + srv Service +} + +var log = logging.Logger("v2_api") + +// Register registers the core apis to the router. +func Register(ctx map[string]interface{}, r chi.Router) { + srv := ctx[BootstrappedService].(Service) + h := handler{srv: srv} + + r.Post("/documents", h.CreateDocument) + r.Patch("/documents/{"+coreapi.DocumentIDParam+"}", h.UpdateDocument) + r.Post("/documents/{"+coreapi.DocumentIDParam+"}/commit", h.Commit) + r.Get("/documents/{"+coreapi.DocumentIDParam+"}/pending", h.GetPendingDocument) + r.Get("/documents/{"+coreapi.DocumentIDParam+"}/committed", h.GetCommittedDocument) + r.Get("/documents/{"+coreapi.DocumentIDParam+"}/versions/{"+coreapi.VersionIDParam+"}", h.GetDocumentVersion) +} diff --git a/httpapi/v2/handler_test.go b/httpapi/v2/handler_test.go new file mode 100644 index 000000000..4bbafe819 --- /dev/null +++ b/httpapi/v2/handler_test.go @@ -0,0 +1,17 @@ +// +build unit + +package v2 + +import ( + "testing" + + "github.com/go-chi/chi" + "github.com/stretchr/testify/assert" +) + +func TestRegister(t *testing.T) { + r := chi.NewRouter() + ctx := map[string]interface{}{BootstrappedService: Service{}} + Register(ctx, r) + assert.Len(t, r.Routes(), 6) +} diff --git a/httpapi/v2/service.go b/httpapi/v2/service.go new file mode 100644 index 000000000..55c31c333 --- /dev/null +++ b/httpapi/v2/service.go @@ -0,0 +1,41 @@ +package v2 + +import ( + "context" + + "github.com/centrifuge/go-centrifuge/documents" + "github.com/centrifuge/go-centrifuge/jobs" + "github.com/centrifuge/go-centrifuge/pending" +) + +// Service is the entry point for all the V2 APIs. +type Service struct { + pendingDocSrv pending.Service + tokenRegistry documents.TokenRegistry +} + +// CreateDocument creates a pending document from the given payload. +// if the document_id is provided, next version of the document is created. +func (s Service) CreateDocument(ctx context.Context, req documents.UpdatePayload) (documents.Model, error) { + return s.pendingDocSrv.Create(ctx, req) +} + +// UpdateDocument updates a pending document with the given payload +func (s Service) UpdateDocument(ctx context.Context, req documents.UpdatePayload) (documents.Model, error) { + return s.pendingDocSrv.Update(ctx, req) +} + +// Commit creates a document out of a pending document. +func (s Service) Commit(ctx context.Context, docID []byte) (documents.Model, jobs.JobID, error) { + return s.pendingDocSrv.Commit(ctx, docID) +} + +// GetDocument returns the document associated with docID and status. +func (s Service) GetDocument(ctx context.Context, docID []byte, status documents.Status) (documents.Model, error) { + return s.pendingDocSrv.Get(ctx, docID, status) +} + +// GetDocumentVersion returns the specific version of the document. +func (s Service) GetDocumentVersion(ctx context.Context, docID, versionID []byte) (documents.Model, error) { + return s.pendingDocSrv.GetVersion(ctx, docID, versionID) +} diff --git a/documents/entity/test_bootstrapper.go b/httpapi/v2/test_v2.go similarity index 79% rename from documents/entity/test_bootstrapper.go rename to httpapi/v2/test_v2.go index d939fc500..8768bc5b8 100644 --- a/documents/entity/test_bootstrapper.go +++ b/httpapi/v2/test_v2.go @@ -1,6 +1,6 @@ -// +build integration unit +// +build unit integration -package entity +package v2 func (b Bootstrapper) TestBootstrap(context map[string]interface{}) error { return b.Bootstrap(context) diff --git a/identity/converters.go b/identity/converters.go index b046a76ea..dae818b2a 100644 --- a/identity/converters.go +++ b/identity/converters.go @@ -4,7 +4,6 @@ import ( "strings" "github.com/centrifuge/go-centrifuge/utils" - "github.com/ethereum/go-ethereum/common" ) // StringsToDIDs converts hex strings to DIDs. @@ -122,21 +121,3 @@ func RemoveDuplicateDIDs(dids []DID) []DID { return res } - -// AddressToDIDs converts addresses to DIDs. -func AddressToDIDs(addrs ...common.Address) (dids []DID) { - for _, addr := range addrs { - dids = append(dids, NewDID(addr)) - } - - return dids -} - -// DIDsToAddress converts DIDs to addresses. -func DIDsToAddress(dids ...DID) (addrs []common.Address) { - for _, did := range dids { - addrs = append(addrs, did.ToAddress()) - } - - return addrs -} diff --git a/identity/converters_test.go b/identity/converters_test.go index 89a463f47..961899881 100644 --- a/identity/converters_test.go +++ b/identity/converters_test.go @@ -44,10 +44,6 @@ func TestDIDsPointers(t *testing.T) { vdids := FromPointerDIDs(pdids...) assert.Len(t, vdids, 3) - addrs := DIDsToAddress(vdids...) - adids := AddressToDIDs(addrs...) - assert.Equal(t, vdids, adids) - epdids := DIDsPointers(vdids...) assert.Equal(t, pdids, epdids) } diff --git a/identity/did.go b/identity/did.go index 3a23d8f25..cd9c5ccba 100644 --- a/identity/did.go +++ b/identity/did.go @@ -104,7 +104,7 @@ func GetPurposeByName(name string) Purpose { } // DID stores the identity address of the user -type DID common.Address +type DID [DIDLength]byte // DIDLength contains the length of a DID const DIDLength = common.AddressLength @@ -211,10 +211,10 @@ type Service interface { GetKey(did DID, key [32]byte) (*KeyResponse, error) // RawExecute calls the execute method on the identity contract - RawExecute(ctx context.Context, to common.Address, data []byte, gasLimit uint64) (txID IDTX, done chan bool, err error) + RawExecute(ctx context.Context, to common.Address, data []byte, gasLimit uint64) (txID IDTX, done chan error, err error) - // Execute creates the abi encoding an calls the execute method on the identity contract - Execute(ctx context.Context, to common.Address, contractAbi, methodName string, args ...interface{}) (txID IDTX, done chan bool, err error) + // Execute creates the abi encoding and calls the execute method on the identity contract + Execute(ctx context.Context, to common.Address, contractAbi, methodName string, args ...interface{}) (txID IDTX, done chan error, err error) // AddMultiPurposeKey adds a key with multiple purposes AddMultiPurposeKey(context context.Context, key [32]byte, purposes []*big.Int, keyType *big.Int) error diff --git a/identity/ideth/execute_integration_test.go b/identity/ideth/execute_integration_test.go index 0ff50a087..cb36c9dc1 100644 --- a/identity/ideth/execute_integration_test.go +++ b/identity/ideth/execute_integration_test.go @@ -71,9 +71,9 @@ func TestExecute_successful(t *testing.T) { // call execute _, done, err := idSrv.Execute(aCtx, anchorAddress, anchors.AnchorContractABI, "commit", testAnchorIdPreimage.BigInt(), testRootHash, proofs) - isDone := <-done - assert.True(t, isDone) assert.Nil(t, err, "Execute method calls should be successful") + doneErr := <-done + assert.NoError(t, doneErr) checkAnchor(t, testAnchorId, rootHash) resetDefaultCentID() @@ -181,10 +181,8 @@ func commitAnchorWithoutExecute(t *testing.T, anchorContract *anchors.AnchorCont errOut <- nil }) assert.Nil(t, err, "add anchor commit tx should be successful ") - isDone := <-done - // non async task - - assert.True(t, isDone, "add anchor commit tx should be successful ") + doneErr := <-done + assert.Nil(t, doneErr, "add anchor commit tx should be successful ") return nil diff --git a/identity/ideth/factory.go b/identity/ideth/factory.go index c51a69c0f..b9dbe7cc3 100644 --- a/identity/ideth/factory.go +++ b/identity/ideth/factory.go @@ -134,10 +134,10 @@ func (s *factory) CreateIdentity(ctx context.Context) (did *id.DID, err error) { return nil, err } - isDone := <-done + err = <-done // non async task - if !isDone { - return nil, errors.New("Create Identity Job failed: jobID:%s", jobID.String()) + if err != nil { + return nil, errors.New("Create Identity Job failed: jobID:%s with error [%s]", jobID.String(), err) } tx, err := s.jobManager.GetJob(createdDID, jobID) diff --git a/identity/ideth/service.go b/identity/ideth/service.go index 034c4bdfb..ec0d23443 100644 --- a/identity/ideth/service.go +++ b/identity/ideth/service.go @@ -138,10 +138,10 @@ func (i service) AddKey(ctx context.Context, key id.Key) error { return err } - isDone := <-done + err = <-done // non async task - if !isDone { - return errors.New("add key Job failed: jobID:%s", jobID.String()) + if err != nil { + return errors.New("add key Job failed: jobID:%s with error [%s]", jobID.String(), err) } return nil @@ -167,11 +167,9 @@ func (i service) AddMultiPurposeKey(ctx context.Context, key [32]byte, purposes return err } - isDone := <-done - // non async task - if !isDone { - return errors.New("add key multi purpose Job failed: jobID:%s", jobID.String()) - + err = <-done + if err != nil { + return errors.New("add key multi purpose Job failed: jobID[%s] with error [%s]", jobID.String(), err.Error()) } return nil } @@ -195,10 +193,10 @@ func (i service) RevokeKey(ctx context.Context, key [32]byte) error { return err } - isDone := <-done + err = <-done // non async task - if !isDone { - return errors.New("revoke key Job failed: jobID:%s", jobID.String()) + if err != nil { + return errors.New("revoke key Job failed: jobID:%s with error [%s]", jobID.String(), err.Error()) } return nil @@ -246,7 +244,7 @@ func (i service) GetKey(did id.DID, key [32]byte) (*id.KeyResponse, error) { // RawExecute calls the execute method on the identity contract // TODO once we clean up transaction to not use higher level deps we can change back the return to be transactions.txID -func (i service) RawExecute(ctx context.Context, to common.Address, data []byte, gasLimit uint64) (txID id.IDTX, done chan bool, err error) { +func (i service) RawExecute(ctx context.Context, to common.Address, data []byte, gasLimit uint64) (txID id.IDTX, done chan error, err error) { jobID := contextutil.Job(ctx) did, err := NewDIDFromContext(ctx) if err != nil { @@ -265,7 +263,7 @@ func (i service) RawExecute(ctx context.Context, to common.Address, data []byte, // Execute creates the abi encoding an calls the execute method on the identity contract // TODO once we clean up transaction to not use higher level deps we can change back the return to be transactions.txID -func (i service) Execute(ctx context.Context, to common.Address, contractAbi, methodName string, args ...interface{}) (txID id.IDTX, done chan bool, err error) { +func (i service) Execute(ctx context.Context, to common.Address, contractAbi, methodName string, args ...interface{}) (txID id.IDTX, done chan error, err error) { abiObj, err := abi.JSON(strings.NewReader(contractAbi)) if err != nil { return jobs.NilJobID(), nil, err @@ -471,9 +469,6 @@ func NewDIDFromContext(ctx context.Context) (id.DID, error) { return id.DID{}, err } - addressByte, err := tc.GetIdentityID() - if err != nil { - return id.DID{}, err - } + addressByte := tc.GetIdentityID() return id.NewDID(common.BytesToAddress(addressByte)), nil } diff --git a/jobs/job.go b/jobs/job.go index 9f02b96f3..e12e221c4 100644 --- a/jobs/job.go +++ b/jobs/job.go @@ -174,7 +174,7 @@ type Config interface { // Manager is a manager for centrifuge Jobs. type Manager interface { // ExecuteWithinJob executes the given unit of work within a Job - ExecuteWithinJob(ctx context.Context, accountID identity.DID, existingJobID JobID, desc string, work func(accountID identity.DID, jobID JobID, jobManager Manager, err chan<- error)) (jobID JobID, done chan bool, err error) + ExecuteWithinJob(ctx context.Context, accountID identity.DID, existingJobID JobID, desc string, work func(accountID identity.DID, jobID JobID, jobManager Manager, err chan<- error)) (jobID JobID, done chan error, err error) GetJob(accountID identity.DID, id JobID) (*Job, error) UpdateJobWithValue(accountID identity.DID, id JobID, key string, value []byte) error UpdateTaskStatus(accountID identity.DID, id JobID, status Status, taskName, message string) error diff --git a/jobs/jobsv1/manager.go b/jobs/jobsv1/manager.go index a9484fddf..5ed913a81 100644 --- a/jobs/jobsv1/manager.go +++ b/jobs/jobsv1/manager.go @@ -5,12 +5,10 @@ import ( "fmt" "time" - notificationpb "github.com/centrifuge/centrifuge-protobufs/gen/go/notification" "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/go-centrifuge/identity" "github.com/centrifuge/go-centrifuge/jobs" "github.com/centrifuge/go-centrifuge/notification" - "github.com/centrifuge/go-centrifuge/utils" ) const ( @@ -69,7 +67,7 @@ func (s *manager) UpdateTaskStatus(accountID identity.DID, id jobs.JobID, status } // ExecuteWithinJob executes a task within a Job. -func (s *manager) ExecuteWithinJob(ctx context.Context, accountID identity.DID, existingJobID jobs.JobID, desc string, work func(accountID identity.DID, txID jobs.JobID, txMan jobs.Manager, err chan<- error)) (txID jobs.JobID, done chan bool, err error) { +func (s *manager) ExecuteWithinJob(ctx context.Context, accountID identity.DID, existingJobID jobs.JobID, desc string, work func(accountID identity.DID, txID jobs.JobID, txMan jobs.Manager, err chan<- error)) (txID jobs.JobID, done chan error, err error) { job, err := s.repo.Get(accountID, existingJobID) if err != nil { job = jobs.NewJob(accountID, desc) @@ -79,17 +77,19 @@ func (s *manager) ExecuteWithinJob(ctx context.Context, accountID identity.DID, } } // set capacity to one so that any late listener won't miss updates. - done = make(chan bool, 1) + done = make(chan error, 1) go func(ctx context.Context) { err := make(chan error) go work(accountID, job.ID, s, err) var mJob *jobs.Job + var doneErr error select { case e := <-err: tempJob, err := s.repo.Get(accountID, job.ID) if err != nil { log.Error(e, err) + doneErr = errors.AppendError(e, err) break } // update job success status only if this wasn't an existing job. @@ -99,12 +99,16 @@ func (s *manager) ExecuteWithinJob(ctx context.Context, accountID identity.DID, if e == nil && jobs.JobIDEqual(existingJobID, jobs.NilJobID()) { tempJob.Status = jobs.Success } else if e != nil { - tempJob.Logs = append(tempJob.Logs, jobs.NewLog(fmt.Sprintf("%s[%s]", managerLogPrefix, desc), e.Error())) + log.Error(e) + action := fmt.Sprintf("%s[%s]", managerLogPrefix, desc) + doneErr = fmt.Errorf(fmt.Sprintf("%s %s", action, e.Error())) + tempJob.Logs = append(tempJob.Logs, jobs.NewLog(action, e.Error())) tempJob.Status = jobs.Failed } - e = s.saveJob(tempJob) - if e != nil { - log.Error(e) + es := s.saveJob(tempJob) + if es != nil { + log.Error(e, es) + doneErr = errors.AppendError(e, es) } mJob = tempJob case <-ctx.Done(): @@ -113,44 +117,42 @@ func (s *manager) ExecuteWithinJob(ctx context.Context, accountID identity.DID, tempJob, err := s.repo.Get(accountID, job.ID) if err != nil { log.Error(err) + doneErr = err break } tempJob.Logs = append(tempJob.Logs, jobs.NewLog("context closed", msg)) e := s.saveJob(tempJob) if e != nil { log.Error(e) + doneErr = e } mJob = tempJob } // non blocking send select { - case done <- true: + case done <- doneErr: default: // must not happen log.Error("job done channel capacity breach") } if mJob != nil && jobs.JobIDEqual(existingJobID, jobs.NilJobID()) { - ts, err1 := utils.ToTimestamp(time.Now().UTC()) - if err1 != nil { - log.Error(err1) - } - notificationMsg := ¬ificationpb.NotificationMessage{ - EventType: uint32(notification.JobCompleted), - AccountId: accountID.String(), - Recorded: ts, + notificationMsg := notification.Message{ + EventType: notification.JobCompleted, + AccountID: accountID.String(), + Recorded: time.Now().UTC(), DocumentType: jobs.JobDataTypeURL, - DocumentId: mJob.ID.String(), + DocumentID: mJob.ID.String(), Status: string(mJob.Status), } if len(mJob.Logs) > 0 { notificationMsg.Message = mJob.Logs[len(mJob.Logs)-1].Message } // Send Job notification webhook - _, err1 = s.notifier.Send(ctx, notificationMsg) - if err1 != nil { - log.Error(err1) + _, err := s.notifier.Send(ctx, notificationMsg) + if err != nil { + log.Error(err) } } diff --git a/jobs/jobsv1/manager_test.go b/jobs/jobsv1/manager_test.go index 3650c8ebc..a1d159f8b 100644 --- a/jobs/jobsv1/manager_test.go +++ b/jobs/jobsv1/manager_test.go @@ -8,7 +8,6 @@ import ( "testing" "time" - notificationpb "github.com/centrifuge/centrifuge-protobufs/gen/go/notification" "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/go-centrifuge/identity" "github.com/centrifuge/go-centrifuge/jobs" @@ -24,12 +23,12 @@ func (mockConfig) GetEthereumContextWaitTimeout() time.Duration { panic("implement me") } -var sendChan chan *notificationpb.NotificationMessage +var sendChan chan notification.Message type mockSender struct{} // Send mocks failure and returns to channel -func (mockSender) Send(ctx context.Context, ntf *notificationpb.NotificationMessage) (notification.Status, error) { +func (mockSender) Send(ctx context.Context, ntf notification.Message) (notification.Status, error) { sendChan <- ntf return notification.Failure, nil } @@ -41,8 +40,8 @@ func TestService_ExecuteWithinTX_happy(t *testing.T) { err <- nil }) assert.NoError(t, err) - <-done - assert.NoError(t, err) + doneErr := <-done + assert.NoError(t, doneErr) assert.NotNil(t, jobID) job, err := srv.GetJob(did, jobID) assert.NoError(t, err) @@ -57,14 +56,15 @@ func TestService_ExecuteWithinTX_err(t *testing.T) { mngr := NewManager(msrv.config, msrv.repo) omgr := mngr.(*manager) omgr.notifier = &mockSender{} - sendChan = make(chan *notificationpb.NotificationMessage) + sendChan = make(chan notification.Message) jobID, done, err := omgr.ExecuteWithinJob(context.Background(), did, jobs.NilJobID(), "SomeTask", func(accountID identity.DID, jobID jobs.JobID, txMan jobs.Manager, err chan<- error) { err <- errors.New(errStr) }) assert.NoError(t, err) - <-done + doneErr := <-done + assert.Error(t, doneErr) ntf := <-sendChan - assert.Equal(t, uint32(notification.JobCompleted), ntf.EventType) + assert.Equal(t, notification.JobCompleted, ntf.EventType) assert.Equal(t, jobs.JobDataTypeURL, ntf.DocumentType) assert.Equal(t, string(jobs.Failed), ntf.Status) assert.Equal(t, errStr, ntf.Message) @@ -87,8 +87,8 @@ func TestService_ExecuteWithinTX_ctxDone(t *testing.T) { }) canc() assert.NoError(t, err) - <-done - assert.NoError(t, err) + doneErr := <-done + assert.NoError(t, doneErr) assert.NotNil(t, tid) job, err := srv.GetJob(did, tid) assert.NoError(t, err) diff --git a/migration/files/03AddDocumentIndex.go b/migration/files/03AddDocumentIndex.go new file mode 100644 index 000000000..fd4dac16c --- /dev/null +++ b/migration/files/03AddDocumentIndex.go @@ -0,0 +1,77 @@ +package migrationfiles + +import ( + "strings" + + "github.com/centrifuge/go-centrifuge/documents" + "github.com/centrifuge/go-centrifuge/documents/entity" + "github.com/centrifuge/go-centrifuge/documents/entityrelationship" + "github.com/centrifuge/go-centrifuge/documents/generic" + "github.com/centrifuge/go-centrifuge/documents/invoice" + "github.com/centrifuge/go-centrifuge/errors" + "github.com/centrifuge/go-centrifuge/storage/leveldb" + "github.com/ethereum/go-ethereum/common/hexutil" + ldb "github.com/syndtr/goleveldb/leveldb" + "github.com/syndtr/goleveldb/leveldb/util" +) + +// AddDocumentIndex03 adds index to the document for efficient fetching. +func AddDocumentIndex03(db *ldb.DB) error { + strRepo := leveldb.NewLevelDBRepository(db) + repo := documents.NewDBRepository(strRepo) + repo.Register(new(invoice.Invoice)) + repo.Register(new(entityrelationship.EntityRelationship)) + repo.Register(new(entity.Entity)) + repo.Register(new(generic.Generic)) + iter := db.NewIterator(util.BytesPrefix([]byte("document_")), nil) + var c, e int + for iter.Next() { + c++ + key := iter.Key() + acc, id, err := getAccountAndID(key) + if err != nil { + e++ + // must have been an older document. skipping + continue + } + m, err := strRepo.Get(key) + if err != nil { + return err + } + + mm, ok := m.(documents.Model) + if !ok { + return documents.ErrDocumentInvalidType + } + + err = repo.Update(acc, id, mm) + if err != nil { + return err + } + + } + log.Infof("Updated index for %d documents\n", c-e) + iter.Release() + err := iter.Error() + if err != nil { + return err + } + + log.Infof("AddDocumentIndex03 Migration Run successfully") + return nil +} + +func getAccountAndID(key []byte) (acc, id []byte, err error) { + str := strings.TrimSpace(strings.TrimPrefix(string(key), "document_")) + d, err := hexutil.Decode(str) + if err != nil { + return nil, nil, err + } + + if len(d) != 52 { + return nil, nil, errors.New("invalid key(%v) of length %d found", string(key), len(d)) + } + + // first 20 bytes are account and last 32 are id + return d[:20], d[20:], nil +} diff --git a/migration/files/04AddStatusToDocuments.go b/migration/files/04AddStatusToDocuments.go new file mode 100644 index 000000000..8ba33cef2 --- /dev/null +++ b/migration/files/04AddStatusToDocuments.go @@ -0,0 +1,60 @@ +package migrationfiles + +import ( + "github.com/centrifuge/go-centrifuge/documents" + "github.com/centrifuge/go-centrifuge/documents/entity" + "github.com/centrifuge/go-centrifuge/documents/entityrelationship" + "github.com/centrifuge/go-centrifuge/documents/generic" + "github.com/centrifuge/go-centrifuge/documents/invoice" + "github.com/centrifuge/go-centrifuge/storage/leveldb" + ldb "github.com/syndtr/goleveldb/leveldb" + "github.com/syndtr/goleveldb/leveldb/util" +) + +// AddStatusToDocuments04 adds status to committed. +func AddStatusToDocuments04(db *ldb.DB) error { + strRepo := leveldb.NewLevelDBRepository(db) + repo := documents.NewDBRepository(strRepo) + repo.Register(new(invoice.Invoice)) + repo.Register(new(entityrelationship.EntityRelationship)) + repo.Register(new(entity.Entity)) + repo.Register(new(generic.Generic)) + iter := db.NewIterator(util.BytesPrefix([]byte("document_")), nil) + var c, e int + for iter.Next() { + c++ + key := iter.Key() + m, err := strRepo.Get(key) + if err != nil { + // model fetch failed, skip + e++ + continue + } + + mm, ok := m.(documents.Model) + if !ok { + return documents.ErrDocumentInvalidType + } + + err = mm.SetStatus(documents.Committed) + if err != nil { + return err + } + + err = strRepo.Update(key, mm) + if err != nil { + return err + } + + } + + log.Infof("Updated status for %d documents\n", c-e) + iter.Release() + err := iter.Error() + if err != nil { + return err + } + + log.Infof("AddStatusToDocuments04 Migration Run successfully") + return nil +} diff --git a/migration/files/04AddStatusToDocuments_test.go b/migration/files/04AddStatusToDocuments_test.go new file mode 100644 index 000000000..5d1581e41 --- /dev/null +++ b/migration/files/04AddStatusToDocuments_test.go @@ -0,0 +1,42 @@ +// +build unit + +package migrationfiles + +import ( + "fmt" + "testing" + + "github.com/centrifuge/go-centrifuge/documents" + "github.com/centrifuge/go-centrifuge/documents/invoice" + migrationutils "github.com/centrifuge/go-centrifuge/migration/utils" + "github.com/centrifuge/go-centrifuge/storage/leveldb" + testingidentity "github.com/centrifuge/go-centrifuge/testingutils/identity" + "github.com/stretchr/testify/assert" + ldb "github.com/syndtr/goleveldb/leveldb" +) + +func TestAddStatusToDocuments04(t *testing.T) { + prefix := fmt.Sprintf("/tmp/datadir_%x", migrationutils.RandomByte32()) + targetDir := fmt.Sprintf("%s.leveldb", prefix) + + // Cleanup after test + defer migrationutils.CleanupDBFiles(prefix) + + db, err := ldb.OpenFile(targetDir, nil) + assert.NoError(t, err) + strRepo := leveldb.NewLevelDBRepository(db) + repo := documents.NewDBRepository(strRepo) + repo.Register(new(invoice.Invoice)) + did := testingidentity.GenerateRandomDID() + + // successful change + inv := invoice.InitInvoice(t, did, invoice.CreateInvoicePayload(t, nil)) + assert.Equal(t, inv.GetStatus(), documents.Pending) + assert.NoError(t, repo.Create(did[:], inv.CurrentVersion(), inv)) + assert.NoError(t, AddStatusToDocuments04(db)) + m, err := repo.Get(did[:], inv.CurrentVersion()) + assert.NoError(t, err) + inv, ok := m.(*invoice.Invoice) + assert.True(t, ok) + assert.Equal(t, inv.GetStatus(), documents.Committed) +} diff --git a/migration/wrapper.go b/migration/wrapper.go index 38dedd514..80e7023f9 100644 --- a/migration/wrapper.go +++ b/migration/wrapper.go @@ -19,8 +19,11 @@ import ( var log = logging.Logger("migrate-cmd") var migrations = map[string]func(*leveldb.DB) error{ - "00Initial": mfiles.Initial00, - "01KeysToHex": mfiles.KeysToHex01, + "00Initial": mfiles.Initial00, + "01KeysToHex": mfiles.KeysToHex01, + "02AddPrefix": mfiles.AddPrefix02, + "03AddDocumentIndex": mfiles.AddDocumentIndex03, + "04AddStatusToDocuments": mfiles.AddStatusToDocuments04, } // Runner is the actor that runs the migrations diff --git a/nft/handler.go b/nft/handler.go deleted file mode 100644 index 23e2790e0..000000000 --- a/nft/handler.go +++ /dev/null @@ -1,72 +0,0 @@ -package nft - -import ( - "context" - - "github.com/centrifuge/go-centrifuge/centerrors" - "github.com/centrifuge/go-centrifuge/code" - "github.com/centrifuge/go-centrifuge/config" - "github.com/centrifuge/go-centrifuge/contextutil" - "github.com/centrifuge/go-centrifuge/protobufs/gen/go/nft" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - logging "github.com/ipfs/go-log" -) - -var apiLog = logging.Logger("nft-api") - -type grpcHandler struct { - config config.Service - service InvoiceUnpaid -} - -// GRPCHandler returns an implementation of invoice.DocumentServiceServer -func GRPCHandler(config config.Service, InvoiceUnpaid InvoiceUnpaid) nftpb.NFTServiceServer { - return &grpcHandler{config: config, service: InvoiceUnpaid} -} - -// MintInvoiceUnpaidNFT will be called from the client API to mint an NFT out of an unpaid invoice -func (g grpcHandler) MintInvoiceUnpaidNFT(ctx context.Context, request *nftpb.NFTMintInvoiceUnpaidRequest) (*nftpb.NFTMintResponse, error) { - apiLog.Infof("Received request to Mint an Invoice Unpaid NFT for invoice %s and deposit address %s", request.DocumentId, request.DepositAddress) - ctxHeader, err := contextutil.Context(ctx, g.config) - if err != nil { - apiLog.Error(err) - return nil, err - } - - // Get proof fields - proofFields, err := g.service.GetRequiredInvoiceUnpaidProofFields(ctxHeader) - if err != nil { - return nil, centerrors.New(code.Unknown, err.Error()) - } - - cfg, err := g.config.GetConfig() - if err != nil { - return nil, centerrors.New(code.Unknown, err.Error()) - } - poRegistry := cfg.GetContractAddress(config.InvoiceUnpaidNFT) - - identifier, err := hexutil.Decode(request.DocumentId) - if err != nil { - return nil, err - } - - req := MintNFTRequest{ - DocumentID: identifier, - RegistryAddress: poRegistry, - DepositAddress: common.HexToAddress(request.DepositAddress), - ProofFields: proofFields, - GrantNFTReadAccess: true, - SubmitNFTReadAccessProof: true, - SubmitTokenProof: true, - } - - resp, _, err := g.service.MintNFT(ctxHeader, req) - if err != nil { - return nil, centerrors.New(code.Unknown, err.Error()) - } - - return &nftpb.NFTMintResponse{ - Header: &nftpb.ResponseHeader{JobId: resp.JobID}, - }, nil -} diff --git a/nft/handler_test.go b/nft/handler_test.go deleted file mode 100644 index 148a4411c..000000000 --- a/nft/handler_test.go +++ /dev/null @@ -1,97 +0,0 @@ -// +build unit - -package nft - -import ( - "context" - "math/big" - "testing" - - "github.com/centrifuge/go-centrifuge/config" - "github.com/centrifuge/go-centrifuge/config/configstore" - "github.com/centrifuge/go-centrifuge/errors" - "github.com/centrifuge/go-centrifuge/protobufs/gen/go/nft" - "github.com/centrifuge/go-centrifuge/testingutils/config" - "github.com/ethereum/go-ethereum/common" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - -type mockInvoiceUnpaid struct { - mock.Mock -} - -func (m *mockInvoiceUnpaid) MintNFT(ctx context.Context, request MintNFTRequest) (*TokenResponse, chan bool, error) { - args := m.Called(ctx, request) - resp, _ := args.Get(0).(*TokenResponse) - return resp, nil, args.Error(1) -} - -func (m *mockInvoiceUnpaid) GetRequiredInvoiceUnpaidProofFields(ctx context.Context) ([]string, error) { - args := m.Called(ctx) - resp, _ := args.Get(0).([]string) - return resp, args.Error(1) -} - -func (m *mockInvoiceUnpaid) TransferFrom(ctx context.Context, registry common.Address, to common.Address, tokenID TokenID) (*TokenResponse, chan bool, error) { - args := m.Called(ctx) - resp, _ := args.Get(0).(*TokenResponse) - return resp, nil, args.Error(1) -} - -func (m *mockInvoiceUnpaid) OwnerOf(registry common.Address, tokenID []byte) (owner common.Address, err error) { - args := m.Called(registry, tokenID) - resp, _ := args.Get(0).(common.Address) - return resp, args.Error(1) -} - -func TestPaymentObligationNFTMint_success(t *testing.T) { - mockService := &mockInvoiceUnpaid{} - mockConfigStore := mockmockConfigStore() - tokID := big.NewInt(1) - nftResponse := &TokenResponse{TokenID: tokID.String()} - nftReq := &nftpb.NFTMintInvoiceUnpaidRequest{ - DocumentId: "0x1234567890", - DepositAddress: "0xf72855759a39fb75fc7341139f5d7a3974d4da08", - } - - // error no account header - handler := grpcHandler{mockConfigStore, mockService} - nftMintResponse, err := handler.MintInvoiceUnpaidNFT(context.Background(), nftReq) - mockService.AssertExpectations(t) - assert.Error(t, err) - assert.Nil(t, nftMintResponse) - - // error generate proofs - mockService.On("GetRequiredInvoiceUnpaidProofFields", mock.Anything).Return(nil, errors.New("fail")).Once() - handler = grpcHandler{mockConfigStore, mockService} - nftMintResponse, err = handler.MintInvoiceUnpaidNFT(testingconfig.HandlerContext(mockConfigStore), nftReq) - mockService.AssertExpectations(t) - assert.Error(t, err) - assert.Nil(t, nftMintResponse) - - // error get config - mockService.On("GetRequiredInvoiceUnpaidProofFields", mock.Anything).Return([]string{"proof1", "proof2"}, nil).Once() - mockConfigStore.On("GetConfig").Return(cfg, errors.New("fail")).Once() - handler = grpcHandler{mockConfigStore, mockService} - nftMintResponse, err = handler.MintInvoiceUnpaidNFT(testingconfig.HandlerContext(mockConfigStore), nftReq) - mockService.AssertExpectations(t) - assert.Error(t, err) - assert.Nil(t, nftMintResponse) - - // success assertions - mockService.On("MintNFT", mock.Anything, mock.Anything).Return(nftResponse, nil).Once() - mockService.On("GetRequiredInvoiceUnpaidProofFields", mock.Anything).Return([]string{"proof1", "proof2"}, nil).Once() - mockConfigStore.On("GetConfig").Return(cfg, nil).Once() - handler = grpcHandler{mockConfigStore, mockService} - nftMintResponse, err = handler.MintInvoiceUnpaidNFT(testingconfig.HandlerContext(mockConfigStore), nftReq) - mockService.AssertExpectations(t) - assert.Nil(t, err, "mint nft should be successful") -} - -func mockmockConfigStore() *configstore.MockService { - mockConfigStore := &configstore.MockService{} - mockConfigStore.On("GetAccount", mock.Anything).Return(&configstore.Account{}, nil) - mockConfigStore.On("GetAllAccounts").Return([]config.Account{&configstore.Account{}}, nil) - return mockConfigStore -} diff --git a/nft/invoice_unpaid.go b/nft/invoice_unpaid.go index b9c309c84..63466de63 100644 --- a/nft/invoice_unpaid.go +++ b/nft/invoice_unpaid.go @@ -77,25 +77,19 @@ type MintNFTRequest struct { GrantNFTReadAccess bool SubmitTokenProof bool SubmitNFTReadAccessProof bool + UseGeneric bool } // Service defines the NFT service to mint and transfer NFTs. type Service interface { // MintNFT mints an NFT - MintNFT(ctx context.Context, request MintNFTRequest) (*TokenResponse, chan bool, error) + MintNFT(ctx context.Context, request MintNFTRequest) (*TokenResponse, chan error, error) // TransferFrom transfers an NFT to another address - TransferFrom(ctx context.Context, registry common.Address, to common.Address, tokenID TokenID) (*TokenResponse, chan bool, error) + TransferFrom(ctx context.Context, registry common.Address, to common.Address, tokenID TokenID) (*TokenResponse, chan error, error) // OwnerOf returns the owner of an NFT OwnerOf(registry common.Address, tokenID []byte) (owner common.Address, err error) } -// InvoiceUnpaid handles transactions related to minting of NFTs for unpaid invoices -type InvoiceUnpaid interface { - Service - // GetRequiredInvoiceUnpaidProofFields returns the required proof field properties - GetRequiredInvoiceUnpaidProofFields(ctx context.Context) ([]string, error) -} - // TokenResponse holds tokenID and transaction ID. type TokenResponse struct { TokenID string diff --git a/nft/service.go b/nft/service.go index 5715a9d6e..a64f66a41 100644 --- a/nft/service.go +++ b/nft/service.go @@ -2,8 +2,8 @@ package nft import ( "context" - "encoding/hex" - "fmt" + "crypto/sha256" + "encoding/json" "math/big" "time" @@ -16,8 +16,7 @@ import ( "github.com/centrifuge/go-centrifuge/jobs" "github.com/centrifuge/go-centrifuge/queue" "github.com/centrifuge/go-centrifuge/utils" - "github.com/centrifuge/go-centrifuge/utils/byteutils" - "github.com/centrifuge/go-centrifuge/utils/stringutils" + "github.com/centrifuge/precise-proofs/proofs" "github.com/centrifuge/precise-proofs/proofs/proto" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -29,6 +28,9 @@ var log = logging.Logger("nft") const ( // ErrNFTMinted error for NFT already minted for registry ErrNFTMinted = errors.Error("NFT already minted") + + // GenericMintMethodABI constant interface to interact with mint methods + GenericMintMethodABI = `[{"constant":false,"inputs":[{"name":"usr","type":"address"},{"name":"tkn","type":"uint256"},{"name":"anchor","type":"uint256"},{"name":"data_root","type":"bytes32"},{"name":"signatures_root","type":"bytes32"},{"name":"properties","type":"bytes[]"},{"name":"values","type":"bytes[]"},{"name":"salts","type":"bytes32[]"},{"name":"proofs","type":"bytes32[][]"}],"name":"mint","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]` ) // Config is the config interface for nft package @@ -71,21 +73,6 @@ func newService( } } -func (s *service) filterMintProofs(docProof *documents.DocumentProof) *documents.DocumentProof { - // Compact properties - var nonFilteredProofsLiteral = [][]byte{append(documents.CompactProperties(documents.DRTreePrefix), documents.CompactProperties(documents.SigningRootField)...)} - // Byte array Regex - (signatureTreePrefix + signatureProp) + Index[up to 104 characters (52bytes*2)] + Signature key - m0 := append(documents.CompactProperties(documents.SignaturesTreePrefix), []byte{0, 0, 0, 1}...) - var nonFilteredProofsMatch = []string{fmt.Sprintf("%s(.{104})%s", hex.EncodeToString(m0), hex.EncodeToString([]byte{0, 0, 0, 4}))} - - for i, p := range docProof.FieldProofs { - if !byteutils.ContainsBytesInSlice(nonFilteredProofsLiteral, p.GetCompactName()) && !stringutils.ContainsBytesMatchInSlice(nonFilteredProofsMatch, p.GetCompactName()) { - docProof.FieldProofs[i].SortedHashes = docProof.FieldProofs[i].SortedHashes[:len(docProof.FieldProofs[i].SortedHashes)-1] - } - } - return docProof -} - func (s *service) prepareMintRequest(ctx context.Context, tokenID TokenID, cid identity.DID, req MintNFTRequest) (mreq MintRequest, err error) { docProofs, err := s.docSrv.CreateProofs(ctx, req.DocumentID, req.ProofFields) if err != nil { @@ -107,7 +94,15 @@ func (s *service) prepareMintRequest(ctx context.Context, tokenID TokenID, cid i } docProofs.FieldProofs = append(docProofs.FieldProofs, pfs...) - docProofs = s.filterMintProofs(docProofs) + + signaturesRoot, err := model.CalculateSignaturesRoot() + if err != nil { + return mreq, err + } + signingRoot, err := model.CalculateSigningRoot() + if err != nil { + return mreq, err + } anchorID, err := anchors.ToAnchorID(model.CurrentVersion()) if err != nil { @@ -119,41 +114,35 @@ func (s *service) prepareMintRequest(ctx context.Context, tokenID TokenID, cid i return mreq, err } - requestData, err := NewMintRequest(tokenID, req.DepositAddress, anchorID, nextAnchorID, docProofs.FieldProofs) + docRoot, err := model.CalculateDocumentRoot() if err != nil { return mreq, err } - return requestData, nil - -} + optProofs := docProofs.FieldProofs + if req.UseGeneric { + optProofs, err = proofs.OptimizeProofs(docProofs.FieldProofs, docRoot, sha256.New()) + if err != nil { + return mreq, err + } + } -// GetRequiredInvoiceUnpaidProofFields returns required proof fields for an unpaid invoice mint -func (s *service) GetRequiredInvoiceUnpaidProofFields(ctx context.Context) ([]string, error) { - var proofFields []string + // useful to log proof data to be passed to mint method + log.Debugf("\nDocumentRoot %x\nSignaturesRoot %x\nSigningRoot %x\nDocumentID %x\nCurrentVersion %x\n", + docRoot, signaturesRoot, signingRoot, model.ID(), model.CurrentVersion()) + log.Debug(json.MarshalIndent(documents.ConvertProofs(optProofs), "", " ")) - acc, err := contextutil.Account(ctx) - if err != nil { - return nil, err - } - accDIDBytes, err := acc.GetIdentityID() + requestData, err := NewMintRequest(tokenID, req.DepositAddress, anchorID, nextAnchorID, signingRoot, signaturesRoot, optProofs) if err != nil { - return nil, err - } - keys, err := acc.GetKeys() - if err != nil { - return nil, err + return mreq, err } - signingRoot := fmt.Sprintf("%s.%s", documents.DRTreePrefix, documents.SigningRootField) - signerID := hexutil.Encode(append(accDIDBytes, keys[identity.KeyPurposeSigning.Name].PublicKey...)) - signatureSender := fmt.Sprintf("%s.signatures[%s].signature", documents.SignaturesTreePrefix, signerID) - proofFields = []string{"invoice.gross_amount", "invoice.currency", "invoice.date_due", "invoice.sender", "invoice.status", signingRoot, signatureSender, documents.CDTreePrefix + ".next_version"} - return proofFields, nil + return requestData, nil + } // MintNFT mints an NFT -func (s *service) MintNFT(ctx context.Context, req MintNFTRequest) (*TokenResponse, chan bool, error) { +func (s *service) MintNFT(ctx context.Context, req MintNFTRequest) (*TokenResponse, chan error, error) { tc, err := contextutil.Account(ctx) if err != nil { return nil, nil, err @@ -180,10 +169,7 @@ func (s *service) MintNFT(ctx context.Context, req MintNFTRequest) (*TokenRespon return nil, nil, errors.NewTypedError(ErrNFTMinted, errors.New("registry %v", req.RegistryAddress.String())) } - didBytes, err := tc.GetIdentityID() - if err != nil { - return nil, nil, err - } + didBytes := tc.GetIdentityID() // Mint NFT within transaction // We use context.Background() for now so that the transaction is only limited by ethereum timeouts @@ -192,7 +178,7 @@ func (s *service) MintNFT(ctx context.Context, req MintNFTRequest) (*TokenRespon return nil, nil, err } - jobID, done, err := s.jobsManager.ExecuteWithinJob(context.Background(), did, jobs.NilJobID(), "Minting NFT", + jobID, done, err := s.jobsManager.ExecuteWithinJob(contextutil.Copy(ctx), did, jobs.NilJobID(), "Minting NFT", s.minterJob(ctx, tokenID, model, req)) if err != nil { @@ -206,23 +192,19 @@ func (s *service) MintNFT(ctx context.Context, req MintNFTRequest) (*TokenRespon } // TransferFrom transfers an NFT to another address -func (s *service) TransferFrom(ctx context.Context, registry common.Address, to common.Address, tokenID TokenID) (*TokenResponse, chan bool, error) { +func (s *service) TransferFrom(ctx context.Context, registry common.Address, to common.Address, tokenID TokenID) (*TokenResponse, chan error, error) { tc, err := contextutil.Account(ctx) if err != nil { return nil, nil, err } - didBytes, err := tc.GetIdentityID() - if err != nil { - return nil, nil, err - } - + didBytes := tc.GetIdentityID() did, err := identity.NewDIDFromBytes(didBytes) if err != nil { return nil, nil, err } - jobID, done, err := s.jobsManager.ExecuteWithinJob(context.Background(), did, jobs.NilJobID(), "Transfer From NFT", + jobID, done, err := s.jobsManager.ExecuteWithinJob(contextutil.Copy(ctx), did, jobs.NilJobID(), "Transfer From NFT", s.transferFromJob(ctx, registry, did.ToAddress(), to, tokenID)) if err != nil { return nil, nil, err @@ -249,10 +231,10 @@ func (s *service) minterJob(ctx context.Context, tokenID TokenID, model document return } - isDone := <-done - if !isDone { + err = <-done + if err != nil { // some problem occurred in a child task - errOut <- errors.New("update document failed for document %s and job %s", hexutil.Encode(req.DocumentID), jobID) + errOut <- errors.New("update document failed for document %s and job %s with error %s", hexutil.Encode(req.DocumentID), jobID, err.Error()) return } @@ -262,14 +244,25 @@ func (s *service) minterJob(ctx context.Context, tokenID TokenID, model document return } - // to common.Address, tokenId *big.Int, tokenURI string, anchorId *big.Int, properties [][]byte, values [][]byte, salts [][32]byte, proofs [][][32]byte - txID, done, err := s.identityService.Execute(ctx, req.RegistryAddress, InvoiceUnpaidContractABI, "mint", requestData.To, requestData.TokenID, requestData.AnchorID, requestData.Props, requestData.Values, requestData.Salts, requestData.Proofs) + // to common.Address, tokenId *big.Int, tokenURI string, anchorId *big.Int, properties [][]byte, values [][32]byte, salts [][32]byte, proofs [][][32]byte + args := []interface{}{requestData.To, requestData.TokenID, requestData.AnchorID, requestData.Props, requestData.Values, requestData.Salts, requestData.Proofs} + mintContractABI := InvoiceUnpaidContractABI + if req.UseGeneric { + // TODO Remove once we have finalized the generic NFT work + filteredProofs := requestData.Proofs[:len(requestData.Proofs)-1] + // to common.Address, tokenId *big.Int, tokenURI string, anchorId *big.Int, signingRoot [32]byte, signaturesRoot [32]byte, properties [][]byte, values [][]byte, salts [][32]byte, proofs [][][32]byte + args = []interface{}{requestData.To, requestData.TokenID, requestData.AnchorID, requestData.DataRoot, requestData.SignaturesRoot, requestData.Props, requestData.Values, requestData.Salts, filteredProofs} + mintContractABI = GenericMintMethodABI + } + + txID, done, err := s.identityService.Execute(ctx, req.RegistryAddress, mintContractABI, "mint", args...) if err != nil { errOut <- err return } - log.Infof("Sent off ethTX to mint [tokenID: %s, anchor: %x, nextAnchor: %s, registry: %s] to invoice unpaid contract.", - requestData.TokenID, requestData.AnchorID, hexutil.Encode(requestData.NextAnchorID.Bytes()), requestData.To.String()) + + log.Infof("Sent off ethTX to mint [tokenID: %s, anchor: %s, nextAnchor: %s, registry: %s] to invoice unpaid contract.", + hexutil.Encode(requestData.TokenID.Bytes()), hexutil.Encode(requestData.AnchorID.Bytes()), hexutil.Encode(requestData.NextAnchorID.Bytes()), requestData.To.String()) log.Debugf("To: %s", requestData.To.String()) log.Debugf("TokenID: %s", hexutil.Encode(requestData.TokenID.Bytes())) @@ -280,10 +273,10 @@ func (s *service) minterJob(ctx context.Context, tokenID TokenID, model document log.Debugf("Salts: %s", byte32SlicetoString(requestData.Salts)) log.Debugf("Proofs: %s", byteByte32SlicetoString(requestData.Proofs)) - isDone = <-done - if !isDone { + err = <-done + if err != nil { // some problem occurred in a child task - errOut <- errors.New("mint nft failed for document %s and transaction %s", hexutil.Encode(req.DocumentID), txID) + errOut <- errors.New("mint nft failed for document %s and transaction %s with error %s", hexutil.Encode(req.DocumentID), txID, err.Error()) return } @@ -325,10 +318,10 @@ func (s *service) transferFromJob(ctx context.Context, registry common.Address, log.Infof("sent off ethTX to transferFrom [registry: %s tokenID: %s, from: %s, to: %s].", registry.String(), tokenID.String(), from.String(), to.String()) - isDone := <-done - if !isDone { + err = <-done + if err != nil { // some problem occurred in a child task - errOut <- errors.New("failed to transfer token with transaction: %s", txID) + errOut <- errors.New("failed to transfer token with transaction: %s with error %s", txID, err.Error()) return } @@ -391,6 +384,12 @@ type MintRequest struct { // NextAnchorID is the next ID of the document, when updated NextAnchorID *big.Int + // DataRoot of the document + DataRoot [32]byte + + // SignaturesRoot of the document + SignaturesRoot [32]byte + // Props contains the compact props for readRole and tokenRole Props [][]byte @@ -405,21 +404,31 @@ type MintRequest struct { } // NewMintRequest converts the parameters and returns a struct with needed parameter for minting -func NewMintRequest(tokenID TokenID, to common.Address, anchorID anchors.AnchorID, nextAnchorID anchors.AnchorID, proofs []*proofspb.Proof) (MintRequest, error) { +func NewMintRequest(tokenID TokenID, to common.Address, anchorID anchors.AnchorID, nextAnchorID anchors.AnchorID, dataRoot, signaturesRoot []byte, proofs []*proofspb.Proof) (MintRequest, error) { proofData, err := convertToProofData(proofs) if err != nil { return MintRequest{}, err } + dr, err := utils.SliceToByte32(dataRoot) + if err != nil { + return MintRequest{}, err + } + sr, err := utils.SliceToByte32(signaturesRoot) + if err != nil { + return MintRequest{}, err + } return MintRequest{ - To: to, - TokenID: tokenID.BigInt(), - AnchorID: anchorID.BigInt(), - NextAnchorID: nextAnchorID.BigInt(), - Props: proofData.Props, - Values: proofData.Values, - Salts: proofData.Salts, - Proofs: proofData.Proofs}, nil + To: to, + TokenID: tokenID.BigInt(), + AnchorID: anchorID.BigInt(), + NextAnchorID: nextAnchorID.BigInt(), + DataRoot: dr, + SignaturesRoot: sr, + Props: proofData.Props, + Values: proofData.Values, + Salts: proofData.Salts, + Proofs: proofData.Proofs}, nil } type proofData struct { diff --git a/nft/service_integration_test.go b/nft/service_integration_test.go index 400465122..c1368a3cc 100644 --- a/nft/service_integration_test.go +++ b/nft/service_integration_test.go @@ -4,6 +4,7 @@ package nft_test import ( "context" + "encoding/json" "fmt" "os" "testing" @@ -16,15 +17,14 @@ import ( "github.com/centrifuge/go-centrifuge/config/configstore" "github.com/centrifuge/go-centrifuge/contextutil" "github.com/centrifuge/go-centrifuge/documents" + "github.com/centrifuge/go-centrifuge/documents/generic" "github.com/centrifuge/go-centrifuge/documents/invoice" "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/go-centrifuge/identity" "github.com/centrifuge/go-centrifuge/jobs" "github.com/centrifuge/go-centrifuge/nft" - invoicepb "github.com/centrifuge/go-centrifuge/protobufs/gen/go/invoice" "github.com/centrifuge/go-centrifuge/testingutils" "github.com/centrifuge/go-centrifuge/testingutils/identity" - "github.com/centrifuge/go-centrifuge/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/log" @@ -36,7 +36,7 @@ var cfg config.Configuration var cfgService config.Service var idService identity.Service var idFactory identity.Factory -var invoiceUnpaid nft.InvoiceUnpaid +var invoiceUnpaid nft.Service var jobManager jobs.Manager var tokenRegistry documents.TokenRegistry @@ -48,7 +48,7 @@ func TestMain(m *testing.M) { idFactory = ctx[identity.BootstrappedDIDFactory].(identity.Factory) cfg = ctx[bootstrap.BootstrappedConfig].(config.Configuration) cfgService = ctx[config.BootstrappedConfigStorage].(config.Service) - invoiceUnpaid = ctx[bootstrap.BootstrappedInvoiceUnpaid].(nft.InvoiceUnpaid) + invoiceUnpaid = ctx[bootstrap.BootstrappedInvoiceUnpaid].(nft.Service) jobManager = ctx[jobs.BootstrappedService].(jobs.Manager) tokenRegistry = ctx[bootstrap.BootstrappedInvoiceUnpaid].(documents.TokenRegistry) result := m.Run() @@ -56,7 +56,7 @@ func TestMain(m *testing.M) { os.Exit(result) } -func prepareForNFTMinting(t *testing.T) (context.Context, []byte, common.Address, documents.Service, identity.DID) { +func prepareInvoiceForNFTMinting(t *testing.T) (context.Context, []byte, common.Address, documents.Service, identity.DID) { // create identity log.Debug("Create Identity for Testing") didAddr, err := idFactory.CalculateIdentityAddress(context.Background()) @@ -72,30 +72,20 @@ func prepareForNFTMinting(t *testing.T) (context.Context, []byte, common.Address assert.NoError(t, err) // create invoice (anchor) - service, err := registry.LocateService(documenttypes.InvoiceDataTypeUrl) - assert.Nil(t, err, "should not error out when getting invoice service") + invSrv, err := registry.LocateService(documenttypes.InvoiceDataTypeUrl) + assert.Nil(t, err, "should not error out when getting invoice invSrv") ctx, err := contextutil.New(context.Background(), tcr) assert.NoError(t, err) - invSrv := service.(invoice.Service) dueDate := time.Now().Add(4 * 24 * time.Hour) - tm, err := utils.ToTimestamp(dueDate) - assert.NoError(t, err) - model, err := invSrv.DeriveFromCreatePayload(ctx, &invoicepb.InvoiceCreatePayload{ - Data: &invoicepb.InvoiceData{ - Sender: did.String(), - Number: "2132131", - Status: "unpaid", - GrossAmount: "123", - NetAmount: "123", - Currency: "EUR", - DateDue: tm, - }, - }) + assert.NoError(t, err) + + payload := invoicePayload(t, nil, invoiceData(t, &did, &dueDate)) + model := invoice.InitInvoice(t, did, payload) assert.NoError(t, err, "should not error out when creating invoice model") modelUpdated, txID, done, err := invSrv.Create(ctx, model) assert.NoError(t, err) - d := <-done - assert.True(t, d) + err = <-done + assert.NoError(t, err) assert.NoError(t, jobManager.WaitForJob(cid, txID)) // get ID @@ -107,13 +97,47 @@ func prepareForNFTMinting(t *testing.T) (context.Context, []byte, common.Address return ctx, id, registry, invSrv, cid } +func prepareGenericForNFTMinting(t *testing.T, regAddr string, attrs map[documents.AttrKey]documents.Attribute) (context.Context, []byte, common.Address, documents.Service, identity.DID) { + // create identity + log.Debug("Create Identity for Testing") + didAddr, err := idFactory.CalculateIdentityAddress(context.Background()) + assert.NoError(t, err) + did := identity.NewDID(*didAddr) + tc, err := configstore.TempAccount("main", cfg) + assert.NoError(t, err) + tcr := tc.(*configstore.Account) + tcr.IdentityID = did[:] + _, err = cfgService.CreateAccount(tcr) + assert.NoError(t, err) + cid, err := testingidentity.CreateAccountIDWithKeys(cfg.GetEthereumContextWaitTimeout(), tcr, idService, idFactory) + assert.NoError(t, err) + + // create Generic doc (anchor) + genericSrv, err := registry.LocateService(documenttypes.GenericDataTypeUrl) + assert.Nil(t, err, "should not error out when getting generic genSrv") + ctx, err := contextutil.New(context.Background(), tcr) + assert.NoError(t, err) + + payload := genericPayload(t, nil, attrs) + modelUpdated, txID, err := genericSrv.CreateModel(ctx, payload) + assert.NoError(t, err) + assert.NoError(t, jobManager.WaitForJob(cid, txID)) + + // get ID + id := modelUpdated.ID() + registry := common.HexToAddress(regAddr) + + return ctx, id, registry, genericSrv, cid +} + func mintNFT(t *testing.T, ctx context.Context, req nft.MintNFTRequest, cid identity.DID, registry common.Address) nft.TokenID { resp, done, err := invoiceUnpaid.MintNFT(ctx, req) assert.NoError(t, err, "should not error out when minting an invoice") assert.NotNil(t, resp.TokenID, "token id should be present") tokenID, err := nft.TokenIDFromString(resp.TokenID) assert.NoError(t, err, "should not error out when getting tokenID hex") - <-done + err = <-done + assert.NoError(t, err) jobID, err := jobs.FromString(resp.JobID) assert.NoError(t, err) assert.NoError(t, jobManager.WaitForJob(cid, jobID)) @@ -124,13 +148,12 @@ func mintNFT(t *testing.T, ctx context.Context, req nft.MintNFTRequest, cid iden } func TestInvoiceUnpaidService_mint_grant_read_access(t *testing.T) { - ctx, id, registry, invSrv, cid := prepareForNFTMinting(t) + ctx, id, registry, invSrv, cid := prepareInvoiceForNFTMinting(t) regAddr := registry.String() log.Info(regAddr) acc, err := contextutil.Account(ctx) assert.NoError(t, err) - accDIDBytes, err := acc.GetIdentityID() - assert.NoError(t, err) + accDIDBytes := acc.GetIdentityID() keys, err := acc.GetKeys() assert.NoError(t, err) signerId := hexutil.Encode(append(accDIDBytes, keys[identity.KeyPurposeSigning.Name].PublicKey...)) @@ -163,8 +186,53 @@ func TestInvoiceUnpaidService_mint_grant_read_access(t *testing.T) { assert.True(t, errors.IsOfType(nft.ErrNFTMinted, err)) } +func TestGenericMintNFT(t *testing.T) { + attrs := map[documents.AttrKey]documents.Attribute{} + loanAmount := "loanAmount" + loanAmountValue := "100.10001" + attr0, err := documents.NewStringAttribute(loanAmount, documents.AttrDecimal, loanAmountValue) + assert.NoError(t, err) + attrs[attr0.Key] = attr0 + asIsValue := "dateValue" + asIsValueValue := time.Now().UTC().Format(time.RFC3339Nano) + attr1, err := documents.NewStringAttribute(asIsValue, documents.AttrTimestamp, asIsValueValue) + assert.NoError(t, err) + attrs[attr1.Key] = attr1 + afterRehabValue := "afterRehabValue" + afterRehabValueValue := "2000" + attr2, err := documents.NewStringAttribute(afterRehabValue, documents.AttrDecimal, afterRehabValueValue) + assert.NoError(t, err) + attrs[attr2.Key] = attr2 + scAddrs := testingutils.GetDAppSmartContractAddresses() + fmt.Println(scAddrs) + ctx, id, registry, invSrv, cid := prepareGenericForNFTMinting(t, scAddrs["genericNFT"], attrs) + fmt.Println("Generic NFT Registry", scAddrs["genericNFT"]) + + attributeLoanAmount := fmt.Sprintf("%s.attributes[%s].byte_val", documents.CDTreePrefix, attr0.Key.String()) + attributeAsIsVal := fmt.Sprintf("%s.attributes[%s].byte_val", documents.CDTreePrefix, attr1.Key.String()) + attributeAfterRehabVal := fmt.Sprintf("%s.attributes[%s].byte_val", documents.CDTreePrefix, attr2.Key.String()) + proofFields := []string{attributeLoanAmount, attributeAsIsVal, attributeAfterRehabVal} + + req := nft.MintNFTRequest{ + DocumentID: id, + RegistryAddress: registry, + DepositAddress: cid.ToAddress(), + ProofFields: proofFields, + GrantNFTReadAccess: false, + SubmitNFTReadAccessProof: false, + SubmitTokenProof: true, + UseGeneric: true, + } + _ = mintNFT(t, ctx, req, cid, registry) + doc, err := invSrv.GetCurrentVersion(ctx, id) + assert.NoError(t, err) + cd, err := doc.PackCoreDocument() + assert.NoError(t, err) + assert.Len(t, cd.Roles, 2) +} + func failMintNFT(t *testing.T, grantNFT, nftReadAccess bool) { - ctx, id, registry, _, cid := prepareForNFTMinting(t) + ctx, id, registry, _, cid := prepareInvoiceForNFTMinting(t) req := nft.MintNFTRequest{ DocumentID: id, RegistryAddress: registry, @@ -186,11 +254,10 @@ func TestEthereumInvoiceUnpaid_MintNFT_no_grant_access(t *testing.T) { } func mintNFTWithProofs(t *testing.T, grantAccess, tokenProof, readAccessProof bool) (context.Context, nft.TokenID, identity.DID) { - ctx, id, registry, invSrv, cid := prepareForNFTMinting(t) + ctx, id, registry, invSrv, cid := prepareInvoiceForNFTMinting(t) acc, err := contextutil.Account(ctx) assert.NoError(t, err) - accDIDBytes, err := acc.GetIdentityID() - assert.NoError(t, err) + accDIDBytes := acc.GetIdentityID() keys, err := acc.GetKeys() assert.NoError(t, err) signerId := hexutil.Encode(append(accDIDBytes, keys[identity.KeyPurposeSigning.Name].PublicKey...)) @@ -246,7 +313,8 @@ func TestTransferNFT(t *testing.T) { // successful resp, done, err := invoiceUnpaid.TransferFrom(ctx, registry, to, tokenID) assert.NoError(t, err) - <-done + err = <-done + assert.NoError(t, err) jobID, err := jobs.FromString(resp.JobID) assert.NoError(t, err) assert.NoError(t, jobManager.WaitForJob(did, jobID)) @@ -259,7 +327,9 @@ func TestTransferNFT(t *testing.T) { secondTo := common.HexToAddress("0xFBb1b73C4f0BDa4f67dcA266ce6Ef42f520fBB98") resp, done, err = invoiceUnpaid.TransferFrom(ctx, registry, secondTo, tokenID) assert.NoError(t, err) - <-done + err = <-done + assert.Error(t, err) + assert.Contains(t, err.Error(), "from address is not the owner of tokenID") jobID, err = jobs.FromString(resp.JobID) assert.NoError(t, err) @@ -267,3 +337,41 @@ func TestTransferNFT(t *testing.T) { assert.Error(t, err) assert.Contains(t, err.Error(), "from address is not the owner of tokenID") } + +func invoicePayload(t *testing.T, collaborators []identity.DID, data []byte) documents.CreatePayload { + return documents.CreatePayload{ + Scheme: invoice.Scheme, + Collaborators: documents.CollaboratorsAccess{ + ReadWriteCollaborators: collaborators, + }, + Data: data, + } +} + +func genericPayload(t *testing.T, collaborators []identity.DID, attrs map[documents.AttrKey]documents.Attribute) documents.CreatePayload { + return documents.CreatePayload{ + Scheme: generic.Scheme, + Collaborators: documents.CollaboratorsAccess{ + ReadWriteCollaborators: collaborators, + }, + Attributes: attrs, + } +} + +func invoiceData(t *testing.T, did *identity.DID, tm *time.Time) []byte { + dec, err := documents.NewDecimal("123") + assert.NoError(t, err) + data := invoice.Data{ + Sender: did, + Number: "2132131", + Status: "unpaid", + GrossAmount: dec, + NetAmount: dec, + Currency: "EUR", + DateDue: tm, + } + + d, err := json.Marshal(data) + assert.NoError(t, err) + return d +} diff --git a/nft/service_test.go b/nft/service_test.go index 27cd65415..3cecec300 100644 --- a/nft/service_test.go +++ b/nft/service_test.go @@ -3,21 +3,16 @@ package nft import ( - "context" - "fmt" "math/big" "testing" "time" "github.com/centrifuge/centrifuge-protobufs/gen/go/coredocument" "github.com/centrifuge/go-centrifuge/config" - "github.com/centrifuge/go-centrifuge/config/configstore" - "github.com/centrifuge/go-centrifuge/contextutil" "github.com/centrifuge/go-centrifuge/documents" "github.com/centrifuge/go-centrifuge/documents/invoice" "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/go-centrifuge/ethereum" - "github.com/centrifuge/go-centrifuge/identity" "github.com/centrifuge/go-centrifuge/jobs" "github.com/centrifuge/go-centrifuge/testingutils" "github.com/centrifuge/go-centrifuge/testingutils/commons" @@ -176,7 +171,7 @@ func TestInvoiceUnpaid(t *testing.T) { configMock.On("GetEthereumDefaultAccountName").Return("ethacc") cid := testingidentity.GenerateRandomDID() configMock.On("GetIdentityID").Return(cid[:], nil) - configMock.On("GetEthereumAccount").Return(&config.AccountConfig{}, nil) + configMock.On("GetEthereumAccount", "main").Return(&config.AccountConfig{}, nil) configMock.On("GetEthereumContextWaitTimeout").Return(time.Second) configMock.On("GetReceiveEventNotificationEndpoint").Return("") configMock.On("GetP2PKeyPair").Return("", "") @@ -186,7 +181,7 @@ func TestInvoiceUnpaid(t *testing.T) { queueSrv := new(testingutils.MockQueue) jobMan := new(testingjobs.MockJobManager) jobMan.On("ExecuteWithinJob", mock.Anything, mock.Anything, mock.Anything, mock.Anything, - mock.Anything, mock.Anything).Return(jobs.NilJobID(), make(chan bool), nil) + mock.Anything, mock.Anything).Return(jobs.NilJobID(), make(chan error), nil) return docServiceMock, invoiceUnpaidMock, idServiceMock, ethClientMock, configMock, queueSrv, jobMan }, MintNFTRequest{DocumentID: decodeHex("0x1212"), ProofFields: []string{"collaborators[0]"}, DepositAddress: common.HexToAddress("0xf72855759a39fb75fc7341139f5d7a3974d4da08")}, @@ -224,98 +219,12 @@ func TestInvoiceUnpaid(t *testing.T) { } } -func TestEthereumInvoiceUnpaid_GetRequiredInvoiceUnpaidProofFields(t *testing.T) { - service := newService(nil, nil, nil, nil, nil, nil, nil, nil) - - //missing account in context - ctxh := context.Background() - proofList, err := service.GetRequiredInvoiceUnpaidProofFields(ctxh) - assert.Error(t, err) - assert.Nil(t, proofList) - - //error identity keys - tc, err := configstore.NewAccount("main", cfg) - assert.Nil(t, err) - acc := tc.(*configstore.Account) - acc.EthereumAccount = &config.AccountConfig{ - Key: "blabla", - } - ctxh, err = contextutil.New(ctxh, acc) - assert.Nil(t, err) - proofList, err = service.GetRequiredInvoiceUnpaidProofFields(ctxh) - assert.Error(t, err) - assert.Nil(t, proofList) - - //success assertions - tc, err = configstore.NewAccount("main", cfg) - assert.Nil(t, err) - ctxh, err = contextutil.New(ctxh, tc) - assert.Nil(t, err) - proofList, err = service.GetRequiredInvoiceUnpaidProofFields(ctxh) - assert.NoError(t, err) - assert.Len(t, proofList, 8) - accDIDBytes, err := tc.GetIdentityID() - assert.NoError(t, err) - keys, err := tc.GetKeys() - assert.NoError(t, err) - signerID := hexutil.Encode(append(accDIDBytes, keys[identity.KeyPurposeSigning.Name].PublicKey...)) - signatureSender := fmt.Sprintf("%s.signatures[%s].signature", documents.SignaturesTreePrefix, signerID) - assert.Equal(t, signatureSender, proofList[6]) -} - -func TestFilterMintProofs(t *testing.T) { - service := newService(nil, nil, nil, nil, nil, nil, nil, nil) - indexKey := utils.RandomSlice(52) - docProof := &documents.DocumentProof{ - FieldProofs: []*proofspb.Proof{ - { - Property: proofs.CompactName([]byte{10, 100, 5, 20, 69, 1, 0, 1}...), - Value: utils.RandomSlice(32), - Salt: utils.RandomSlice(32), - Hash: utils.RandomSlice(32), - SortedHashes: [][]byte{ - utils.RandomSlice(32), - utils.RandomSlice(32), - utils.RandomSlice(32), - }, - }, - { - Property: proofs.CompactName(append(documents.CompactProperties(documents.DRTreePrefix), documents.CompactProperties(documents.SigningRootField)...)...), - Value: utils.RandomSlice(32), - Salt: utils.RandomSlice(32), - Hash: utils.RandomSlice(32), - SortedHashes: [][]byte{ - utils.RandomSlice(32), - utils.RandomSlice(32), - utils.RandomSlice(32), - }, - }, - { - Property: proofs.CompactName(append([]byte{3, 0, 0, 0, 0, 0, 0, 1}, append(indexKey, []byte{0, 0, 0, 4}...)...)...), - Value: utils.RandomSlice(32), - Salt: utils.RandomSlice(32), - Hash: utils.RandomSlice(32), - SortedHashes: [][]byte{ - utils.RandomSlice(32), - utils.RandomSlice(32), - utils.RandomSlice(32), - }, - }, - }, - } - - docProofAux := service.filterMintProofs(docProof) - assert.Len(t, docProofAux.FieldProofs[0].SortedHashes, 2) - assert.Len(t, docProofAux.FieldProofs[1].SortedHashes, 3) - assert.Len(t, docProofAux.FieldProofs[2].SortedHashes, 3) -} - func TestTokenTransfer(t *testing.T) { configMock := &testingconfig.MockConfig{} configMock.On("GetEthereumDefaultAccountName").Return("ethacc") cid := testingidentity.GenerateRandomDID() configMock.On("GetIdentityID").Return(cid[:], nil) - configMock.On("GetEthereumAccount").Return(&config.AccountConfig{}, nil) + configMock.On("GetEthereumAccount", "main").Return(&config.AccountConfig{}, nil) configMock.On("GetEthereumContextWaitTimeout").Return(time.Second) configMock.On("GetReceiveEventNotificationEndpoint").Return("") configMock.On("GetP2PKeyPair").Return("", "") @@ -326,7 +235,7 @@ func TestTokenTransfer(t *testing.T) { jobID := jobs.NewJobID() jobMan := new(testingjobs.MockJobManager) jobMan.On("ExecuteWithinJob", mock.Anything, mock.Anything, mock.Anything, mock.Anything, - mock.Anything, mock.Anything).Return(jobID, make(chan bool), nil) + mock.Anything, mock.Anything).Return(jobID, make(chan error), nil) idServiceMock := &testingcommons.MockIdentityService{} diff --git a/notification/notification.go b/notification/notification.go index 0e49062ff..5417ad9f1 100644 --- a/notification/notification.go +++ b/notification/notification.go @@ -3,10 +3,9 @@ package notification import ( "context" "encoding/json" + "time" "github.com/centrifuge/go-centrifuge/contextutil" - - "github.com/centrifuge/centrifuge-protobufs/gen/go/notification" "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/go-centrifuge/utils" logging "github.com/ipfs/go-log" @@ -28,9 +27,22 @@ const ( Success Status = 1 ) +// Message is the payload used to send the notifications. +type Message struct { + EventType EventType `json:"event_type"` + Recorded time.Time `json:"recorded" swaggertype:"primitive,string"` + DocumentType string `json:"document_type"` + Status string `json:"status"` + Message string `json:"message"` + DocumentID string `json:"document_id"` + AccountID string `json:"account_id"` // account_id is the account associated to webhook + FromID string `json:"from_id"` // from_id if provided, original trigger of the event + ToID string `json:"to_id"` // to_id if provided, final destination of the event +} + // Sender defines methods that can handle a notification. type Sender interface { - Send(ctx context.Context, notification *notificationpb.NotificationMessage) (Status, error) + Send(ctx context.Context, notification Message) (Status, error) } // NewWebhookSender returns an implementation of a Sender that sends notifications through webhooks. @@ -40,11 +52,10 @@ func NewWebhookSender() Sender { // NewWebhookSender implements Sender. // Sends notification through a webhook defined. -type webhookSender struct { -} +type webhookSender struct{} // Send sends notification to the defined webhook. -func (wh webhookSender) Send(ctx context.Context, notification *notificationpb.NotificationMessage) (Status, error) { +func (wh webhookSender) Send(ctx context.Context, notification Message) (Status, error) { tc, err := contextutil.Account(ctx) if err != nil { return Failure, err diff --git a/notification/notification_test.go b/notification/notification_test.go index c1503adee..d559ad538 100644 --- a/notification/notification_test.go +++ b/notification/notification_test.go @@ -16,14 +16,11 @@ import ( "github.com/centrifuge/go-centrifuge/testingutils/config" "github.com/centrifuge/centrifuge-protobufs/documenttypes" - "github.com/centrifuge/centrifuge-protobufs/gen/go/notification" "github.com/centrifuge/go-centrifuge/bootstrap" "github.com/centrifuge/go-centrifuge/bootstrap/bootstrappers/testlogging" "github.com/centrifuge/go-centrifuge/config" "github.com/centrifuge/go-centrifuge/utils" "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/golang/protobuf/ptypes" - "github.com/golang/protobuf/ptypes/timestamp" "github.com/stretchr/testify/assert" ) @@ -42,36 +39,26 @@ func TestMain(m *testing.M) { os.Exit(result) } -type mockConfig struct { - url string -} - -func (m mockConfig) GetReceiveEventNotificationEndpoint() string { - return m.url -} - func TestWebhookSender_Send(t *testing.T) { docID := utils.RandomSlice(32) accountID := utils.RandomSlice(identity.DIDLength) senderID := utils.RandomSlice(identity.DIDLength) statusMsg := "failure" message := "some random error" - ts, err := ptypes.TimestampProto(time.Now().UTC()) - assert.Nil(t, err, "Should not error out") var wg sync.WaitGroup wg.Add(1) mux := http.NewServeMux() mux.HandleFunc("/webhook", func(writer http.ResponseWriter, request *http.Request) { var resp struct { - EventType uint32 `json:"event_type,omitempty"` - AccountId string `json:"account_id,omitempty"` - FromId string `json:"from_id,omitempty"` - ToId string `json:"to_id,omitempty"` - Recorded *timestamp.Timestamp `json:"recorded,omitempty"` - DocumentType string `json:"document_type,omitempty"` - DocumentId string `json:"document_id,omitempty"` - Status string `json:"status,omitempty"` - Message string `json:"message,omitempty"` + EventType uint32 `json:"event_type,omitempty"` + AccountId string `json:"account_id,omitempty"` + FromId string `json:"from_id,omitempty"` + ToId string `json:"to_id,omitempty"` + Recorded time.Time `json:"recorded,omitempty"` + DocumentType string `json:"document_type,omitempty"` + DocumentId string `json:"document_id,omitempty"` + Status string `json:"status,omitempty"` + Message string `json:"message,omitempty"` } defer request.Body.Close() data, err := ioutil.ReadAll(request.Body) @@ -93,14 +80,14 @@ func TestWebhookSender_Send(t *testing.T) { defer server.Close() wb := NewWebhookSender() - notif := ¬ificationpb.NotificationMessage{ - DocumentId: hexutil.Encode(docID), + notif := Message{ + DocumentID: hexutil.Encode(docID), DocumentType: documenttypes.InvoiceDataTypeUrl, - AccountId: hexutil.Encode(accountID), - FromId: hexutil.Encode(senderID), - ToId: hexutil.Encode(accountID), - EventType: uint32(ReceivedPayload), - Recorded: ts, + AccountID: hexutil.Encode(accountID), + FromID: hexutil.Encode(senderID), + ToID: hexutil.Encode(accountID), + EventType: ReceivedPayload, + Recorded: time.Now().UTC(), Status: statusMsg, Message: message, } diff --git a/p2p/bootstrapper_test.go b/p2p/bootstrapper_test.go index fa685c64c..70ac639dd 100644 --- a/p2p/bootstrapper_test.go +++ b/p2p/bootstrapper_test.go @@ -33,7 +33,7 @@ func TestBootstrapper_Bootstrap(t *testing.T) { cs.On("GetConfig").Return(&configstore.NodeConfig{}, nil) ids := new(testingcommons.MockIdentityService) m[identity.BootstrappedDIDService] = ids - m[documents.BootstrappedDocumentService] = documents.DefaultService(cfg, nil, nil, documents.NewServiceRegistry(), ids) + m[documents.BootstrappedDocumentService] = documents.DefaultService(cfg, nil, nil, documents.NewServiceRegistry(), ids, nil, nil) m[bootstrap.BootstrappedInvoiceUnpaid] = new(testingdocuments.MockRegistry) err = b.Bootstrap(m) diff --git a/p2p/client.go b/p2p/client.go index 6343430ad..a5df6f021 100644 --- a/p2p/client.go +++ b/p2p/client.go @@ -6,8 +6,6 @@ import ( "github.com/centrifuge/centrifuge-protobufs/gen/go/coredocument" "github.com/centrifuge/centrifuge-protobufs/gen/go/p2p" - "github.com/centrifuge/go-centrifuge/centerrors" - "github.com/centrifuge/go-centrifuge/code" "github.com/centrifuge/go-centrifuge/contextutil" "github.com/centrifuge/go-centrifuge/documents" "github.com/centrifuge/go-centrifuge/errors" @@ -357,22 +355,22 @@ func (s *peer) validateSignatureResp( err := identity.ValidateDIDBytes(resp.Signature.SignerId, receiver) if err != nil { - return centerrors.New(code.AuthenticationFailed, fmt.Sprintf("signature invalid with err: %s", err.Error())) + return errors.New("signature invalid with err: %s", err.Error()) } tm, err := model.Timestamp() if err != nil { - return centerrors.New(code.AuthenticationFailed, fmt.Sprintf("cannot get model timestamp : %s", err.Error())) + return errors.New("cannot get model timestamp : %s", err.Error()) } signingRoot, err := model.CalculateSigningRoot() if err != nil { - return centerrors.New(code.AuthenticationFailed, fmt.Sprintf("failed to calculate signing root: %s", err.Error())) + return errors.New("failed to calculate signing root: %s", err.Error()) } err = s.idService.ValidateSignature(receiver, resp.Signature.PublicKey, resp.Signature.Signature, signingRoot, tm) if err != nil { - return centerrors.New(code.AuthenticationFailed, fmt.Sprintf("signature invalid with err: %s", err.Error())) + return errors.New("signature invalid with err: %s", err.Error()) } return nil diff --git a/p2p/client_integration_test.go b/p2p/client_integration_test.go index 8c2be5c9b..959db2f13 100644 --- a/p2p/client_integration_test.go +++ b/p2p/client_integration_test.go @@ -17,14 +17,12 @@ import ( "github.com/centrifuge/go-centrifuge/contextutil" "github.com/centrifuge/go-centrifuge/crypto" "github.com/centrifuge/go-centrifuge/documents" - "github.com/centrifuge/go-centrifuge/documents/purchaseorder" + "github.com/centrifuge/go-centrifuge/documents/invoice" "github.com/centrifuge/go-centrifuge/identity" "github.com/centrifuge/go-centrifuge/testingutils/config" - "github.com/centrifuge/go-centrifuge/testingutils/documents" "github.com/centrifuge/go-centrifuge/testingutils/identity" "github.com/centrifuge/go-centrifuge/utils" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" "github.com/stretchr/testify/assert" ) @@ -133,21 +131,21 @@ func prepareDocumentForP2PHandler(t *testing.T, collaborators [][]byte) document acc.IdentityID = defaultDID[:] accKeys, err := acc.GetKeys() assert.NoError(t, err) - payalod := testingdocuments.CreatePOPayload() - var cs []string - for _, c := range collaborators { - cs = append(cs, hexutil.Encode(c)) - } - payalod.WriteAccess = cs - po := new(purchaseorder.PurchaseOrder) - err = po.InitPurchaseOrderInput(payalod, defaultDID) + payload := invoice.CreateInvoicePayload(t, nil) + dids, err := identity.BytesToDIDs(collaborators...) assert.NoError(t, err) - po.SetUsedAnchorRepoAddress(cfg.GetContractAddress(config.AnchorRepo)) - err = po.AddUpdateLog(defaultDID) + var cs []identity.DID + for _, did := range dids { + cs = append(cs, *did) + } + payload.Collaborators.ReadWriteCollaborators = cs + inv := invoice.InitInvoice(t, defaultDID, payload) + inv.SetUsedAnchorRepoAddress(cfg.GetContractAddress(config.AnchorRepo)) + err = inv.AddUpdateLog(defaultDID) assert.NoError(t, err) - _, err = po.CalculateDataRoot() + _, err = inv.CalculateDataRoot() assert.NoError(t, err) - sr, err := po.CalculateSigningRoot() + sr, err := inv.CalculateSigningRoot() assert.NoError(t, err) s, err := crypto.SignMessage(accKeys[identity.KeyPurposeSigning.Name].PrivateKey, sr, crypto.CurveSecp256K1) assert.NoError(t, err) @@ -157,8 +155,8 @@ func prepareDocumentForP2PHandler(t *testing.T, collaborators [][]byte) document PublicKey: accKeys[identity.KeyPurposeSigning.Name].PublicKey, Signature: s, } - po.AppendSignatures(sig) - _, err = po.CalculateDocumentRoot() + inv.AppendSignatures(sig) + _, err = inv.CalculateDocumentRoot() assert.NoError(t, err) - return po + return inv } diff --git a/p2p/client_test.go b/p2p/client_test.go index 15916f92c..38fed39c3 100644 --- a/p2p/client_test.go +++ b/p2p/client_test.go @@ -8,16 +8,13 @@ import ( "github.com/centrifuge/centrifuge-protobufs/gen/go/coredocument" "github.com/centrifuge/centrifuge-protobufs/gen/go/p2p" - "github.com/centrifuge/go-centrifuge/contextutil" - "github.com/centrifuge/go-centrifuge/documents" - "github.com/centrifuge/go-centrifuge/documents/purchaseorder" + "github.com/centrifuge/centrifuge-protobufs/gen/go/protocol" + "github.com/centrifuge/go-centrifuge/documents/invoice" "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/go-centrifuge/identity" "github.com/centrifuge/go-centrifuge/p2p/common" - "github.com/centrifuge/go-centrifuge/protobufs/gen/go/protocol" "github.com/centrifuge/go-centrifuge/testingutils/commons" "github.com/centrifuge/go-centrifuge/testingutils/config" - "github.com/centrifuge/go-centrifuge/testingutils/documents" "github.com/centrifuge/go-centrifuge/testingutils/identity" "github.com/centrifuge/go-centrifuge/utils" "github.com/centrifuge/go-centrifuge/version" @@ -52,7 +49,7 @@ func TestGetSignatureForDocument_fail_connect(t *testing.T) { idService := getIDMocks(ctx, did) m := &MockMessenger{} testClient := &peer{config: cfg, idService: idService, mes: m, disablePeerStore: true} - cd, model := createCDWithEmbeddedPO(t, ctx, did, nil) + model, cd := invoice.CreateInvoiceWithEmbedCD(t, ctx, did, nil) _, err = p2pcommon.PrepareP2PEnvelope(ctx, c.GetNetworkID(), p2pcommon.MessageTypeRequestSignature, &p2ppb.SignatureRequest{Document: &cd}) assert.NoError(t, err, "signature request could not be created") @@ -71,7 +68,7 @@ func TestGetSignatureForDocument_fail_version_check(t *testing.T) { idService := getIDMocks(ctx, did) m := &MockMessenger{} testClient := &peer{config: cfg, idService: idService, mes: m, disablePeerStore: true} - cd, model := createCDWithEmbeddedPO(t, ctx, did, nil) + model, cd := invoice.CreateInvoiceWithEmbedCD(t, ctx, did, nil) _, err = p2pcommon.PrepareP2PEnvelope(ctx, c.GetNetworkID(), p2pcommon.MessageTypeRequestSignature, &p2ppb.SignatureRequest{Document: &cd}) assert.NoError(t, err, "signature request could not be created") @@ -91,7 +88,7 @@ func TestGetSignatureForDocument_fail_did(t *testing.T) { idService := getIDMocks(ctx, did) m := &MockMessenger{} testClient := &peer{config: cfg, idService: idService, mes: m, disablePeerStore: true} - cd, model := createCDWithEmbeddedPO(t, ctx, did, nil) + model, cd := invoice.CreateInvoiceWithEmbedCD(t, ctx, did, nil) _, err = p2pcommon.PrepareP2PEnvelope(ctx, c.GetNetworkID(), p2pcommon.MessageTypeRequestSignature, &p2ppb.SignatureRequest{Document: &cd}) assert.NoError(t, err, "signature request could not be created") @@ -104,7 +101,7 @@ func TestGetSignatureForDocument_fail_did(t *testing.T) { m.AssertExpectations(t) assert.Nil(t, resp, "must be nil") assert.Error(t, err, "must not be nil") - assert.Contains(t, err.Error(), "[5]signature invalid with err: provided bytes doesn't match centID") + assert.Contains(t, err.Error(), "signature invalid with err: provided bytes doesn't match centID") } @@ -136,36 +133,3 @@ func (s *peer) createSignatureResp(centNodeVer string, signature *coredocumentpb return &protocolpb.P2PEnvelope{Body: reqB} } - -func createCDWithEmbeddedPO(t *testing.T, ctx context.Context, did identity.DID, collaborators []identity.DID) (coredocumentpb.CoreDocument, documents.Model) { - po := new(purchaseorder.PurchaseOrder) - data := testingdocuments.CreatePOPayload() - if len(collaborators) > 0 { - var cs []string - for _, c := range collaborators { - cs = append(cs, c.String()) - } - - data.WriteAccess = cs - } - err := po.InitPurchaseOrderInput(data, did) - assert.NoError(t, err) - _, err = po.CalculateDataRoot() - assert.NoError(t, err) - sr, err := po.CalculateSigningRoot() - assert.NoError(t, err) - - acc, err := contextutil.Account(ctx) - assert.NoError(t, err) - - sig, err := acc.SignMsg(sr) - assert.NoError(t, err) - - po.AppendSignatures(sig) - _, err = po.CalculateDocumentRoot() - assert.NoError(t, err) - cd, err := po.PackCoreDocument() - assert.NoError(t, err) - - return cd, po -} diff --git a/p2p/common/protocol.go b/p2p/common/protocol.go index 02b41e1b9..0e46d4fbc 100644 --- a/p2p/common/protocol.go +++ b/p2p/common/protocol.go @@ -8,10 +8,10 @@ import ( errorspb "github.com/centrifuge/centrifuge-protobufs/gen/go/errors" "github.com/centrifuge/centrifuge-protobufs/gen/go/p2p" + "github.com/centrifuge/centrifuge-protobufs/gen/go/protocol" "github.com/centrifuge/go-centrifuge/contextutil" "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/go-centrifuge/identity" - "github.com/centrifuge/go-centrifuge/protobufs/gen/go/protocol" "github.com/centrifuge/go-centrifuge/utils" "github.com/centrifuge/go-centrifuge/version" "github.com/golang/protobuf/proto" @@ -113,11 +113,7 @@ func PrepareP2PEnvelope(ctx context.Context, networkID uint32, messageType Messa return nil, err } - centIDBytes, err := self.GetIdentityID() - if err != nil { - return nil, err - } - + centIDBytes := self.GetIdentityID() tm, err := utils.ToTimestamp(time.Now().UTC()) if err != nil { return nil, err diff --git a/p2p/common/protocol_test.go b/p2p/common/protocol_test.go index d56fdeebd..b23341216 100644 --- a/p2p/common/protocol_test.go +++ b/p2p/common/protocol_test.go @@ -8,17 +8,15 @@ import ( "testing" "github.com/centrifuge/centrifuge-protobufs/gen/go/p2p" + "github.com/centrifuge/centrifuge-protobufs/gen/go/protocol" "github.com/centrifuge/go-centrifuge/bootstrap" "github.com/centrifuge/go-centrifuge/bootstrap/bootstrappers/testlogging" "github.com/centrifuge/go-centrifuge/config" "github.com/centrifuge/go-centrifuge/config/configstore" "github.com/centrifuge/go-centrifuge/contextutil" - "github.com/centrifuge/go-centrifuge/protobufs/gen/go/protocol" + "github.com/centrifuge/go-centrifuge/identity" "github.com/centrifuge/go-centrifuge/utils" "github.com/golang/protobuf/proto" - - "github.com/centrifuge/go-centrifuge/identity" - "github.com/libp2p/go-libp2p-protocol" "github.com/stretchr/testify/assert" ) @@ -94,12 +92,12 @@ func TestPrepareP2PEnvelope(t *testing.T) { acc := &configstore.Account{ IdentityID: id, P2PKeyPair: configstore.KeyPair{ - Priv: ssk, - Pub: spk, + Pvt: ssk, + Pub: spk, }, SigningKeyPair: configstore.KeyPair{ - Priv: ssk, - Pub: spk, + Pvt: ssk, + Pub: spk, }, } ctx, _ := contextutil.New(context.Background(), acc) diff --git a/p2p/common/protocol_testworld.go b/p2p/common/protocol_testworld.go index 02fa9f62c..6203e23f3 100644 --- a/p2p/common/protocol_testworld.go +++ b/p2p/common/protocol_testworld.go @@ -8,8 +8,8 @@ import ( "time" "github.com/centrifuge/centrifuge-protobufs/gen/go/p2p" + "github.com/centrifuge/centrifuge-protobufs/gen/go/protocol" "github.com/centrifuge/go-centrifuge/contextutil" - "github.com/centrifuge/go-centrifuge/protobufs/gen/go/protocol" "github.com/centrifuge/go-centrifuge/utils" "github.com/centrifuge/go-centrifuge/version" "github.com/golang/protobuf/proto" @@ -24,11 +24,7 @@ func PrepareP2PEnvelopeIncorrectNodeVersion(ctx context.Context, networkID uint3 return nil, err } - centIDBytes, err := self.GetIdentityID() - if err != nil { - return nil, err - } - + centIDBytes := self.GetIdentityID() tm, err := utils.ToTimestamp(time.Now().UTC()) if err != nil { return nil, err @@ -74,11 +70,7 @@ func PrepareP2PEnvelopeInvalidBody(ctx context.Context, networkID uint32, messag return nil, err } - centIDBytes, err := self.GetIdentityID() - if err != nil { - return nil, err - } - + centIDBytes := self.GetIdentityID() tm, err := utils.ToTimestamp(time.Now().UTC()) if err != nil { return nil, err diff --git a/p2p/messenger/messenger.go b/p2p/messenger/messenger.go index de0576f2d..4db09dd4a 100644 --- a/p2p/messenger/messenger.go +++ b/p2p/messenger/messenger.go @@ -7,8 +7,8 @@ import ( "sync" "time" + pb "github.com/centrifuge/centrifuge-protobufs/gen/go/protocol" "github.com/centrifuge/go-centrifuge/errors" - pb "github.com/centrifuge/go-centrifuge/protobufs/gen/go/protocol" ggio "github.com/gogo/protobuf/io" "github.com/golang/protobuf/proto" logging "github.com/ipfs/go-log" diff --git a/p2p/messenger/messenger_test.go b/p2p/messenger/messenger_test.go index 627928676..05b488dda 100644 --- a/p2p/messenger/messenger_test.go +++ b/p2p/messenger/messenger_test.go @@ -12,6 +12,7 @@ import ( "time" "github.com/centrifuge/centrifuge-protobufs/gen/go/p2p" + pb "github.com/centrifuge/centrifuge-protobufs/gen/go/protocol" "github.com/centrifuge/go-centrifuge/anchors" "github.com/centrifuge/go-centrifuge/bootstrap" "github.com/centrifuge/go-centrifuge/bootstrap/bootstrappers/testlogging" @@ -23,7 +24,6 @@ import ( "github.com/centrifuge/go-centrifuge/identity/ideth" "github.com/centrifuge/go-centrifuge/jobs/jobsv1" "github.com/centrifuge/go-centrifuge/p2p/common" - pb "github.com/centrifuge/go-centrifuge/protobufs/gen/go/protocol" "github.com/centrifuge/go-centrifuge/queue" "github.com/centrifuge/go-centrifuge/storage/leveldb" "github.com/centrifuge/go-centrifuge/testingutils/config" @@ -335,6 +335,6 @@ func runDHT(ctx context.Context, h host.Host, bootstrapPeers []string) error { func updateKeys(c config.Configuration) config.Configuration { n := c.(*configstore.NodeConfig) n.MainIdentity.SigningKeyPair.Pub = "../../build/resources/signingKey.pub.pem" - n.MainIdentity.SigningKeyPair.Priv = "../../build/resources/signingKey.key.pem" + n.MainIdentity.SigningKeyPair.Pvt = "../../build/resources/signingKey.key.pem" return c } diff --git a/p2p/receiver/handler.go b/p2p/receiver/handler.go index 0f1b6ee8b..7ad484a84 100644 --- a/p2p/receiver/handler.go +++ b/p2p/receiver/handler.go @@ -6,22 +6,23 @@ import ( errorspb "github.com/centrifuge/centrifuge-protobufs/gen/go/errors" "github.com/centrifuge/centrifuge-protobufs/gen/go/p2p" - "github.com/centrifuge/go-centrifuge/centerrors" - "github.com/centrifuge/go-centrifuge/code" + pb "github.com/centrifuge/centrifuge-protobufs/gen/go/protocol" "github.com/centrifuge/go-centrifuge/config" "github.com/centrifuge/go-centrifuge/contextutil" "github.com/centrifuge/go-centrifuge/documents" "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/go-centrifuge/identity" "github.com/centrifuge/go-centrifuge/p2p/common" - pb "github.com/centrifuge/go-centrifuge/protobufs/gen/go/protocol" "github.com/centrifuge/go-centrifuge/utils/timeutils" "github.com/ethereum/go-ethereum/common" "github.com/golang/protobuf/proto" + logging "github.com/ipfs/go-log" "github.com/libp2p/go-libp2p-peer" "github.com/libp2p/go-libp2p-protocol" ) +var log = logging.Logger("p2p-handler") + // Handler implements protocol message handlers type Handler struct { config config.Service @@ -145,7 +146,7 @@ func (srv *Handler) RequestDocumentSignature(ctx context.Context, sigReq *p2ppb. signature, err := srv.docSrv.RequestDocumentSignature(ctx, model, collaborator) if err != nil { - return nil, centerrors.New(code.Unknown, err.Error()) + return nil, err } return &p2ppb.SignatureResponse{Signature: signature}, nil @@ -194,7 +195,7 @@ func (srv *Handler) SendAnchoredDocument(ctx context.Context, docReq *p2ppb.Anch err = srv.docSrv.ReceiveAnchoredDocument(ctx, model, collaborator) if err != nil { - return nil, centerrors.New(code.Unknown, err.Error()) + return nil, err } return &p2ppb.AnchorDocumentResponse{Accepted: true}, nil @@ -285,6 +286,9 @@ func (srv *Handler) validateDocumentAccess(ctx context.Context, docReq *p2ppb.Ge } func (srv *Handler) convertToErrorEnvelop(ierr error) (*pb.P2PEnvelope, error) { + // Log on server side + log.Error(ierr) + ierr = errors.Mask(ierr) errPb := &errorspb.Error{Message: ierr.Error()} errBytes, errx := proto.Marshal(errPb) diff --git a/p2p/receiver/handler_integration_test.go b/p2p/receiver/handler_integration_test.go index bbd06a505..19b5e288d 100644 --- a/p2p/receiver/handler_integration_test.go +++ b/p2p/receiver/handler_integration_test.go @@ -9,10 +9,12 @@ import ( "os" "testing" + "github.com/centrifuge/go-centrifuge/documents/invoice" "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/centrifuge-protobufs/gen/go/coredocument" "github.com/centrifuge/centrifuge-protobufs/gen/go/p2p" + "github.com/centrifuge/centrifuge-protobufs/gen/go/protocol" "github.com/centrifuge/go-centrifuge/anchors" "github.com/centrifuge/go-centrifuge/bootstrap" "github.com/centrifuge/go-centrifuge/bootstrap/bootstrappers/testingbootstrap" @@ -23,11 +25,9 @@ import ( cented25519 "github.com/centrifuge/go-centrifuge/crypto/ed25519" "github.com/centrifuge/go-centrifuge/crypto/secp256k1" "github.com/centrifuge/go-centrifuge/documents" - "github.com/centrifuge/go-centrifuge/documents/purchaseorder" "github.com/centrifuge/go-centrifuge/identity" "github.com/centrifuge/go-centrifuge/p2p/common" "github.com/centrifuge/go-centrifuge/p2p/receiver" - "github.com/centrifuge/go-centrifuge/protobufs/gen/go/protocol" "github.com/centrifuge/go-centrifuge/storage" "github.com/centrifuge/go-centrifuge/testingutils/config" "github.com/centrifuge/go-centrifuge/testingutils/documents" @@ -169,7 +169,7 @@ func TestHandler_SendAnchoredDocument_update_fail(t *testing.T) { assert.Nil(t, err) watchCommittedAnchor := <-anchorConfirmations - assert.True(t, watchCommittedAnchor, "No error should be thrown by context") + assert.NoError(t, watchCommittedAnchor, "No error should be thrown by context") anchorResp, err := handler.SendAnchoredDocument(ctx, &p2ppb.AnchorDocumentRequest{Document: &cd}, accDID) assert.Error(t, err) @@ -217,7 +217,7 @@ func TestHandler_SendAnchoredDocument(t *testing.T) { assert.Nil(t, err) watchCommittedAnchor := <-anchorConfirmations - assert.True(t, watchCommittedAnchor, "No error should be thrown by context") + assert.NoError(t, watchCommittedAnchor, "No error should be thrown by context") cd, err = po.PackCoreDocument() assert.NoError(t, err) @@ -246,7 +246,7 @@ func TestHandler_SendAnchoredDocument(t *testing.T) { assert.Nil(t, err) watchCommittedAnchor = <-anchorConfirmations - assert.True(t, watchCommittedAnchor, "No error should be thrown by context") + assert.NoError(t, watchCommittedAnchor, "No error should be thrown by context") ncd, err = npo.PackCoreDocument() assert.NoError(t, err) @@ -295,7 +295,7 @@ func createIdentity(t *testing.T) identity.DID { return *did } -func prepareDocumentForP2PHandler(t *testing.T, po *purchaseorder.PurchaseOrder) (*purchaseorder.PurchaseOrder, coredocumentpb.CoreDocument) { +func prepareDocumentForP2PHandler(t *testing.T, inv *invoice.Invoice) (*invoice.Invoice, coredocumentpb.CoreDocument) { ctx := testingconfig.CreateAccountContext(t, cfg) accCfg, err := contextutil.Account(ctx) assert.NoError(t, err) @@ -303,18 +303,15 @@ func prepareDocumentForP2PHandler(t *testing.T, po *purchaseorder.PurchaseOrder) acc.IdentityID = defaultDID[:] accKeys, err := acc.GetKeys() assert.NoError(t, err) - if po == nil { - payload := testingdocuments.CreatePOPayload() - po = new(purchaseorder.PurchaseOrder) - err = po.InitPurchaseOrderInput(payload, defaultDID) - assert.NoError(t, err) + if inv == nil { + inv = invoice.InitInvoice(t, defaultDID, invoice.CreateInvoicePayload(t, nil)) } - po.SetUsedAnchorRepoAddress(cfg.GetContractAddress(config.AnchorRepo)) - err = po.AddUpdateLog(defaultDID) + inv.SetUsedAnchorRepoAddress(cfg.GetContractAddress(config.AnchorRepo)) + err = inv.AddUpdateLog(defaultDID) assert.NoError(t, err) - _, err = po.CalculateDataRoot() + _, err = inv.CalculateDataRoot() assert.NoError(t, err) - sr, err := po.CalculateSigningRoot() + sr, err := inv.CalculateSigningRoot() assert.NoError(t, err) s, err := crypto.SignMessage(accKeys[identity.KeyPurposeSigning.Name].PrivateKey, sr, crypto.CurveSecp256K1) assert.NoError(t, err) @@ -324,19 +321,19 @@ func prepareDocumentForP2PHandler(t *testing.T, po *purchaseorder.PurchaseOrder) PublicKey: accKeys[identity.KeyPurposeSigning.Name].PublicKey, Signature: s, } - po.AppendSignatures(sig) - _, err = po.CalculateDocumentRoot() + inv.AppendSignatures(sig) + _, err = inv.CalculateDocumentRoot() assert.NoError(t, err) - cd, err := po.PackCoreDocument() + cd, err := inv.PackCoreDocument() assert.NoError(t, err) - return po, cd + return inv, cd } -func updateDocumentForP2Phandler(t *testing.T, po *purchaseorder.PurchaseOrder) (*purchaseorder.PurchaseOrder, coredocumentpb.CoreDocument) { - cd, err := po.CoreDocument.PrepareNewVersion(nil, documents.CollaboratorsAccess{}, nil) +func updateDocumentForP2Phandler(t *testing.T, inv *invoice.Invoice) (*invoice.Invoice, coredocumentpb.CoreDocument) { + cd, err := inv.CoreDocument.PrepareNewVersion(nil, documents.CollaboratorsAccess{}, nil) assert.NoError(t, err) - po.CoreDocument = cd - return prepareDocumentForP2PHandler(t, po) + inv.CoreDocument = cd + return prepareDocumentForP2PHandler(t, inv) } func resolveSignatureResponse(t *testing.T, p2pEnv *protocolpb.P2PEnvelope) *p2ppb.SignatureResponse { diff --git a/p2p/receiver/handler_test.go b/p2p/receiver/handler_test.go index 8e73ed6f0..17b3cc41b 100644 --- a/p2p/receiver/handler_test.go +++ b/p2p/receiver/handler_test.go @@ -11,6 +11,7 @@ import ( errorspb "github.com/centrifuge/centrifuge-protobufs/gen/go/errors" "github.com/centrifuge/centrifuge-protobufs/gen/go/p2p" + "github.com/centrifuge/centrifuge-protobufs/gen/go/protocol" "github.com/centrifuge/go-centrifuge/anchors" "github.com/centrifuge/go-centrifuge/bootstrap" "github.com/centrifuge/go-centrifuge/bootstrap/bootstrappers/testlogging" @@ -22,7 +23,6 @@ import ( "github.com/centrifuge/go-centrifuge/identity" "github.com/centrifuge/go-centrifuge/jobs/jobsv1" "github.com/centrifuge/go-centrifuge/p2p/common" - "github.com/centrifuge/go-centrifuge/protobufs/gen/go/protocol" "github.com/centrifuge/go-centrifuge/queue" "github.com/centrifuge/go-centrifuge/storage/leveldb" "github.com/centrifuge/go-centrifuge/testingutils/commons" @@ -71,7 +71,7 @@ func TestMain(m *testing.M) { cfg = ctx[bootstrap.BootstrappedConfig].(config.Configuration) cfgService := ctx[config.BootstrappedConfigStorage].(config.Service) registry = ctx[documents.BootstrappedRegistry].(*documents.ServiceRegistry) - docSrv := documents.DefaultService(cfg, nil, nil, registry, mockIDService) + docSrv := documents.DefaultService(cfg, nil, nil, registry, mockIDService, nil, nil) _, pub, _ := crypto.GenerateEd25519Key(rand.Reader) defaultPID, _ = libp2pPeer.IDFromPublicKey(pub) mockIDService.On("ValidateKey", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) diff --git a/p2p/receiver/validator.go b/p2p/receiver/validator.go index fa298f623..da0df917d 100644 --- a/p2p/receiver/validator.go +++ b/p2p/receiver/validator.go @@ -2,11 +2,8 @@ package receiver import ( "context" - "fmt" "github.com/centrifuge/centrifuge-protobufs/gen/go/p2p" - "github.com/centrifuge/go-centrifuge/centerrors" - "github.com/centrifuge/go-centrifuge/code" "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/go-centrifuge/identity" "github.com/centrifuge/go-centrifuge/version" @@ -103,5 +100,5 @@ func HandshakeValidator(networkID uint32, idService identity.Service) ValidatorG } func incompatibleNetworkError(configNetwork uint32, nodeNetwork uint32) error { - return centerrors.New(code.NetworkMismatch, fmt.Sprintf("Incompatible network id: node network: %d, client network: %d", configNetwork, nodeNetwork)) + return errors.New("Incompatible network id: node network: %d, client network: %d", configNetwork, nodeNetwork) } diff --git a/p2p/server.go b/p2p/server.go index f1bc02850..df29f5df7 100644 --- a/p2p/server.go +++ b/p2p/server.go @@ -6,6 +6,7 @@ import ( "sync" "time" + pb "github.com/centrifuge/centrifuge-protobufs/gen/go/protocol" "github.com/centrifuge/go-centrifuge/config" crypto2 "github.com/centrifuge/go-centrifuge/crypto" "github.com/centrifuge/go-centrifuge/errors" @@ -13,7 +14,6 @@ import ( "github.com/centrifuge/go-centrifuge/p2p/common" ms "github.com/centrifuge/go-centrifuge/p2p/messenger" "github.com/centrifuge/go-centrifuge/p2p/receiver" - pb "github.com/centrifuge/go-centrifuge/protobufs/gen/go/protocol" ds "github.com/ipfs/go-datastore" "github.com/ipfs/go-ipfs-addr" logging "github.com/ipfs/go-log" @@ -109,16 +109,13 @@ func (s *peer) Start(ctx context.Context, wg *sync.WaitGroup, startupErr chan<- } func (s *peer) initProtocols() error { - tcs, err := s.config.GetAllAccounts() + tcs, err := s.config.GetAccounts() if err != nil { return err } var protocols []protocol.ID for _, t := range tcs { - accID, err := t.GetIdentityID() - if err != nil { - return err - } + accID := t.GetIdentityID() DID, err := identity.NewDIDFromBytes(accID) if err != nil { return err diff --git a/p2p/server_test.go b/p2p/server_test.go index 924925dce..3035e51bc 100644 --- a/p2p/server_test.go +++ b/p2p/server_test.go @@ -153,15 +153,15 @@ func TestCentP2PServer_makeBasicHostWithWrongExternalIP(t *testing.T) { func updateKeys(c config.Configuration) config.Configuration { n := c.(*configstore.NodeConfig) n.MainIdentity.P2PKeyPair.Pub = "../build/resources/p2pKey.pub.pem" - n.MainIdentity.P2PKeyPair.Priv = "../build/resources/p2pKey.key.pem" + n.MainIdentity.P2PKeyPair.Pvt = "../build/resources/p2pKey.key.pem" n.MainIdentity.SigningKeyPair.Pub = "../build/resources/signingKey.pub.pem" - n.MainIdentity.SigningKeyPair.Priv = "../build/resources/signingKey.key.pem" + n.MainIdentity.SigningKeyPair.Pvt = "../build/resources/signingKey.key.pem" return c } func mockmockConfigStore(n config.Configuration) *configstore.MockService { mockConfigStore := &configstore.MockService{} mockConfigStore.On("GetConfig").Return(n, nil) - mockConfigStore.On("GetAllAccounts").Return([]config.Account{&configstore.Account{IdentityID: utils.RandomSlice(identity.DIDLength)}}, nil) + mockConfigStore.On("GetAccounts").Return([]config.Account{&configstore.Account{IdentityID: utils.RandomSlice(identity.DIDLength)}}, nil) return mockConfigStore } diff --git a/p2p/server_testworld.go b/p2p/server_testworld.go index 151802f68..6eebc0b27 100644 --- a/p2p/server_testworld.go +++ b/p2p/server_testworld.go @@ -7,12 +7,12 @@ import ( "github.com/centrifuge/centrifuge-protobufs/gen/go/coredocument" "github.com/centrifuge/centrifuge-protobufs/gen/go/p2p" + "github.com/centrifuge/centrifuge-protobufs/gen/go/protocol" "github.com/centrifuge/go-centrifuge/contextutil" "github.com/centrifuge/go-centrifuge/documents" "github.com/centrifuge/go-centrifuge/errors" "github.com/centrifuge/go-centrifuge/identity" "github.com/centrifuge/go-centrifuge/p2p/common" - "github.com/centrifuge/go-centrifuge/protobufs/gen/go/protocol" "github.com/centrifuge/go-centrifuge/utils" "github.com/golang/protobuf/proto" ) diff --git a/pending/bootstrapper.go b/pending/bootstrapper.go new file mode 100644 index 000000000..9b654f962 --- /dev/null +++ b/pending/bootstrapper.go @@ -0,0 +1,31 @@ +package pending + +import ( + "github.com/centrifuge/go-centrifuge/documents" + "github.com/centrifuge/go-centrifuge/errors" + "github.com/centrifuge/go-centrifuge/storage" +) + +const ( + // BootstrappedPendingDocumentService is the key to bootstrapped document service + BootstrappedPendingDocumentService = "BootstrappedPendingDocumentService" +) + +// Bootstrapper implements bootstrap.Bootstrapper. +type Bootstrapper struct{} + +// Bootstrap sets the required storage and registers +func (Bootstrapper) Bootstrap(ctx map[string]interface{}) error { + docSrv, ok := ctx[documents.BootstrappedDocumentService].(documents.Service) + if !ok { + return errors.New("%s not found in the bootstrapper", documents.BootstrappedDocumentService) + } + + ldb, ok := ctx[storage.BootstrappedDB].(storage.Repository) + if !ok { + return errors.New("%s not found in the bootstrapper", storage.BootstrappedDB) + } + repo := NewRepository(ldb) + ctx[BootstrappedPendingDocumentService] = DefaultService(docSrv, repo) + return nil +} diff --git a/pending/bootstrapper_test.go b/pending/bootstrapper_test.go new file mode 100644 index 000000000..372ae54b8 --- /dev/null +++ b/pending/bootstrapper_test.go @@ -0,0 +1,33 @@ +// +build unit + +package pending + +import ( + "testing" + + "github.com/centrifuge/go-centrifuge/documents" + "github.com/centrifuge/go-centrifuge/storage" + "github.com/centrifuge/go-centrifuge/storage/leveldb" + testingdocuments "github.com/centrifuge/go-centrifuge/testingutils/documents" + "github.com/stretchr/testify/assert" +) + +func TestBootstrapper_Bootstrap(t *testing.T) { + ctx := make(map[string]interface{}) + randomPath := leveldb.GetRandomTestStoragePath() + db, err := leveldb.NewLevelDBStorage(randomPath) + assert.Nil(t, err) + repo := leveldb.NewLevelDBRepository(db) + + // missing doc srv + b := Bootstrapper{} + assert.Error(t, b.Bootstrap(ctx)) + + // missing repo + ctx[documents.BootstrappedDocumentService] = new(testingdocuments.MockService) + assert.Error(t, b.Bootstrap(ctx)) + + // success + ctx[storage.BootstrappedDB] = repo + assert.NoError(t, b.Bootstrap(ctx)) +} diff --git a/pending/repository.go b/pending/repository.go new file mode 100644 index 000000000..18da108f2 --- /dev/null +++ b/pending/repository.go @@ -0,0 +1,79 @@ +package pending + +import ( + "github.com/centrifuge/go-centrifuge/documents" + "github.com/centrifuge/go-centrifuge/errors" + "github.com/centrifuge/go-centrifuge/storage" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +const ( + // DocPrefix holds the generic prefix of a document in DB + DocPrefix string = "pending_document_" +) + +// Repository defines the required methods for a document repository. +// Can be implemented by any type that stores the documents. Ex: levelDB, sql etc... +type Repository interface { + // Get returns the Model associated with ID, owned by accountID + Get(accountID, id []byte) (documents.Model, error) + + // Create creates the model if not present in the DB. + // should error out if the document exists. + Create(accountID, id []byte, model documents.Model) error + + // Update strictly updates the model. + // Will error out when the model doesn't exist in the DB. + Update(accountID, id []byte, model documents.Model) error + + // Delete deletes the data associated with account and ID. + Delete(accountID, id []byte) error +} + +// NewRepository creates an instance of the pending document Repository +func NewRepository(db storage.Repository) Repository { + return &repo{db: db} +} + +type repo struct { + db storage.Repository +} + +// getKey returns document_+accountID+id +func (r *repo) getKey(accountID, id []byte) []byte { + hexKey := hexutil.Encode(append(accountID, id...)) + return append([]byte(DocPrefix), []byte(hexKey)...) +} + +// Get returns the Model associated with ID, owned by accountID +func (r *repo) Get(accountID, id []byte) (documents.Model, error) { + key := r.getKey(accountID, id) + model, err := r.db.Get(key) + if err != nil { + return nil, err + } + m, ok := model.(documents.Model) + if !ok { + return nil, errors.New("docID %s for account %s is not a model object", hexutil.Encode(id), hexutil.Encode(accountID)) + } + return m, nil +} + +// Create creates the model if not present in the DB. +// should error out if the document exists. +func (r *repo) Create(accountID, id []byte, model documents.Model) error { + key := r.getKey(accountID, id) + return r.db.Create(key, model) +} + +// Update strictly updates the model. +// Will error out when the model doesn't exist in the DB. +func (r *repo) Update(accountID, id []byte, model documents.Model) error { + key := r.getKey(accountID, id) + return r.db.Update(key, model) +} + +func (r *repo) Delete(accountID, id []byte) error { + key := r.getKey(accountID, id) + return r.db.Delete(key) +} diff --git a/pending/repository_test.go b/pending/repository_test.go new file mode 100644 index 000000000..1a5b0ad22 --- /dev/null +++ b/pending/repository_test.go @@ -0,0 +1,166 @@ +// +build unit + +package pending + +import ( + "encoding/json" + "os" + "reflect" + "testing" + "time" + + "github.com/centrifuge/go-centrifuge/anchors" + "github.com/centrifuge/go-centrifuge/bootstrap" + "github.com/centrifuge/go-centrifuge/bootstrap/bootstrappers/testlogging" + "github.com/centrifuge/go-centrifuge/config" + "github.com/centrifuge/go-centrifuge/config/configstore" + "github.com/centrifuge/go-centrifuge/documents" + "github.com/centrifuge/go-centrifuge/ethereum" + "github.com/centrifuge/go-centrifuge/identity" + "github.com/centrifuge/go-centrifuge/jobs/jobsv1" + "github.com/centrifuge/go-centrifuge/queue" + "github.com/centrifuge/go-centrifuge/storage" + "github.com/centrifuge/go-centrifuge/storage/leveldb" + testingcommons "github.com/centrifuge/go-centrifuge/testingutils/commons" + testingidentity "github.com/centrifuge/go-centrifuge/testingutils/identity" + "github.com/centrifuge/go-centrifuge/utils" + "github.com/stretchr/testify/assert" +) + +var ctx map[string]interface{} +var cfg config.Configuration +var did = testingidentity.GenerateRandomDID() + +func TestMain(m *testing.M) { + ctx = make(map[string]interface{}) + ethClient := ðereum.MockEthClient{} + ethClient.On("GetEthClient").Return(nil) + ctx[ethereum.BootstrappedEthereumClient] = ethClient + ibootstappers := []bootstrap.TestBootstrapper{ + &testlogging.TestLoggingBootstrapper{}, + &config.Bootstrapper{}, + &leveldb.Bootstrapper{}, + &configstore.Bootstrapper{}, + jobsv1.Bootstrapper{}, + &queue.Bootstrapper{}, + &anchors.Bootstrapper{}, + } + ctx[identity.BootstrappedDIDService] = &testingcommons.MockIdentityService{} + ctx[identity.BootstrappedDIDFactory] = &testingcommons.MockIdentityFactory{} + bootstrap.RunTestBootstrappers(ibootstappers, ctx) + cfg = ctx[bootstrap.BootstrappedConfig].(config.Configuration) + cfg.Set("identityId", did.String()) + cfg.Set("keys.p2p.publicKey", "../build/resources/p2pKey.pub.pem") + cfg.Set("keys.p2p.privateKey", "../build/resources/p2pKey.key.pem") + cfg.Set("keys.signing.publicKey", "../build/resources/signingKey.pub.pem") + cfg.Set("keys.signing.privateKey", "../build/resources/signingKey.key.pem") + result := m.Run() + bootstrap.RunTestTeardown(ibootstappers) + os.Exit(result) +} + +func getRepository(ctx map[string]interface{}) Repository { + db := ctx[storage.BootstrappedDB].(storage.Repository) + return NewRepository(db) +} + +type doc struct { + documents.Model + DocID, Current, Next []byte + SomeString string `json:"some_string"` + Time time.Time +} + +type unknownDoc struct { + SomeString string `json:"some_string"` +} + +func (unknownDoc) Type() reflect.Type { + return reflect.TypeOf(unknownDoc{}) +} + +func (u *unknownDoc) JSON() ([]byte, error) { + return json.Marshal(u) +} + +func (u *unknownDoc) FromJSON(j []byte) error { + return json.Unmarshal(j, u) +} + +func (m *doc) ID() []byte { + return m.DocID +} + +func (m *doc) CurrentVersion() []byte { + return m.Current +} + +func (m *doc) NextVersion() []byte { + return m.Next +} + +func (m *doc) JSON() ([]byte, error) { + return json.Marshal(m) +} + +func (m *doc) FromJSON(data []byte) error { + return json.Unmarshal(data, m) +} + +func (m *doc) Type() reflect.Type { + return reflect.TypeOf(m) +} + +func (m *doc) Timestamp() (time.Time, error) { + return m.Time, nil +} + +func TestLevelDBRepo_Get_Create_Update(t *testing.T) { + repor := getRepository(ctx) + + accountID, id := utils.RandomSlice(32), utils.RandomSlice(32) + m, err := repor.Get(accountID, id) + assert.Error(t, err, "must return error") + assert.Nil(t, m) + + d := &doc{SomeString: "Hello, Repo!", DocID: id} + err = repor.Create(accountID, id, d) + assert.Nil(t, err, "Create: unknown error") + + m, err = repor.Get(accountID, id) + assert.Error(t, err, "doc is not registered yet") + assert.Nil(t, m) + + repor.(*repo).db.Register(&doc{}) + m, err = repor.Get(accountID, id) + assert.Nil(t, err) + assert.NotNil(t, m) + nd := m.(*doc) + assert.Equal(t, d, nd, "must be equal") + + d.SomeString = "Hello, World!" + err = repor.Update(accountID, id, d) + assert.Nil(t, err, "Update: unknown error") + + m, err = repor.Get(accountID, id) + assert.Nil(t, err, "Get: unknown error") + nd = m.(*doc) + assert.Equal(t, d, nd, "must be equal") + + assert.NoError(t, repor.Delete(accountID, id)) + m, err = repor.Get(accountID, id) + assert.Error(t, err) + assert.Nil(t, m) + + // a document id sent which is not a model + repor.(*repo).db.Register(&unknownDoc{}) + unid := utils.RandomSlice(32) + u := unknownDoc{SomeString: "unknown"} + //hexKey := hexutil.Encode(append(accountID, unid...)) + err = repor.(*repo).db.Create(repor.(*repo).getKey(accountID, unid), &u) + assert.NoError(t, err) + m, err = repor.Get(accountID, unid) + if assert.Error(t, err) { + assert.Contains(t, err.Error(), "is not a model object") + } +} diff --git a/pending/service.go b/pending/service.go new file mode 100644 index 000000000..b533836ec --- /dev/null +++ b/pending/service.go @@ -0,0 +1,158 @@ +package pending + +import ( + "bytes" + "context" + + "github.com/centrifuge/go-centrifuge/contextutil" + "github.com/centrifuge/go-centrifuge/documents" + "github.com/centrifuge/go-centrifuge/errors" + "github.com/centrifuge/go-centrifuge/jobs" + logging "github.com/ipfs/go-log" +) + +var srvLog = logging.Logger("pending-service") + +// ErrPendingDocumentExists is a sentinel error used when document was created and tried to create a new one. +const ErrPendingDocumentExists = errors.Error("Pending document already created") + +// Service provides an interface for functions common to all document types +type Service interface { + // Get returns the document associated with docID and Status. + Get(ctx context.Context, docID []byte, status documents.Status) (documents.Model, error) + + // GetVersion returns the document associated with docID and versionID. + GetVersion(ctx context.Context, docID, versionID []byte) (documents.Model, error) + + // Update updates a pending document from the payload + Update(ctx context.Context, payload documents.UpdatePayload) (documents.Model, error) + + // Create creates a pending document from the payload + Create(ctx context.Context, payload documents.UpdatePayload) (documents.Model, error) + + // Commit validates, shares and anchors document + Commit(ctx context.Context, docID []byte) (documents.Model, jobs.JobID, error) +} + +// service implements Service +type service struct { + docSrv documents.Service + pendingRepo Repository +} + +// DefaultService returns the default implementation of the service +func DefaultService(docSrv documents.Service, repo Repository) Service { + return service{ + docSrv: docSrv, + pendingRepo: repo, + } +} + +// Get returns the document associated with docID +// If status is pending, we return the pending document from pending repo. +// else, we defer Get to document service. +func (s service) Get(ctx context.Context, docID []byte, status documents.Status) (documents.Model, error) { + if status != documents.Pending { + return s.docSrv.GetCurrentVersion(ctx, docID) + } + + did, err := contextutil.AccountDID(ctx) + if err != nil { + return nil, contextutil.ErrDIDMissingFromContext + } + + return s.pendingRepo.Get(did[:], docID) +} + +// GetVersion return the specific version of the document +// We try to fetch the version from the document service, if found return +// else look in pending repo for specific version. +func (s service) GetVersion(ctx context.Context, docID, versionID []byte) (documents.Model, error) { + doc, err := s.docSrv.GetVersion(ctx, docID, versionID) + if err == nil { + return doc, nil + } + + accID, err := contextutil.AccountDID(ctx) + if err != nil { + return nil, contextutil.ErrDIDMissingFromContext + } + + doc, err = s.pendingRepo.Get(accID[:], docID) + if err != nil || !bytes.Equal(versionID, doc.CurrentVersion()) { + return nil, documents.ErrDocumentNotFound + } + + return doc, nil +} + +// Create creates either a new document or next version of an anchored document and stores the document. +// errors out if there an pending document created already +func (s service) Create(ctx context.Context, payload documents.UpdatePayload) (documents.Model, error) { + accID, err := contextutil.AccountDID(ctx) + if err != nil { + return nil, contextutil.ErrDIDMissingFromContext + } + + if len(payload.DocumentID) > 0 { + _, err := s.pendingRepo.Get(accID[:], payload.DocumentID) + if err == nil { + // found an existing pending document. error out + return nil, ErrPendingDocumentExists + } + } + + doc, err := s.docSrv.Derive(ctx, payload) + if err != nil { + return nil, err + } + + // we create one document per ID. hence, we use ID instead of current version + // since its common to all document versions. + return doc, s.pendingRepo.Create(accID[:], doc.ID(), doc) +} + +// Update updates a pending document from the payload +func (s service) Update(ctx context.Context, payload documents.UpdatePayload) (documents.Model, error) { + accID, err := contextutil.AccountDID(ctx) + if err != nil { + return nil, contextutil.ErrDIDMissingFromContext + } + + m, err := s.pendingRepo.Get(accID[:], payload.DocumentID) + if err != nil { + return nil, err + } + + mp, ok := m.(documents.Patcher) + if !ok { + return nil, documents.ErrNotPatcher + } + + err = mp.Patch(payload) + if err != nil { + return nil, err + } + doc := mp.(documents.Model) + return doc, s.pendingRepo.Update(accID[:], doc.ID(), doc) +} + +// Commit triggers validations, state change and anchor job +func (s service) Commit(ctx context.Context, docID []byte) (documents.Model, jobs.JobID, error) { + accID, err := contextutil.AccountDID(ctx) + if err != nil { + return nil, jobs.NilJobID(), contextutil.ErrDIDMissingFromContext + } + + model, err := s.pendingRepo.Get(accID[:], docID) + if err != nil { + return nil, jobs.NilJobID(), documents.ErrDocumentNotFound + } + + jobID, err := s.docSrv.Commit(ctx, model) + if err != nil { + return nil, jobs.NilJobID(), err + } + + return model, jobID, s.pendingRepo.Delete(accID[:], docID) +} diff --git a/pending/service_test.go b/pending/service_test.go new file mode 100644 index 000000000..8b60f751f --- /dev/null +++ b/pending/service_test.go @@ -0,0 +1,229 @@ +// +build unit + +package pending + +import ( + "context" + "testing" + + "github.com/centrifuge/go-centrifuge/contextutil" + "github.com/centrifuge/go-centrifuge/documents" + "github.com/centrifuge/go-centrifuge/errors" + "github.com/centrifuge/go-centrifuge/jobs" + testingconfig "github.com/centrifuge/go-centrifuge/testingutils/config" + testingdocuments "github.com/centrifuge/go-centrifuge/testingutils/documents" + "github.com/centrifuge/go-centrifuge/utils" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +type mockRepo struct { + mock.Mock + Repository +} + +func (m *mockRepo) Get(accID, id []byte) (documents.Model, error) { + args := m.Called(accID, id) + doc, _ := args.Get(0).(documents.Model) + return doc, args.Error(1) +} + +func (m *mockRepo) Delete(accID, id []byte) error { + args := m.Called(accID, id) + return args.Error(0) +} + +func (m *mockRepo) Create(accID, id []byte, doc documents.Model) error { + args := m.Called(accID, id, doc) + return args.Error(0) +} + +func (m *mockRepo) Update(accID, id []byte, doc documents.Model) error { + args := m.Called(accID, id, doc) + return args.Error(0) +} + +func TestService_Commit(t *testing.T) { + s := service{} + + // missing did + ctx := context.Background() + docID := utils.RandomSlice(32) + _, _, err := s.Commit(ctx, docID) + assert.Error(t, err) + assert.True(t, errors.IsOfType(contextutil.ErrDIDMissingFromContext, err)) + + // missing model + ctx = testingconfig.CreateAccountContext(t, cfg) + repo := new(mockRepo) + repo.On("Get", did[:], docID).Return(nil, errors.New("not found")).Once() + s.pendingRepo = repo + _, _, err = s.Commit(ctx, docID) + assert.Error(t, err) + + // failed commit + doc := new(documents.MockModel) + repo.On("Get", did[:], docID).Return(doc, nil) + docSrv := new(testingdocuments.MockService) + docSrv.On("Commit", ctx, doc).Return(nil, errors.New("failed to commit")).Once() + s.docSrv = docSrv + _, _, err = s.Commit(ctx, docID) + assert.Error(t, err) + + // success + jobID := jobs.NewJobID() + repo.On("Delete", did[:], docID).Return(nil) + docSrv.On("Commit", ctx, doc).Return(jobID, nil) + m, jid, err := s.Commit(ctx, docID) + assert.NoError(t, err) + assert.Equal(t, jobID, jid) + assert.NotNil(t, m) + docSrv.AssertExpectations(t) + doc.AssertExpectations(t) +} + +func TestService_Create(t *testing.T) { + s := service{} + + // missing did + ctx := context.Background() + payload := documents.UpdatePayload{} + _, err := s.Create(ctx, payload) + assert.Error(t, err) + assert.True(t, errors.IsOfType(contextutil.ErrDIDMissingFromContext, err)) + + // derive failed + ctx = testingconfig.CreateAccountContext(t, cfg) + docSrv := new(testingdocuments.MockService) + docSrv.On("Derive", ctx, payload).Return(nil, errors.New("failed to derive")).Once() + s.docSrv = docSrv + _, err = s.Create(ctx, payload) + assert.Error(t, err) + + // already existing document + payload.DocumentID = utils.RandomSlice(32) + repo := new(mockRepo) + repo.On("Get", did[:], payload.DocumentID).Return(new(documents.MockModel), nil).Once() + s.pendingRepo = repo + _, err = s.Create(ctx, payload) + assert.Error(t, err) + assert.True(t, errors.IsOfType(ErrPendingDocumentExists, err)) + + // success + repo.On("Get", did[:], payload.DocumentID).Return(nil, errors.New("missing")).Once() + doc := new(documents.MockModel) + doc.On("ID").Return(payload.DocumentID).Once() + repo.On("Create", did[:], payload.DocumentID, doc).Return(nil).Once() + docSrv.On("Derive", ctx, payload).Return(doc, nil).Once() + gdoc, err := s.Create(ctx, payload) + assert.NoError(t, err) + assert.Equal(t, doc, gdoc) + doc.AssertExpectations(t) + docSrv.AssertExpectations(t) + repo.AssertExpectations(t) +} + +func TestService_Get(t *testing.T) { + // not pending document + st := documents.Committed + s := service{} + ctx := context.Background() + docID := utils.RandomSlice(32) + docSrv := new(testingdocuments.MockService) + docSrv.On("GetCurrentVersion", docID).Return(new(documents.MockModel), nil).Once() + s.docSrv = docSrv + doc, err := s.Get(ctx, docID, st) + assert.NoError(t, err) + assert.NotNil(t, doc) + + // pending doc + // missing did from context + st = documents.Pending + _, err = s.Get(ctx, docID, st) + assert.Error(t, err) + assert.True(t, errors.IsOfType(contextutil.ErrDIDMissingFromContext, err)) + + // success + repo := new(mockRepo) + repo.On("Get", did[:], docID).Return(doc, nil).Once() + s.pendingRepo = repo + ctx = testingconfig.CreateAccountContext(t, cfg) + gdoc, err := s.Get(ctx, docID, st) + assert.NoError(t, err) + assert.Equal(t, doc, gdoc) + docSrv.AssertExpectations(t) + repo.AssertExpectations(t) +} + +func TestService_GetVersion(t *testing.T) { + // found in docService + doc := new(documents.MockModel) + docID, versionID := utils.RandomSlice(32), utils.RandomSlice(32) + s := service{} + ctx := context.Background() + docSrv := new(testingdocuments.MockService) + docSrv.On("GetVersion", docID, versionID).Return(doc, nil).Once() + s.docSrv = docSrv + gdoc, err := s.GetVersion(ctx, docID, versionID) + assert.NoError(t, err) + assert.Equal(t, doc, gdoc) + + // not found in docService + // ctx with no did + docSrv.On("GetVersion", docID, versionID).Return(nil, documents.ErrDocumentNotFound) + _, err = s.GetVersion(ctx, docID, versionID) + assert.Error(t, err) + assert.True(t, errors.IsOfType(contextutil.ErrDIDMissingFromContext, err)) + + // different current version + ctx = testingconfig.CreateAccountContext(t, cfg) + repo := new(mockRepo) + repo.On("Get", did[:], docID).Return(doc, nil) + doc.On("CurrentVersion").Return(utils.RandomSlice(32)).Once() + s.pendingRepo = repo + _, err = s.GetVersion(ctx, docID, versionID) + assert.Error(t, err) + assert.True(t, errors.IsOfType(documents.ErrDocumentNotFound, err)) + + // successful retrieval + doc.On("CurrentVersion").Return(versionID).Once() + gdoc, err = s.GetVersion(ctx, docID, versionID) + assert.NoError(t, err) + assert.Equal(t, doc, gdoc) + docSrv.AssertExpectations(t) + repo.AssertExpectations(t) + doc.AssertExpectations(t) +} + +func TestService_Update(t *testing.T) { + s := service{} + + // missing did + ctx := context.Background() + payload := documents.UpdatePayload{} + _, err := s.Update(ctx, payload) + assert.Error(t, err) + assert.True(t, errors.IsOfType(contextutil.ErrDIDMissingFromContext, err)) + + // document doesnt exist yet + repo := new(mockRepo) + repo.On("Get", did[:], payload.DocumentID).Return(nil, errors.New("not found")).Once() + s.pendingRepo = repo + ctx = testingconfig.CreateAccountContext(t, cfg) + _, err = s.Update(ctx, payload) + assert.Error(t, err) + + // Patch error + oldModel := new(documents.MockModel) + oldModel.On("Patch", payload).Return(errors.New("error patching")).Once() + repo.On("Get", did[:], payload.DocumentID).Return(oldModel, nil) + _, err = s.Update(ctx, payload) + assert.Error(t, err) + + // Success + oldModel.On("ID").Return(payload.DocumentID).Once() + oldModel.On("Patch", payload).Return(nil).Once() + repo.On("Update", did[:], payload.DocumentID, oldModel).Return(nil).Once() + _, err = s.Update(ctx, payload) + assert.NoError(t, err) +} diff --git a/pending/test_pending.go b/pending/test_pending.go new file mode 100644 index 000000000..90f38a3a5 --- /dev/null +++ b/pending/test_pending.go @@ -0,0 +1,55 @@ +// +build integration unit + +package pending + +import ( + "context" + + "github.com/centrifuge/go-centrifuge/documents" + "github.com/centrifuge/go-centrifuge/jobs" + "github.com/stretchr/testify/mock" +) + +func (b Bootstrapper) TestBootstrap(context map[string]interface{}) error { + return b.Bootstrap(context) +} + +func (Bootstrapper) TestTearDown() error { + return nil +} + +type MockService struct { + mock.Mock + Service +} + +func (m *MockService) Create(ctx context.Context, payload documents.UpdatePayload) (documents.Model, error) { + args := m.Called(ctx, payload) + doc, _ := args.Get(0).(documents.Model) + return doc, args.Error(1) +} + +func (m *MockService) Update(ctx context.Context, payload documents.UpdatePayload) (documents.Model, error) { + args := m.Called(ctx, payload) + doc, _ := args.Get(0).(documents.Model) + return doc, args.Error(1) +} + +func (m *MockService) Commit(ctx context.Context, docID []byte) (documents.Model, jobs.JobID, error) { + args := m.Called(ctx, docID) + doc, _ := args.Get(0).(documents.Model) + jobID, _ := args.Get(1).(jobs.JobID) + return doc, jobID, args.Error(2) +} + +func (m *MockService) Get(ctx context.Context, docID []byte, st documents.Status) (documents.Model, error) { + args := m.Called(ctx, docID, st) + doc, _ := args.Get(0).(documents.Model) + return doc, args.Error(1) +} + +func (m *MockService) GetVersion(ctx context.Context, docID, versionID []byte) (documents.Model, error) { + args := m.Called(ctx, docID, versionID) + doc, _ := args.Get(0).(documents.Model) + return doc, args.Error(1) +} diff --git a/protobufs/account/service.proto b/protobufs/account/service.proto deleted file mode 100644 index dcc16db52..000000000 --- a/protobufs/account/service.proto +++ /dev/null @@ -1,91 +0,0 @@ -syntax = "proto3"; - -package account; - -option go_package = "accountpb"; -option java_multiple_files = true; -option java_outer_classname = "ServiceProto"; -option java_package = "com.account"; - -import "google/api/annotations.proto"; -import "google/protobuf/empty.proto"; -import "protoc-gen-swagger/options/annotations.proto"; - -// AccountService allows for account management on the Node -service AccountService { - rpc GetAccount(GetAccountRequest) returns (AccountData) { - option (google.api.http) = { - get: "/v1/accounts/{account_id}" - }; - option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = { - description: "Get Account" - }; - } - rpc GetAllAccounts(google.protobuf.Empty) returns (GetAllAccountResponse) { - option (google.api.http) = { - get: "/v1/accounts" - }; - option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = { - description: "Get All Accounts" - }; - } - rpc CreateAccount(AccountData) returns (AccountData) { - option (google.api.http) = { - post: "/v1/accounts" - body: "*" - }; - option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = { - description: "Creates an Account" - }; - } - rpc GenerateAccount(google.protobuf.Empty) returns (AccountData) { - option (google.api.http) = { - post: "/v1/accounts/generate" - }; - option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = { - description: "Generates an Account taking defaults based on the main account" - }; - } - rpc UpdateAccount(UpdateAccountRequest) returns (AccountData) { - option (google.api.http) = { - put: "/v1/accounts/{account_id}" - body: "*" - }; - option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = { - description: "Updates an Account" - }; - } -} - -message GetAccountRequest { - string account_id = 1; -} - -message GetAllAccountResponse { - repeated AccountData data = 1; -} - -message UpdateAccountRequest { - string account_id = 1; - AccountData data = 2; -} - -message EthereumAccount { - string address = 1; - string key = 2; - string password = 3; -} - -message KeyPair { - string pub = 1; - string pvt = 2; -} - -message AccountData { - EthereumAccount eth_account = 1; - string eth_default_account_name = 2; - string receive_event_notification_endpoint = 3; - string identity_id = 4; - KeyPair signing_key_pair = 5; - KeyPair p2p_key_pair = 7; -} diff --git a/protobufs/document/service.proto b/protobufs/document/service.proto deleted file mode 100644 index cf766d155..000000000 --- a/protobufs/document/service.proto +++ /dev/null @@ -1,104 +0,0 @@ -syntax = "proto3"; - -package document; - -option go_package = "documentpb"; -option java_multiple_files = true; -option java_outer_classname = "ServiceProto"; -option java_package = "com.document"; - -import "google/protobuf/timestamp.proto"; -import "precise-proofs/proofs/proto/proof.proto"; - -message AccessTokenParams { - // The identity being granted access to the document - string grantee = 4; - // Original identifier of the document - string document_identifier = 2; -} - -message BinaryAttachment { - string name = 1; - //mime type of attached file - string file_type = 2; - // in bytes - uint64 size = 3; - string data = 4; - //the md5 checksum of the original file for easier verification - optional - string checksum = 5; -} - -message PaymentDetails { - //identifying this payment. could be a sequential number, could be a transaction hash of the crypto payment - string id = 1; - google.protobuf.Timestamp date_executed = 2; - //centrifuge id of payee - string payee = 3; - //centrifuge id of payer - string payer = 4; - string amount = 5; - string currency = 6; - //payment reference (e.g. reference field on bank transfer) - string reference = 7; - string bank_name = 8; - string bank_address = 9; - string bank_country = 10; - string bank_account_number = 11; - string bank_account_currency = 12; - string bank_account_holder_name = 13; - string bank_key = 14; - //the ID of the chain to use in URI format. e.g. "ethereum://42/" - string crypto_chain_uri = 15; - //the transaction in which the payment happened - string crypto_transaction_id = 16; - //from address - string crypto_from = 17; - //to address - string crypto_to = 18; -} - -message RequestHeader { - repeated string read_access = 1; - repeated string write_access = 2; -} - -message Signature { - string identity = 1; - string key = 2; // can be blank if the signature is missing - bool valid = 3; // OR: status = valid or missing -} - -message NFT { - string registry = 1; - // read owner from Ethereum and empty when used in POST/PUT - string owner = 3; - string token_id = 2; - // index of the token in the registry - string token_index = 4; -} - -message AccessToken { - string identifier = 1; - string granter = 2; - string grantee = 3; - string document_identifier = 4; -} - -message ResponseHeader { - string document_id = 1; - string version_id = 2; - string author = 3; - string created_at = 4; - repeated string read_access = 5; - repeated string write_access = 6; - string job_id = 7; - repeated NFT nfts = 8; -} - -// Attribute represents a custom attribute -message Attribute { - // this is the sha256 hash of the label of the attribute, is not allowed to be updated by the client - string key = 1; - string type = 2; - string value = 3; -} diff --git a/protobufs/entity/service.proto b/protobufs/entity/service.proto deleted file mode 100644 index c156faf20..000000000 --- a/protobufs/entity/service.proto +++ /dev/null @@ -1,152 +0,0 @@ -syntax = "proto3"; - -package entity; - -option go_package = "entitypb"; -option java_multiple_files = true; -option java_outer_classname = "ServiceProto"; -option java_package = "com.entity"; - -import "document/service.proto"; -import "entity/entity.proto"; -import "google/api/annotations.proto"; -import "protoc-gen-swagger/options/annotations.proto"; - -// EntityService contains all common interactions for entity documents -service EntityService { - rpc Create(EntityCreatePayload) returns (EntityResponse) { - option (google.api.http) = { - post: "/v1/entities" - body: "*" - }; - option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = { - description: "Creates an entity" - }; - } - rpc Update(EntityUpdatePayload) returns (EntityResponse) { - option (google.api.http) = { - put: "/v1/entities/{document_id}" - body: "*" - }; - option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = { - description: "Updates an entity" - }; - } - rpc Get(GetRequest) returns (EntityResponse) { - option (google.api.http) = { - get: "/v1/entities/{document_id}" - }; - option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = { - description: "Get the current entity" - }; - } - // Entity Relation Get - rpc GetEntityByRelationship(GetRequestRelationship) returns (EntityResponse) { - option (google.api.http) = { - get: "/v1/relationships/{relationship_id}/entity" - }; - option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = { - description: "Get entity from business partner" - }; - } - // Entity Relation Share - rpc Share(RelationshipPayload) returns (RelationshipResponse) { - option (google.api.http) = { - post: "/v1/entities/{document_id}/share" - body: "*" - }; - option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = { - description: "Share the entity document with others" - }; - } - // Entity Relation Revoke - rpc Revoke(RelationshipPayload) returns (RelationshipResponse) { - option (google.api.http) = { - post: "/v1/entities/{document_id}/revoke" - body: "*" - }; - option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = { - description: "revoke an entity document share" - }; - } -} - -message GetRequest { - string document_id = 1; -} - -message GetRequestRelationship { - string relationship_id = 1; -} - -message GetVersionRequest { - string identifier = 1; - string version = 2; -} - -message EntityCreatePayload { - repeated string read_access = 1; - repeated string write_access = 2; - EntityData data = 3; - // custom attributes - map attributes = 4; -} - -message EntityUpdatePayload { - string document_id = 1; - repeated string read_access = 2; - repeated string write_access = 3; - EntityData data = 4; - // custom attributes - map attributes = 5; -} - -message EntityResponse { - document.ResponseHeader header = 1; - EntityDataResponse data = 2; - // custom attributes - map attributes = 3; -} - -message Relationship { - string identity = 1; - bool active = 2; -} - -// EntityData is the default entity schema -message EntityData { - string identity = 1; - string legal_name = 2; - // address - repeated Address addresses = 3; - // tax information - repeated PaymentDetail payment_details = 4; - // Entity contact list - repeated Contact contacts = 5; -} - -// Entity Relationships -message EntityDataResponse { - EntityData entity = 1; - repeated Relationship relationships = 2; -} - -message RelationshipPayload { - // entity identifier - string document_id = 1; - string target_identity = 2; -} - -message RelationshipData { - // DID of relationship owner - string owner_identity = 1; - // DID of target identity - string target_identity = 2; - // identifier of Entity whose data can be accessed via this relationship - string entity_identifier = 3; -} - -message RelationshipResponse { - document.ResponseHeader header = 1; - repeated RelationshipData relationship = 2; -} diff --git a/protobufs/funding/funding.proto b/protobufs/funding/funding.proto deleted file mode 100644 index 9b64fb0e5..000000000 --- a/protobufs/funding/funding.proto +++ /dev/null @@ -1,145 +0,0 @@ -syntax = "proto3"; - -package fun; - -option go_package = "funpb"; -option java_multiple_files = true; -option java_outer_classname = "FundingProto"; -option java_package = "com.fun"; - -import "document/service.proto"; -import "google/api/annotations.proto"; -import "protoc-gen-swagger/options/annotations.proto"; - -// FundingService contains all common interactions for funding extension documents -service FundingService { - rpc Create(FundingCreatePayload) returns (FundingResponse) { - option (google.api.http) = { - post: "/v1/documents/{document_id}/funding_agreements" - body: "*" - }; - option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = { - description: "Adds a funding to a document" - }; - } - rpc Update(FundingUpdatePayload) returns (FundingResponse) { - option (google.api.http) = { - put: "/v1/documents/{document_id}/funding_agreements/{agreement_id}" - body: "*" - }; - option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = { - description: "Updates a funding agreement in a document" - }; - } - rpc Sign(Request) returns (FundingResponse) { - option (google.api.http) = { - post: "/v1/documents/{document_id}/funding_agreements/{agreement_id}/sign" - body: "*" - }; - option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = { - description: "Signs funding agreement in a document" - }; - } - rpc Get(Request) returns (FundingResponse) { - option (google.api.http) = { - get: "/v1/documents/{document_id}/funding_agreements/{agreement_id}" - }; - option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = { - description: "Get a funding agreement of a latest document" - }; - } - rpc GetVersion(GetVersionRequest) returns (FundingResponse) { - option (google.api.http) = { - get: "/v1/documents/{document_id}/versions/{version_id}/funding_agreements/{agreement_id}" - }; - option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = { - description: "Get a funding agreement of a document version" - }; - } - rpc GetList(GetListRequest) returns (FundingListResponse) { - option (google.api.http) = { - get: "/v1/documents/{document_id}/funding_agreements" - }; - option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = { - description: "Get all funding agreements of a latest document" - }; - } - rpc GetListVersion(GetListVersionRequest) returns (FundingListResponse) { - option (google.api.http) = { - get: "/v1/documents/{document_id}/versions/{version_id}/funding_agreements" - }; - option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = { - description: "Get all funding agreements of a document version" - }; - } -} - -message FundingCreatePayload { - string document_id = 1; - FundingData data = 2; -} - -message FundingUpdatePayload { - string document_id = 1; - string agreement_id = 2; - FundingData data = 3; -} - -message FundingResponse { - document.ResponseHeader header = 1; - FundingResponseData data = 2; -} - -message FundingListResponse { - document.ResponseHeader header = 1; - repeated FundingResponseData data = 2; -} - -message Request { - string document_id = 1; - string agreement_id = 2; -} - -message GetVersionRequest { - string document_id = 1; - string version_id = 2; - string agreement_id = 3; -} - -message GetListRequest { - string document_id = 1; -} - -message GetListVersionRequest { - string document_id = 1; - string version_id = 2; -} - -// FundingData is the default funding extension schema -message FundingData { - string agreement_id = 1; - string amount = 2; - string apr = 3; - string days = 4; - string fee = 5; - string repayment_due_date = 7; //(e.g. invoice date + grace days) - string repayment_occurred_date = 8; - string repayment_amount = 9; - string currency = 10; // optional if not set currency of payment details will be used - string nft_address = 11; - string payment_details_id = 12; - string funder_id = 13; - string borrower_id = 14; -} - -message FundingResponseData { - FundingData funding = 1; - repeated FundingSignature signatures = 2; -} - -message FundingSignature { - string valid = 1; - string outdated_signature = 2; - string identity = 3; - string signed_version = 4; -} diff --git a/protobufs/gen/go/account/service.pb.go b/protobufs/gen/go/account/service.pb.go deleted file mode 100644 index 636d0cf96..000000000 --- a/protobufs/gen/go/account/service.pb.go +++ /dev/null @@ -1,597 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// source: account/service.proto - -package accountpb - -import ( - context "context" - fmt "fmt" - math "math" - - proto "github.com/golang/protobuf/proto" - empty "github.com/golang/protobuf/ptypes/empty" - _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/options" - _ "google.golang.org/genproto/googleapis/api/annotations" - grpc "google.golang.org/grpc" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package - -type GetAccountRequest struct { - AccountId string `protobuf:"bytes,1,opt,name=account_id,json=accountId,proto3" json:"account_id,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *GetAccountRequest) Reset() { *m = GetAccountRequest{} } -func (m *GetAccountRequest) String() string { return proto.CompactTextString(m) } -func (*GetAccountRequest) ProtoMessage() {} -func (*GetAccountRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_a506582bbbbe51d0, []int{0} -} - -func (m *GetAccountRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_GetAccountRequest.Unmarshal(m, b) -} -func (m *GetAccountRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_GetAccountRequest.Marshal(b, m, deterministic) -} -func (m *GetAccountRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetAccountRequest.Merge(m, src) -} -func (m *GetAccountRequest) XXX_Size() int { - return xxx_messageInfo_GetAccountRequest.Size(m) -} -func (m *GetAccountRequest) XXX_DiscardUnknown() { - xxx_messageInfo_GetAccountRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_GetAccountRequest proto.InternalMessageInfo - -func (m *GetAccountRequest) GetAccountId() string { - if m != nil { - return m.AccountId - } - return "" -} - -type GetAllAccountResponse struct { - Data []*AccountData `protobuf:"bytes,1,rep,name=data,proto3" json:"data,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *GetAllAccountResponse) Reset() { *m = GetAllAccountResponse{} } -func (m *GetAllAccountResponse) String() string { return proto.CompactTextString(m) } -func (*GetAllAccountResponse) ProtoMessage() {} -func (*GetAllAccountResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_a506582bbbbe51d0, []int{1} -} - -func (m *GetAllAccountResponse) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_GetAllAccountResponse.Unmarshal(m, b) -} -func (m *GetAllAccountResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_GetAllAccountResponse.Marshal(b, m, deterministic) -} -func (m *GetAllAccountResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetAllAccountResponse.Merge(m, src) -} -func (m *GetAllAccountResponse) XXX_Size() int { - return xxx_messageInfo_GetAllAccountResponse.Size(m) -} -func (m *GetAllAccountResponse) XXX_DiscardUnknown() { - xxx_messageInfo_GetAllAccountResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_GetAllAccountResponse proto.InternalMessageInfo - -func (m *GetAllAccountResponse) GetData() []*AccountData { - if m != nil { - return m.Data - } - return nil -} - -type UpdateAccountRequest struct { - AccountId string `protobuf:"bytes,1,opt,name=account_id,json=accountId,proto3" json:"account_id,omitempty"` - Data *AccountData `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *UpdateAccountRequest) Reset() { *m = UpdateAccountRequest{} } -func (m *UpdateAccountRequest) String() string { return proto.CompactTextString(m) } -func (*UpdateAccountRequest) ProtoMessage() {} -func (*UpdateAccountRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_a506582bbbbe51d0, []int{2} -} - -func (m *UpdateAccountRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_UpdateAccountRequest.Unmarshal(m, b) -} -func (m *UpdateAccountRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_UpdateAccountRequest.Marshal(b, m, deterministic) -} -func (m *UpdateAccountRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_UpdateAccountRequest.Merge(m, src) -} -func (m *UpdateAccountRequest) XXX_Size() int { - return xxx_messageInfo_UpdateAccountRequest.Size(m) -} -func (m *UpdateAccountRequest) XXX_DiscardUnknown() { - xxx_messageInfo_UpdateAccountRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_UpdateAccountRequest proto.InternalMessageInfo - -func (m *UpdateAccountRequest) GetAccountId() string { - if m != nil { - return m.AccountId - } - return "" -} - -func (m *UpdateAccountRequest) GetData() *AccountData { - if m != nil { - return m.Data - } - return nil -} - -type EthereumAccount struct { - Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` - Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` - Password string `protobuf:"bytes,3,opt,name=password,proto3" json:"password,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *EthereumAccount) Reset() { *m = EthereumAccount{} } -func (m *EthereumAccount) String() string { return proto.CompactTextString(m) } -func (*EthereumAccount) ProtoMessage() {} -func (*EthereumAccount) Descriptor() ([]byte, []int) { - return fileDescriptor_a506582bbbbe51d0, []int{3} -} - -func (m *EthereumAccount) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_EthereumAccount.Unmarshal(m, b) -} -func (m *EthereumAccount) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_EthereumAccount.Marshal(b, m, deterministic) -} -func (m *EthereumAccount) XXX_Merge(src proto.Message) { - xxx_messageInfo_EthereumAccount.Merge(m, src) -} -func (m *EthereumAccount) XXX_Size() int { - return xxx_messageInfo_EthereumAccount.Size(m) -} -func (m *EthereumAccount) XXX_DiscardUnknown() { - xxx_messageInfo_EthereumAccount.DiscardUnknown(m) -} - -var xxx_messageInfo_EthereumAccount proto.InternalMessageInfo - -func (m *EthereumAccount) GetAddress() string { - if m != nil { - return m.Address - } - return "" -} - -func (m *EthereumAccount) GetKey() string { - if m != nil { - return m.Key - } - return "" -} - -func (m *EthereumAccount) GetPassword() string { - if m != nil { - return m.Password - } - return "" -} - -type KeyPair struct { - Pub string `protobuf:"bytes,1,opt,name=pub,proto3" json:"pub,omitempty"` - Pvt string `protobuf:"bytes,2,opt,name=pvt,proto3" json:"pvt,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *KeyPair) Reset() { *m = KeyPair{} } -func (m *KeyPair) String() string { return proto.CompactTextString(m) } -func (*KeyPair) ProtoMessage() {} -func (*KeyPair) Descriptor() ([]byte, []int) { - return fileDescriptor_a506582bbbbe51d0, []int{4} -} - -func (m *KeyPair) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_KeyPair.Unmarshal(m, b) -} -func (m *KeyPair) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_KeyPair.Marshal(b, m, deterministic) -} -func (m *KeyPair) XXX_Merge(src proto.Message) { - xxx_messageInfo_KeyPair.Merge(m, src) -} -func (m *KeyPair) XXX_Size() int { - return xxx_messageInfo_KeyPair.Size(m) -} -func (m *KeyPair) XXX_DiscardUnknown() { - xxx_messageInfo_KeyPair.DiscardUnknown(m) -} - -var xxx_messageInfo_KeyPair proto.InternalMessageInfo - -func (m *KeyPair) GetPub() string { - if m != nil { - return m.Pub - } - return "" -} - -func (m *KeyPair) GetPvt() string { - if m != nil { - return m.Pvt - } - return "" -} - -type AccountData struct { - EthAccount *EthereumAccount `protobuf:"bytes,1,opt,name=eth_account,json=ethAccount,proto3" json:"eth_account,omitempty"` - EthDefaultAccountName string `protobuf:"bytes,2,opt,name=eth_default_account_name,json=ethDefaultAccountName,proto3" json:"eth_default_account_name,omitempty"` - ReceiveEventNotificationEndpoint string `protobuf:"bytes,3,opt,name=receive_event_notification_endpoint,json=receiveEventNotificationEndpoint,proto3" json:"receive_event_notification_endpoint,omitempty"` - IdentityId string `protobuf:"bytes,4,opt,name=identity_id,json=identityId,proto3" json:"identity_id,omitempty"` - SigningKeyPair *KeyPair `protobuf:"bytes,5,opt,name=signing_key_pair,json=signingKeyPair,proto3" json:"signing_key_pair,omitempty"` - P2PKeyPair *KeyPair `protobuf:"bytes,7,opt,name=p2p_key_pair,json=p2pKeyPair,proto3" json:"p2p_key_pair,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *AccountData) Reset() { *m = AccountData{} } -func (m *AccountData) String() string { return proto.CompactTextString(m) } -func (*AccountData) ProtoMessage() {} -func (*AccountData) Descriptor() ([]byte, []int) { - return fileDescriptor_a506582bbbbe51d0, []int{5} -} - -func (m *AccountData) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_AccountData.Unmarshal(m, b) -} -func (m *AccountData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_AccountData.Marshal(b, m, deterministic) -} -func (m *AccountData) XXX_Merge(src proto.Message) { - xxx_messageInfo_AccountData.Merge(m, src) -} -func (m *AccountData) XXX_Size() int { - return xxx_messageInfo_AccountData.Size(m) -} -func (m *AccountData) XXX_DiscardUnknown() { - xxx_messageInfo_AccountData.DiscardUnknown(m) -} - -var xxx_messageInfo_AccountData proto.InternalMessageInfo - -func (m *AccountData) GetEthAccount() *EthereumAccount { - if m != nil { - return m.EthAccount - } - return nil -} - -func (m *AccountData) GetEthDefaultAccountName() string { - if m != nil { - return m.EthDefaultAccountName - } - return "" -} - -func (m *AccountData) GetReceiveEventNotificationEndpoint() string { - if m != nil { - return m.ReceiveEventNotificationEndpoint - } - return "" -} - -func (m *AccountData) GetIdentityId() string { - if m != nil { - return m.IdentityId - } - return "" -} - -func (m *AccountData) GetSigningKeyPair() *KeyPair { - if m != nil { - return m.SigningKeyPair - } - return nil -} - -func (m *AccountData) GetP2PKeyPair() *KeyPair { - if m != nil { - return m.P2PKeyPair - } - return nil -} - -func init() { - proto.RegisterType((*GetAccountRequest)(nil), "account.GetAccountRequest") - proto.RegisterType((*GetAllAccountResponse)(nil), "account.GetAllAccountResponse") - proto.RegisterType((*UpdateAccountRequest)(nil), "account.UpdateAccountRequest") - proto.RegisterType((*EthereumAccount)(nil), "account.EthereumAccount") - proto.RegisterType((*KeyPair)(nil), "account.KeyPair") - proto.RegisterType((*AccountData)(nil), "account.AccountData") -} - -func init() { proto.RegisterFile("account/service.proto", fileDescriptor_a506582bbbbe51d0) } - -var fileDescriptor_a506582bbbbe51d0 = []byte{ - // 714 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0xcb, 0x6e, 0x13, 0x4b, - 0x10, 0xd5, 0xe4, 0x71, 0x7d, 0x53, 0x4e, 0x1c, 0xa7, 0x15, 0xdf, 0x6b, 0x26, 0x24, 0x8c, 0x86, - 0x8d, 0x89, 0x88, 0x47, 0x31, 0x0b, 0x44, 0x90, 0x10, 0x0e, 0xb1, 0xa2, 0x08, 0x11, 0x45, 0x46, - 0x2c, 0x60, 0x33, 0xb4, 0x3d, 0x95, 0x71, 0xcb, 0x76, 0x4f, 0x67, 0xba, 0xed, 0xc8, 0x42, 0x2c, - 0x60, 0xcf, 0xc6, 0x7c, 0x01, 0xdf, 0xc4, 0x17, 0x20, 0xf1, 0x21, 0x68, 0xda, 0x3d, 0x7e, 0x60, - 0x5b, 0x88, 0x95, 0xa7, 0xab, 0xeb, 0xd4, 0x39, 0xa7, 0x5c, 0xd5, 0x50, 0xa0, 0xcd, 0x66, 0xd4, - 0xe3, 0xca, 0x93, 0x18, 0xf7, 0x59, 0x13, 0xcb, 0x22, 0x8e, 0x54, 0x44, 0x32, 0x26, 0x6c, 0xdf, - 0x0d, 0xa3, 0x28, 0xec, 0xa0, 0x47, 0x05, 0xf3, 0x28, 0xe7, 0x91, 0xa2, 0x8a, 0x45, 0x5c, 0x8e, - 0xd2, 0xec, 0x3d, 0x73, 0xab, 0x4f, 0x8d, 0xde, 0xb5, 0x87, 0x5d, 0xa1, 0x06, 0xe6, 0xf2, 0xa1, - 0xfe, 0x69, 0x1e, 0x85, 0xc8, 0x8f, 0xe4, 0x2d, 0x0d, 0x43, 0x8c, 0xbd, 0x48, 0x68, 0xf8, 0x7c, - 0x29, 0xb7, 0x02, 0x3b, 0xe7, 0xa8, 0xaa, 0x23, 0xda, 0x3a, 0xde, 0xf4, 0x50, 0x2a, 0xb2, 0x0f, - 0x60, 0x84, 0xf8, 0x2c, 0x28, 0x5a, 0x8e, 0x55, 0xda, 0xa8, 0x6f, 0x98, 0xc8, 0x45, 0xe0, 0x56, - 0xa1, 0x90, 0x60, 0x3a, 0x9d, 0x31, 0x4c, 0x8a, 0x88, 0x4b, 0x24, 0x25, 0x58, 0x0b, 0xa8, 0xa2, - 0x45, 0xcb, 0x59, 0x2d, 0x65, 0x2b, 0xbb, 0x65, 0x03, 0x29, 0x9b, 0xbc, 0x33, 0xaa, 0x68, 0x5d, - 0x67, 0xb8, 0x3e, 0xec, 0xbe, 0x11, 0x01, 0x55, 0xf8, 0x57, 0xcc, 0x63, 0x82, 0x15, 0xc7, 0xfa, - 0x03, 0xc1, 0x5b, 0xd8, 0xae, 0xa9, 0x16, 0xc6, 0xd8, 0xeb, 0x9a, 0x4b, 0x52, 0x84, 0x0c, 0x0d, - 0x82, 0x18, 0xa5, 0x34, 0x85, 0xd3, 0x23, 0xc9, 0xc3, 0x6a, 0x1b, 0x07, 0xba, 0xea, 0x46, 0x3d, - 0xf9, 0x24, 0x36, 0xfc, 0x2b, 0xa8, 0x94, 0xb7, 0x51, 0x1c, 0x14, 0x57, 0x75, 0x78, 0x7c, 0x76, - 0x8f, 0x20, 0xf3, 0x12, 0x07, 0x57, 0x94, 0xc5, 0x09, 0x50, 0xf4, 0x1a, 0xa6, 0x5c, 0xf2, 0xa9, - 0x23, 0x7d, 0x95, 0x96, 0x12, 0x7d, 0xe5, 0xfe, 0x58, 0x81, 0xec, 0x94, 0x3e, 0xf2, 0x04, 0xb2, - 0xa8, 0x5a, 0xbe, 0x91, 0xae, 0xb1, 0xd9, 0x4a, 0x71, 0x6c, 0xe5, 0x37, 0xd5, 0x75, 0x40, 0xd5, - 0x4a, 0x1d, 0x3c, 0x86, 0x62, 0x02, 0x0d, 0xf0, 0x9a, 0xf6, 0x3a, 0x2a, 0x2d, 0xe1, 0x73, 0xda, - 0x45, 0xc3, 0x58, 0x40, 0xd5, 0x3a, 0x1b, 0x5d, 0x1b, 0xd0, 0x25, 0xed, 0x22, 0x79, 0x05, 0xf7, - 0x63, 0x6c, 0x22, 0xeb, 0xa3, 0x8f, 0x7d, 0x4c, 0x20, 0x91, 0x62, 0xd7, 0xac, 0xa9, 0x47, 0xc1, - 0x47, 0x1e, 0x88, 0x88, 0x71, 0x65, 0x9c, 0x3a, 0x26, 0xb5, 0x96, 0x64, 0x5e, 0x4e, 0x25, 0xd6, - 0x4c, 0x1e, 0xb9, 0x07, 0x59, 0x16, 0x20, 0x57, 0x4c, 0x0d, 0x92, 0xbf, 0x69, 0x4d, 0xc3, 0x20, - 0x0d, 0x5d, 0x04, 0xe4, 0x04, 0xf2, 0x92, 0x85, 0x9c, 0xf1, 0xd0, 0x6f, 0xe3, 0xc0, 0x17, 0x94, - 0xc5, 0xc5, 0x75, 0x6d, 0x34, 0x3f, 0x36, 0x6a, 0x7a, 0x58, 0xcf, 0x99, 0xcc, 0xb4, 0xa7, 0x15, - 0xd8, 0x14, 0x15, 0x31, 0xc1, 0x65, 0x96, 0xe0, 0x40, 0x54, 0x84, 0xf9, 0xae, 0x7c, 0x59, 0x87, - 0x9c, 0xf1, 0xfb, 0x7a, 0xb4, 0x50, 0xe4, 0x06, 0x60, 0x32, 0xd8, 0xc4, 0x1e, 0xc3, 0xe7, 0xa6, - 0xdd, 0x5e, 0x38, 0x46, 0xee, 0xf1, 0xb0, 0xba, 0x65, 0x67, 0xcf, 0x51, 0x39, 0x26, 0xfa, 0xf9, - 0xfb, 0xcf, 0xaf, 0x2b, 0x7b, 0xe4, 0x8e, 0xd7, 0x3f, 0xf6, 0x0c, 0x40, 0x7a, 0x1f, 0x26, 0xd3, - 0xfa, 0x91, 0x48, 0xc8, 0xcd, 0xec, 0x85, 0x24, 0xff, 0x95, 0x47, 0x9b, 0x5a, 0x4e, 0x37, 0xb5, - 0x5c, 0x4b, 0x36, 0xd5, 0x3e, 0x98, 0x91, 0x33, 0xb7, 0x48, 0xee, 0x83, 0x61, 0x95, 0xd8, 0x79, - 0x4d, 0xde, 0xe9, 0xa4, 0x02, 0xa4, 0x56, 0x90, 0x23, 0x9b, 0xd3, 0x0a, 0x48, 0x1b, 0xb6, 0x5e, - 0xc4, 0x38, 0xd9, 0x24, 0xb2, 0xd0, 0xce, 0x12, 0x93, 0xe5, 0x61, 0x75, 0xd7, 0x26, 0x23, 0xbc, - 0x74, 0x28, 0x9f, 0xf1, 0xba, 0xe3, 0xce, 0x30, 0x9d, 0x58, 0x87, 0xe4, 0x9b, 0x05, 0xdb, 0xe7, - 0xc8, 0x31, 0x9e, 0xe2, 0x5b, 0xe6, 0x71, 0x31, 0xe3, 0xfb, 0x61, 0xf5, 0xb9, 0xfd, 0x2c, 0xad, - 0x31, 0xcd, 0xe9, 0x28, 0xda, 0x66, 0x3c, 0x74, 0xcc, 0x74, 0x4b, 0xa7, 0x41, 0x25, 0x06, 0x4e, - 0xc4, 0x1d, 0xd5, 0x42, 0xa7, 0x4b, 0x19, 0x77, 0xe8, 0x94, 0xba, 0xff, 0xdd, 0xc2, 0xcc, 0x3f, - 0x11, 0x9a, 0x92, 0xe4, 0x93, 0x05, 0x5b, 0x33, 0x6f, 0x0b, 0xd9, 0x1f, 0x2b, 0x59, 0xf4, 0xe6, - 0x2c, 0x11, 0xfa, 0x54, 0xb7, 0x66, 0x04, 0x98, 0x6b, 0xcd, 0x81, 0xbd, 0x7c, 0x0c, 0x4e, 0xac, - 0xc3, 0xd3, 0x12, 0x64, 0x9b, 0x51, 0x37, 0xad, 0x7b, 0xba, 0x69, 0x86, 0xf2, 0x2a, 0xe9, 0xd0, - 0x95, 0xf5, 0x2e, 0x7d, 0xd1, 0x44, 0xa3, 0xf1, 0x8f, 0xee, 0xda, 0xa3, 0x5f, 0x01, 0x00, 0x00, - 0xff, 0xff, 0x6a, 0xe1, 0x03, 0x0c, 0x11, 0x06, 0x00, 0x00, -} - -// Reference imports to suppress errors if they are not otherwise used. -var _ context.Context -var _ grpc.ClientConn - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion4 - -// AccountServiceClient is the client API for AccountService service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. -type AccountServiceClient interface { - GetAccount(ctx context.Context, in *GetAccountRequest, opts ...grpc.CallOption) (*AccountData, error) - GetAllAccounts(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*GetAllAccountResponse, error) - CreateAccount(ctx context.Context, in *AccountData, opts ...grpc.CallOption) (*AccountData, error) - GenerateAccount(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*AccountData, error) - UpdateAccount(ctx context.Context, in *UpdateAccountRequest, opts ...grpc.CallOption) (*AccountData, error) -} - -type accountServiceClient struct { - cc *grpc.ClientConn -} - -func NewAccountServiceClient(cc *grpc.ClientConn) AccountServiceClient { - return &accountServiceClient{cc} -} - -func (c *accountServiceClient) GetAccount(ctx context.Context, in *GetAccountRequest, opts ...grpc.CallOption) (*AccountData, error) { - out := new(AccountData) - err := c.cc.Invoke(ctx, "/account.AccountService/GetAccount", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *accountServiceClient) GetAllAccounts(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*GetAllAccountResponse, error) { - out := new(GetAllAccountResponse) - err := c.cc.Invoke(ctx, "/account.AccountService/GetAllAccounts", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *accountServiceClient) CreateAccount(ctx context.Context, in *AccountData, opts ...grpc.CallOption) (*AccountData, error) { - out := new(AccountData) - err := c.cc.Invoke(ctx, "/account.AccountService/CreateAccount", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *accountServiceClient) GenerateAccount(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*AccountData, error) { - out := new(AccountData) - err := c.cc.Invoke(ctx, "/account.AccountService/GenerateAccount", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *accountServiceClient) UpdateAccount(ctx context.Context, in *UpdateAccountRequest, opts ...grpc.CallOption) (*AccountData, error) { - out := new(AccountData) - err := c.cc.Invoke(ctx, "/account.AccountService/UpdateAccount", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// AccountServiceServer is the server API for AccountService service. -type AccountServiceServer interface { - GetAccount(context.Context, *GetAccountRequest) (*AccountData, error) - GetAllAccounts(context.Context, *empty.Empty) (*GetAllAccountResponse, error) - CreateAccount(context.Context, *AccountData) (*AccountData, error) - GenerateAccount(context.Context, *empty.Empty) (*AccountData, error) - UpdateAccount(context.Context, *UpdateAccountRequest) (*AccountData, error) -} - -func RegisterAccountServiceServer(s *grpc.Server, srv AccountServiceServer) { - s.RegisterService(&_AccountService_serviceDesc, srv) -} - -func _AccountService_GetAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetAccountRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(AccountServiceServer).GetAccount(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/account.AccountService/GetAccount", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(AccountServiceServer).GetAccount(ctx, req.(*GetAccountRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _AccountService_GetAllAccounts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(empty.Empty) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(AccountServiceServer).GetAllAccounts(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/account.AccountService/GetAllAccounts", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(AccountServiceServer).GetAllAccounts(ctx, req.(*empty.Empty)) - } - return interceptor(ctx, in, info, handler) -} - -func _AccountService_CreateAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(AccountData) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(AccountServiceServer).CreateAccount(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/account.AccountService/CreateAccount", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(AccountServiceServer).CreateAccount(ctx, req.(*AccountData)) - } - return interceptor(ctx, in, info, handler) -} - -func _AccountService_GenerateAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(empty.Empty) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(AccountServiceServer).GenerateAccount(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/account.AccountService/GenerateAccount", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(AccountServiceServer).GenerateAccount(ctx, req.(*empty.Empty)) - } - return interceptor(ctx, in, info, handler) -} - -func _AccountService_UpdateAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(UpdateAccountRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(AccountServiceServer).UpdateAccount(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/account.AccountService/UpdateAccount", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(AccountServiceServer).UpdateAccount(ctx, req.(*UpdateAccountRequest)) - } - return interceptor(ctx, in, info, handler) -} - -var _AccountService_serviceDesc = grpc.ServiceDesc{ - ServiceName: "account.AccountService", - HandlerType: (*AccountServiceServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "GetAccount", - Handler: _AccountService_GetAccount_Handler, - }, - { - MethodName: "GetAllAccounts", - Handler: _AccountService_GetAllAccounts_Handler, - }, - { - MethodName: "CreateAccount", - Handler: _AccountService_CreateAccount_Handler, - }, - { - MethodName: "GenerateAccount", - Handler: _AccountService_GenerateAccount_Handler, - }, - { - MethodName: "UpdateAccount", - Handler: _AccountService_UpdateAccount_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "account/service.proto", -} diff --git a/protobufs/gen/go/account/service.pb.gw.go b/protobufs/gen/go/account/service.pb.gw.go deleted file mode 100644 index c290653d4..000000000 --- a/protobufs/gen/go/account/service.pb.gw.go +++ /dev/null @@ -1,329 +0,0 @@ -// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. -// source: account/service.proto - -/* -Package accountpb is a reverse proxy. - -It translates gRPC into RESTful JSON APIs. -*/ -package accountpb - -import ( - "io" - "net/http" - - "github.com/golang/protobuf/proto" - "github.com/golang/protobuf/ptypes/empty" - "github.com/grpc-ecosystem/grpc-gateway/runtime" - "github.com/grpc-ecosystem/grpc-gateway/utilities" - "golang.org/x/net/context" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/grpclog" - "google.golang.org/grpc/status" -) - -var _ codes.Code -var _ io.Reader -var _ status.Status -var _ = runtime.String -var _ = utilities.NewDoubleArray - -func request_AccountService_GetAccount_0(ctx context.Context, marshaler runtime.Marshaler, client AccountServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq GetAccountRequest - var metadata runtime.ServerMetadata - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["account_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "account_id") - } - - protoReq.AccountId, err = runtime.String(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "account_id", err) - } - - msg, err := client.GetAccount(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func request_AccountService_GetAllAccounts_0(ctx context.Context, marshaler runtime.Marshaler, client AccountServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq empty.Empty - var metadata runtime.ServerMetadata - - msg, err := client.GetAllAccounts(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func request_AccountService_CreateAccount_0(ctx context.Context, marshaler runtime.Marshaler, client AccountServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq AccountData - var metadata runtime.ServerMetadata - - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - msg, err := client.CreateAccount(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func request_AccountService_GenerateAccount_0(ctx context.Context, marshaler runtime.Marshaler, client AccountServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq empty.Empty - var metadata runtime.ServerMetadata - - msg, err := client.GenerateAccount(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func request_AccountService_UpdateAccount_0(ctx context.Context, marshaler runtime.Marshaler, client AccountServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq UpdateAccountRequest - var metadata runtime.ServerMetadata - - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["account_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "account_id") - } - - protoReq.AccountId, err = runtime.String(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "account_id", err) - } - - msg, err := client.UpdateAccount(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -// RegisterAccountServiceHandlerFromEndpoint is same as RegisterAccountServiceHandler but -// automatically dials to "endpoint" and closes the connection when "ctx" gets done. -func RegisterAccountServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { - conn, err := grpc.Dial(endpoint, opts...) - if err != nil { - return err - } - defer func() { - if err != nil { - if cerr := conn.Close(); cerr != nil { - grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) - } - return - } - go func() { - <-ctx.Done() - if cerr := conn.Close(); cerr != nil { - grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) - } - }() - }() - - return RegisterAccountServiceHandler(ctx, mux, conn) -} - -// RegisterAccountServiceHandler registers the http handlers for service AccountService to "mux". -// The handlers forward requests to the grpc endpoint over "conn". -func RegisterAccountServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { - return RegisterAccountServiceHandlerClient(ctx, mux, NewAccountServiceClient(conn)) -} - -// RegisterAccountServiceHandlerClient registers the http handlers for service AccountService -// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "AccountServiceClient". -// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "AccountServiceClient" -// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in -// "AccountServiceClient" to call the correct interceptors. -func RegisterAccountServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client AccountServiceClient) error { - - mux.Handle("GET", pattern_AccountService_GetAccount_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - if cn, ok := w.(http.CloseNotifier); ok { - go func(done <-chan struct{}, closed <-chan bool) { - select { - case <-done: - case <-closed: - cancel() - } - }(ctx.Done(), cn.CloseNotify()) - } - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_AccountService_GetAccount_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_AccountService_GetAccount_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("GET", pattern_AccountService_GetAllAccounts_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - if cn, ok := w.(http.CloseNotifier); ok { - go func(done <-chan struct{}, closed <-chan bool) { - select { - case <-done: - case <-closed: - cancel() - } - }(ctx.Done(), cn.CloseNotify()) - } - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_AccountService_GetAllAccounts_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_AccountService_GetAllAccounts_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("POST", pattern_AccountService_CreateAccount_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - if cn, ok := w.(http.CloseNotifier); ok { - go func(done <-chan struct{}, closed <-chan bool) { - select { - case <-done: - case <-closed: - cancel() - } - }(ctx.Done(), cn.CloseNotify()) - } - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_AccountService_CreateAccount_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_AccountService_CreateAccount_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("POST", pattern_AccountService_GenerateAccount_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - if cn, ok := w.(http.CloseNotifier); ok { - go func(done <-chan struct{}, closed <-chan bool) { - select { - case <-done: - case <-closed: - cancel() - } - }(ctx.Done(), cn.CloseNotify()) - } - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_AccountService_GenerateAccount_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_AccountService_GenerateAccount_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("PUT", pattern_AccountService_UpdateAccount_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - if cn, ok := w.(http.CloseNotifier); ok { - go func(done <-chan struct{}, closed <-chan bool) { - select { - case <-done: - case <-closed: - cancel() - } - }(ctx.Done(), cn.CloseNotify()) - } - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_AccountService_UpdateAccount_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_AccountService_UpdateAccount_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - return nil -} - -var ( - pattern_AccountService_GetAccount_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "accounts", "account_id"}, "")) - - pattern_AccountService_GetAllAccounts_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "accounts"}, "")) - - pattern_AccountService_CreateAccount_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "accounts"}, "")) - - pattern_AccountService_GenerateAccount_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "accounts", "generate"}, "")) - - pattern_AccountService_UpdateAccount_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "accounts", "account_id"}, "")) -) - -var ( - forward_AccountService_GetAccount_0 = runtime.ForwardResponseMessage - - forward_AccountService_GetAllAccounts_0 = runtime.ForwardResponseMessage - - forward_AccountService_CreateAccount_0 = runtime.ForwardResponseMessage - - forward_AccountService_GenerateAccount_0 = runtime.ForwardResponseMessage - - forward_AccountService_UpdateAccount_0 = runtime.ForwardResponseMessage -) diff --git a/protobufs/gen/go/document/service.pb.go b/protobufs/gen/go/document/service.pb.go deleted file mode 100644 index 25235aa00..000000000 --- a/protobufs/gen/go/document/service.pb.go +++ /dev/null @@ -1,786 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// source: document/service.proto - -package documentpb - -import ( - fmt "fmt" - math "math" - - _ "github.com/centrifuge/precise-proofs/proofs/proto" - proto "github.com/golang/protobuf/proto" - timestamp "github.com/golang/protobuf/ptypes/timestamp" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package - -type AccessTokenParams struct { - // The identity being granted access to the document - Grantee string `protobuf:"bytes,4,opt,name=grantee,proto3" json:"grantee,omitempty"` - // Original identifier of the document - DocumentIdentifier string `protobuf:"bytes,2,opt,name=document_identifier,json=documentIdentifier,proto3" json:"document_identifier,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *AccessTokenParams) Reset() { *m = AccessTokenParams{} } -func (m *AccessTokenParams) String() string { return proto.CompactTextString(m) } -func (*AccessTokenParams) ProtoMessage() {} -func (*AccessTokenParams) Descriptor() ([]byte, []int) { - return fileDescriptor_a33bfc9dfc97cd3f, []int{0} -} - -func (m *AccessTokenParams) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_AccessTokenParams.Unmarshal(m, b) -} -func (m *AccessTokenParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_AccessTokenParams.Marshal(b, m, deterministic) -} -func (m *AccessTokenParams) XXX_Merge(src proto.Message) { - xxx_messageInfo_AccessTokenParams.Merge(m, src) -} -func (m *AccessTokenParams) XXX_Size() int { - return xxx_messageInfo_AccessTokenParams.Size(m) -} -func (m *AccessTokenParams) XXX_DiscardUnknown() { - xxx_messageInfo_AccessTokenParams.DiscardUnknown(m) -} - -var xxx_messageInfo_AccessTokenParams proto.InternalMessageInfo - -func (m *AccessTokenParams) GetGrantee() string { - if m != nil { - return m.Grantee - } - return "" -} - -func (m *AccessTokenParams) GetDocumentIdentifier() string { - if m != nil { - return m.DocumentIdentifier - } - return "" -} - -type BinaryAttachment struct { - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - //mime type of attached file - FileType string `protobuf:"bytes,2,opt,name=file_type,json=fileType,proto3" json:"file_type,omitempty"` - // in bytes - Size uint64 `protobuf:"varint,3,opt,name=size,proto3" json:"size,omitempty"` - Data string `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"` - //the md5 checksum of the original file for easier verification - optional - Checksum string `protobuf:"bytes,5,opt,name=checksum,proto3" json:"checksum,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *BinaryAttachment) Reset() { *m = BinaryAttachment{} } -func (m *BinaryAttachment) String() string { return proto.CompactTextString(m) } -func (*BinaryAttachment) ProtoMessage() {} -func (*BinaryAttachment) Descriptor() ([]byte, []int) { - return fileDescriptor_a33bfc9dfc97cd3f, []int{1} -} - -func (m *BinaryAttachment) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_BinaryAttachment.Unmarshal(m, b) -} -func (m *BinaryAttachment) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_BinaryAttachment.Marshal(b, m, deterministic) -} -func (m *BinaryAttachment) XXX_Merge(src proto.Message) { - xxx_messageInfo_BinaryAttachment.Merge(m, src) -} -func (m *BinaryAttachment) XXX_Size() int { - return xxx_messageInfo_BinaryAttachment.Size(m) -} -func (m *BinaryAttachment) XXX_DiscardUnknown() { - xxx_messageInfo_BinaryAttachment.DiscardUnknown(m) -} - -var xxx_messageInfo_BinaryAttachment proto.InternalMessageInfo - -func (m *BinaryAttachment) GetName() string { - if m != nil { - return m.Name - } - return "" -} - -func (m *BinaryAttachment) GetFileType() string { - if m != nil { - return m.FileType - } - return "" -} - -func (m *BinaryAttachment) GetSize() uint64 { - if m != nil { - return m.Size - } - return 0 -} - -func (m *BinaryAttachment) GetData() string { - if m != nil { - return m.Data - } - return "" -} - -func (m *BinaryAttachment) GetChecksum() string { - if m != nil { - return m.Checksum - } - return "" -} - -type PaymentDetails struct { - //identifying this payment. could be a sequential number, could be a transaction hash of the crypto payment - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - DateExecuted *timestamp.Timestamp `protobuf:"bytes,2,opt,name=date_executed,json=dateExecuted,proto3" json:"date_executed,omitempty"` - //centrifuge id of payee - Payee string `protobuf:"bytes,3,opt,name=payee,proto3" json:"payee,omitempty"` - //centrifuge id of payer - Payer string `protobuf:"bytes,4,opt,name=payer,proto3" json:"payer,omitempty"` - Amount string `protobuf:"bytes,5,opt,name=amount,proto3" json:"amount,omitempty"` - Currency string `protobuf:"bytes,6,opt,name=currency,proto3" json:"currency,omitempty"` - //payment reference (e.g. reference field on bank transfer) - Reference string `protobuf:"bytes,7,opt,name=reference,proto3" json:"reference,omitempty"` - BankName string `protobuf:"bytes,8,opt,name=bank_name,json=bankName,proto3" json:"bank_name,omitempty"` - BankAddress string `protobuf:"bytes,9,opt,name=bank_address,json=bankAddress,proto3" json:"bank_address,omitempty"` - BankCountry string `protobuf:"bytes,10,opt,name=bank_country,json=bankCountry,proto3" json:"bank_country,omitempty"` - BankAccountNumber string `protobuf:"bytes,11,opt,name=bank_account_number,json=bankAccountNumber,proto3" json:"bank_account_number,omitempty"` - BankAccountCurrency string `protobuf:"bytes,12,opt,name=bank_account_currency,json=bankAccountCurrency,proto3" json:"bank_account_currency,omitempty"` - BankAccountHolderName string `protobuf:"bytes,13,opt,name=bank_account_holder_name,json=bankAccountHolderName,proto3" json:"bank_account_holder_name,omitempty"` - BankKey string `protobuf:"bytes,14,opt,name=bank_key,json=bankKey,proto3" json:"bank_key,omitempty"` - //the ID of the chain to use in URI format. e.g. "ethereum://42/" - CryptoChainUri string `protobuf:"bytes,15,opt,name=crypto_chain_uri,json=cryptoChainUri,proto3" json:"crypto_chain_uri,omitempty"` - //the transaction in which the payment happened - CryptoTransactionId string `protobuf:"bytes,16,opt,name=crypto_transaction_id,json=cryptoTransactionId,proto3" json:"crypto_transaction_id,omitempty"` - //from address - CryptoFrom string `protobuf:"bytes,17,opt,name=crypto_from,json=cryptoFrom,proto3" json:"crypto_from,omitempty"` - //to address - CryptoTo string `protobuf:"bytes,18,opt,name=crypto_to,json=cryptoTo,proto3" json:"crypto_to,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *PaymentDetails) Reset() { *m = PaymentDetails{} } -func (m *PaymentDetails) String() string { return proto.CompactTextString(m) } -func (*PaymentDetails) ProtoMessage() {} -func (*PaymentDetails) Descriptor() ([]byte, []int) { - return fileDescriptor_a33bfc9dfc97cd3f, []int{2} -} - -func (m *PaymentDetails) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_PaymentDetails.Unmarshal(m, b) -} -func (m *PaymentDetails) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_PaymentDetails.Marshal(b, m, deterministic) -} -func (m *PaymentDetails) XXX_Merge(src proto.Message) { - xxx_messageInfo_PaymentDetails.Merge(m, src) -} -func (m *PaymentDetails) XXX_Size() int { - return xxx_messageInfo_PaymentDetails.Size(m) -} -func (m *PaymentDetails) XXX_DiscardUnknown() { - xxx_messageInfo_PaymentDetails.DiscardUnknown(m) -} - -var xxx_messageInfo_PaymentDetails proto.InternalMessageInfo - -func (m *PaymentDetails) GetId() string { - if m != nil { - return m.Id - } - return "" -} - -func (m *PaymentDetails) GetDateExecuted() *timestamp.Timestamp { - if m != nil { - return m.DateExecuted - } - return nil -} - -func (m *PaymentDetails) GetPayee() string { - if m != nil { - return m.Payee - } - return "" -} - -func (m *PaymentDetails) GetPayer() string { - if m != nil { - return m.Payer - } - return "" -} - -func (m *PaymentDetails) GetAmount() string { - if m != nil { - return m.Amount - } - return "" -} - -func (m *PaymentDetails) GetCurrency() string { - if m != nil { - return m.Currency - } - return "" -} - -func (m *PaymentDetails) GetReference() string { - if m != nil { - return m.Reference - } - return "" -} - -func (m *PaymentDetails) GetBankName() string { - if m != nil { - return m.BankName - } - return "" -} - -func (m *PaymentDetails) GetBankAddress() string { - if m != nil { - return m.BankAddress - } - return "" -} - -func (m *PaymentDetails) GetBankCountry() string { - if m != nil { - return m.BankCountry - } - return "" -} - -func (m *PaymentDetails) GetBankAccountNumber() string { - if m != nil { - return m.BankAccountNumber - } - return "" -} - -func (m *PaymentDetails) GetBankAccountCurrency() string { - if m != nil { - return m.BankAccountCurrency - } - return "" -} - -func (m *PaymentDetails) GetBankAccountHolderName() string { - if m != nil { - return m.BankAccountHolderName - } - return "" -} - -func (m *PaymentDetails) GetBankKey() string { - if m != nil { - return m.BankKey - } - return "" -} - -func (m *PaymentDetails) GetCryptoChainUri() string { - if m != nil { - return m.CryptoChainUri - } - return "" -} - -func (m *PaymentDetails) GetCryptoTransactionId() string { - if m != nil { - return m.CryptoTransactionId - } - return "" -} - -func (m *PaymentDetails) GetCryptoFrom() string { - if m != nil { - return m.CryptoFrom - } - return "" -} - -func (m *PaymentDetails) GetCryptoTo() string { - if m != nil { - return m.CryptoTo - } - return "" -} - -type RequestHeader struct { - ReadAccess []string `protobuf:"bytes,1,rep,name=read_access,json=readAccess,proto3" json:"read_access,omitempty"` - WriteAccess []string `protobuf:"bytes,2,rep,name=write_access,json=writeAccess,proto3" json:"write_access,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *RequestHeader) Reset() { *m = RequestHeader{} } -func (m *RequestHeader) String() string { return proto.CompactTextString(m) } -func (*RequestHeader) ProtoMessage() {} -func (*RequestHeader) Descriptor() ([]byte, []int) { - return fileDescriptor_a33bfc9dfc97cd3f, []int{3} -} - -func (m *RequestHeader) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_RequestHeader.Unmarshal(m, b) -} -func (m *RequestHeader) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_RequestHeader.Marshal(b, m, deterministic) -} -func (m *RequestHeader) XXX_Merge(src proto.Message) { - xxx_messageInfo_RequestHeader.Merge(m, src) -} -func (m *RequestHeader) XXX_Size() int { - return xxx_messageInfo_RequestHeader.Size(m) -} -func (m *RequestHeader) XXX_DiscardUnknown() { - xxx_messageInfo_RequestHeader.DiscardUnknown(m) -} - -var xxx_messageInfo_RequestHeader proto.InternalMessageInfo - -func (m *RequestHeader) GetReadAccess() []string { - if m != nil { - return m.ReadAccess - } - return nil -} - -func (m *RequestHeader) GetWriteAccess() []string { - if m != nil { - return m.WriteAccess - } - return nil -} - -type Signature struct { - Identity string `protobuf:"bytes,1,opt,name=identity,proto3" json:"identity,omitempty"` - Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` - Valid bool `protobuf:"varint,3,opt,name=valid,proto3" json:"valid,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Signature) Reset() { *m = Signature{} } -func (m *Signature) String() string { return proto.CompactTextString(m) } -func (*Signature) ProtoMessage() {} -func (*Signature) Descriptor() ([]byte, []int) { - return fileDescriptor_a33bfc9dfc97cd3f, []int{4} -} - -func (m *Signature) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Signature.Unmarshal(m, b) -} -func (m *Signature) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Signature.Marshal(b, m, deterministic) -} -func (m *Signature) XXX_Merge(src proto.Message) { - xxx_messageInfo_Signature.Merge(m, src) -} -func (m *Signature) XXX_Size() int { - return xxx_messageInfo_Signature.Size(m) -} -func (m *Signature) XXX_DiscardUnknown() { - xxx_messageInfo_Signature.DiscardUnknown(m) -} - -var xxx_messageInfo_Signature proto.InternalMessageInfo - -func (m *Signature) GetIdentity() string { - if m != nil { - return m.Identity - } - return "" -} - -func (m *Signature) GetKey() string { - if m != nil { - return m.Key - } - return "" -} - -func (m *Signature) GetValid() bool { - if m != nil { - return m.Valid - } - return false -} - -type NFT struct { - Registry string `protobuf:"bytes,1,opt,name=registry,proto3" json:"registry,omitempty"` - // read owner from Ethereum and empty when used in POST/PUT - Owner string `protobuf:"bytes,3,opt,name=owner,proto3" json:"owner,omitempty"` - TokenId string `protobuf:"bytes,2,opt,name=token_id,json=tokenId,proto3" json:"token_id,omitempty"` - // index of the token in the registry - TokenIndex string `protobuf:"bytes,4,opt,name=token_index,json=tokenIndex,proto3" json:"token_index,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *NFT) Reset() { *m = NFT{} } -func (m *NFT) String() string { return proto.CompactTextString(m) } -func (*NFT) ProtoMessage() {} -func (*NFT) Descriptor() ([]byte, []int) { - return fileDescriptor_a33bfc9dfc97cd3f, []int{5} -} - -func (m *NFT) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_NFT.Unmarshal(m, b) -} -func (m *NFT) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_NFT.Marshal(b, m, deterministic) -} -func (m *NFT) XXX_Merge(src proto.Message) { - xxx_messageInfo_NFT.Merge(m, src) -} -func (m *NFT) XXX_Size() int { - return xxx_messageInfo_NFT.Size(m) -} -func (m *NFT) XXX_DiscardUnknown() { - xxx_messageInfo_NFT.DiscardUnknown(m) -} - -var xxx_messageInfo_NFT proto.InternalMessageInfo - -func (m *NFT) GetRegistry() string { - if m != nil { - return m.Registry - } - return "" -} - -func (m *NFT) GetOwner() string { - if m != nil { - return m.Owner - } - return "" -} - -func (m *NFT) GetTokenId() string { - if m != nil { - return m.TokenId - } - return "" -} - -func (m *NFT) GetTokenIndex() string { - if m != nil { - return m.TokenIndex - } - return "" -} - -type AccessToken struct { - Identifier string `protobuf:"bytes,1,opt,name=identifier,proto3" json:"identifier,omitempty"` - Granter string `protobuf:"bytes,2,opt,name=granter,proto3" json:"granter,omitempty"` - Grantee string `protobuf:"bytes,3,opt,name=grantee,proto3" json:"grantee,omitempty"` - DocumentIdentifier string `protobuf:"bytes,4,opt,name=document_identifier,json=documentIdentifier,proto3" json:"document_identifier,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *AccessToken) Reset() { *m = AccessToken{} } -func (m *AccessToken) String() string { return proto.CompactTextString(m) } -func (*AccessToken) ProtoMessage() {} -func (*AccessToken) Descriptor() ([]byte, []int) { - return fileDescriptor_a33bfc9dfc97cd3f, []int{6} -} - -func (m *AccessToken) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_AccessToken.Unmarshal(m, b) -} -func (m *AccessToken) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_AccessToken.Marshal(b, m, deterministic) -} -func (m *AccessToken) XXX_Merge(src proto.Message) { - xxx_messageInfo_AccessToken.Merge(m, src) -} -func (m *AccessToken) XXX_Size() int { - return xxx_messageInfo_AccessToken.Size(m) -} -func (m *AccessToken) XXX_DiscardUnknown() { - xxx_messageInfo_AccessToken.DiscardUnknown(m) -} - -var xxx_messageInfo_AccessToken proto.InternalMessageInfo - -func (m *AccessToken) GetIdentifier() string { - if m != nil { - return m.Identifier - } - return "" -} - -func (m *AccessToken) GetGranter() string { - if m != nil { - return m.Granter - } - return "" -} - -func (m *AccessToken) GetGrantee() string { - if m != nil { - return m.Grantee - } - return "" -} - -func (m *AccessToken) GetDocumentIdentifier() string { - if m != nil { - return m.DocumentIdentifier - } - return "" -} - -type ResponseHeader struct { - DocumentId string `protobuf:"bytes,1,opt,name=document_id,json=documentId,proto3" json:"document_id,omitempty"` - VersionId string `protobuf:"bytes,2,opt,name=version_id,json=versionId,proto3" json:"version_id,omitempty"` - Author string `protobuf:"bytes,3,opt,name=author,proto3" json:"author,omitempty"` - CreatedAt string `protobuf:"bytes,4,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` - ReadAccess []string `protobuf:"bytes,5,rep,name=read_access,json=readAccess,proto3" json:"read_access,omitempty"` - WriteAccess []string `protobuf:"bytes,6,rep,name=write_access,json=writeAccess,proto3" json:"write_access,omitempty"` - JobId string `protobuf:"bytes,7,opt,name=job_id,json=jobId,proto3" json:"job_id,omitempty"` - Nfts []*NFT `protobuf:"bytes,8,rep,name=nfts,proto3" json:"nfts,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ResponseHeader) Reset() { *m = ResponseHeader{} } -func (m *ResponseHeader) String() string { return proto.CompactTextString(m) } -func (*ResponseHeader) ProtoMessage() {} -func (*ResponseHeader) Descriptor() ([]byte, []int) { - return fileDescriptor_a33bfc9dfc97cd3f, []int{7} -} - -func (m *ResponseHeader) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ResponseHeader.Unmarshal(m, b) -} -func (m *ResponseHeader) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ResponseHeader.Marshal(b, m, deterministic) -} -func (m *ResponseHeader) XXX_Merge(src proto.Message) { - xxx_messageInfo_ResponseHeader.Merge(m, src) -} -func (m *ResponseHeader) XXX_Size() int { - return xxx_messageInfo_ResponseHeader.Size(m) -} -func (m *ResponseHeader) XXX_DiscardUnknown() { - xxx_messageInfo_ResponseHeader.DiscardUnknown(m) -} - -var xxx_messageInfo_ResponseHeader proto.InternalMessageInfo - -func (m *ResponseHeader) GetDocumentId() string { - if m != nil { - return m.DocumentId - } - return "" -} - -func (m *ResponseHeader) GetVersionId() string { - if m != nil { - return m.VersionId - } - return "" -} - -func (m *ResponseHeader) GetAuthor() string { - if m != nil { - return m.Author - } - return "" -} - -func (m *ResponseHeader) GetCreatedAt() string { - if m != nil { - return m.CreatedAt - } - return "" -} - -func (m *ResponseHeader) GetReadAccess() []string { - if m != nil { - return m.ReadAccess - } - return nil -} - -func (m *ResponseHeader) GetWriteAccess() []string { - if m != nil { - return m.WriteAccess - } - return nil -} - -func (m *ResponseHeader) GetJobId() string { - if m != nil { - return m.JobId - } - return "" -} - -func (m *ResponseHeader) GetNfts() []*NFT { - if m != nil { - return m.Nfts - } - return nil -} - -// Attribute represents a custom attribute -type Attribute struct { - // this is the sha256 hash of the label of the attribute, is not allowed to be updated by the client - Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` - Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"` - Value string `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Attribute) Reset() { *m = Attribute{} } -func (m *Attribute) String() string { return proto.CompactTextString(m) } -func (*Attribute) ProtoMessage() {} -func (*Attribute) Descriptor() ([]byte, []int) { - return fileDescriptor_a33bfc9dfc97cd3f, []int{8} -} - -func (m *Attribute) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Attribute.Unmarshal(m, b) -} -func (m *Attribute) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Attribute.Marshal(b, m, deterministic) -} -func (m *Attribute) XXX_Merge(src proto.Message) { - xxx_messageInfo_Attribute.Merge(m, src) -} -func (m *Attribute) XXX_Size() int { - return xxx_messageInfo_Attribute.Size(m) -} -func (m *Attribute) XXX_DiscardUnknown() { - xxx_messageInfo_Attribute.DiscardUnknown(m) -} - -var xxx_messageInfo_Attribute proto.InternalMessageInfo - -func (m *Attribute) GetKey() string { - if m != nil { - return m.Key - } - return "" -} - -func (m *Attribute) GetType() string { - if m != nil { - return m.Type - } - return "" -} - -func (m *Attribute) GetValue() string { - if m != nil { - return m.Value - } - return "" -} - -func init() { - proto.RegisterType((*AccessTokenParams)(nil), "document.AccessTokenParams") - proto.RegisterType((*BinaryAttachment)(nil), "document.BinaryAttachment") - proto.RegisterType((*PaymentDetails)(nil), "document.PaymentDetails") - proto.RegisterType((*RequestHeader)(nil), "document.RequestHeader") - proto.RegisterType((*Signature)(nil), "document.Signature") - proto.RegisterType((*NFT)(nil), "document.NFT") - proto.RegisterType((*AccessToken)(nil), "document.AccessToken") - proto.RegisterType((*ResponseHeader)(nil), "document.ResponseHeader") - proto.RegisterType((*Attribute)(nil), "document.Attribute") -} - -func init() { proto.RegisterFile("document/service.proto", fileDescriptor_a33bfc9dfc97cd3f) } - -var fileDescriptor_a33bfc9dfc97cd3f = []byte{ - // 884 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x55, 0x4b, 0x6f, 0x23, 0x45, - 0x10, 0x96, 0x9f, 0xf1, 0x94, 0x1d, 0x93, 0x74, 0xc8, 0x6a, 0x08, 0x8f, 0xcd, 0xfa, 0x42, 0x84, - 0x84, 0x2d, 0x85, 0x03, 0x47, 0xe4, 0x04, 0xc2, 0x46, 0x48, 0x21, 0x72, 0xcc, 0x85, 0x03, 0xa3, - 0xf6, 0x74, 0xd9, 0xee, 0x8d, 0xa7, 0x7b, 0xe8, 0xee, 0xc9, 0xee, 0x70, 0x47, 0xdc, 0x39, 0xf0, - 0x7b, 0x51, 0xbf, 0x6c, 0x07, 0x84, 0xd8, 0x93, 0xfb, 0xfb, 0xea, 0x31, 0x5f, 0x55, 0x57, 0x97, - 0xe1, 0x05, 0x93, 0x79, 0x55, 0xa0, 0x30, 0x13, 0x8d, 0xea, 0x89, 0xe7, 0x38, 0x2e, 0x95, 0x34, - 0x92, 0xf4, 0x22, 0x7f, 0xf6, 0x72, 0x25, 0xe5, 0x6a, 0x83, 0x13, 0xc7, 0x2f, 0xaa, 0xe5, 0xc4, - 0xf0, 0x02, 0xb5, 0xa1, 0x45, 0xe9, 0x5d, 0xcf, 0x3e, 0x2f, 0x15, 0xe6, 0x5c, 0xe3, 0x97, 0xa5, - 0x92, 0x72, 0xa9, 0x27, 0xbb, 0x1f, 0x23, 0x3d, 0xf0, 0x8e, 0xa3, 0x5f, 0xe0, 0x78, 0x9a, 0xe7, - 0xa8, 0xf5, 0x5c, 0x3e, 0xa2, 0xb8, 0xa7, 0x8a, 0x16, 0x9a, 0xa4, 0x70, 0xb0, 0x52, 0x54, 0x18, - 0xc4, 0xb4, 0x7d, 0xde, 0xb8, 0x48, 0x66, 0x11, 0x92, 0x09, 0x9c, 0x44, 0x11, 0x19, 0x67, 0x28, - 0x0c, 0x5f, 0x72, 0x54, 0x69, 0xd3, 0x79, 0x91, 0x68, 0xba, 0xdd, 0x5a, 0x46, 0x7f, 0x34, 0xe0, - 0xe8, 0x8a, 0x0b, 0xaa, 0xea, 0xa9, 0x31, 0x34, 0x5f, 0x5b, 0x33, 0x21, 0xd0, 0x16, 0xb4, 0xc0, - 0xb4, 0xe1, 0xc2, 0xdc, 0x99, 0x7c, 0x0c, 0xc9, 0x92, 0x6f, 0x30, 0x33, 0x75, 0x89, 0x21, 0x5f, - 0xcf, 0x12, 0xf3, 0xba, 0x44, 0x1b, 0xa0, 0xf9, 0x6f, 0x98, 0xb6, 0xce, 0x1b, 0x17, 0xed, 0x99, - 0x3b, 0x5b, 0x8e, 0x51, 0x43, 0x83, 0x42, 0x77, 0x26, 0x67, 0xd0, 0xcb, 0xd7, 0x98, 0x3f, 0xea, - 0xaa, 0x48, 0x3b, 0x3e, 0x47, 0xc4, 0xa3, 0xbf, 0x3a, 0x30, 0xbc, 0xa7, 0xb5, 0x15, 0xf0, 0x2d, - 0x1a, 0xca, 0x37, 0x9a, 0x0c, 0xa1, 0xc9, 0x59, 0x50, 0xd1, 0xe4, 0x8c, 0x7c, 0x03, 0x87, 0x8c, - 0x1a, 0xcc, 0xf0, 0x1d, 0xe6, 0x95, 0x41, 0xe6, 0x74, 0xf4, 0x2f, 0xcf, 0xc6, 0xbe, 0xdd, 0xe3, - 0xd8, 0xee, 0xf1, 0x3c, 0xb6, 0x7b, 0x36, 0xb0, 0x01, 0xdf, 0x05, 0x7f, 0xf2, 0x21, 0x74, 0x4a, - 0x5a, 0xa3, 0x17, 0x9a, 0xcc, 0x3c, 0x88, 0xac, 0x0a, 0x52, 0x3d, 0x20, 0x2f, 0xa0, 0x4b, 0x0b, - 0x59, 0x09, 0x13, 0x94, 0x06, 0xe4, 0x6a, 0xa8, 0x94, 0x42, 0x91, 0xd7, 0x69, 0x37, 0xd4, 0x10, - 0x30, 0xf9, 0x04, 0x12, 0x85, 0x4b, 0xb4, 0x00, 0xd3, 0x03, 0x67, 0xdc, 0x11, 0xb6, 0x85, 0x0b, - 0x2a, 0x1e, 0x33, 0xd7, 0xdb, 0x9e, 0x0f, 0xb5, 0xc4, 0x9d, 0xed, 0xef, 0x2b, 0x18, 0x38, 0x23, - 0x65, 0x4c, 0xa1, 0xd6, 0x69, 0xe2, 0xec, 0x7d, 0xcb, 0x4d, 0x3d, 0xb5, 0x75, 0xc9, 0xad, 0x0e, - 0x55, 0xa7, 0xb0, 0x73, 0xb9, 0xf6, 0x14, 0x19, 0xc3, 0x89, 0xcf, 0x92, 0x3b, 0xa7, 0x4c, 0x54, - 0xc5, 0x02, 0x55, 0xda, 0x77, 0x9e, 0xc7, 0x2e, 0x99, 0xb7, 0xdc, 0x39, 0x03, 0xb9, 0x84, 0xd3, - 0x67, 0xfe, 0xdb, 0xca, 0x06, 0x2e, 0xe2, 0x64, 0x2f, 0xe2, 0x3a, 0x16, 0xf9, 0x35, 0xa4, 0xcf, - 0x62, 0xd6, 0x72, 0xc3, 0x50, 0xf9, 0xaa, 0x0e, 0x5d, 0xd8, 0xe9, 0x5e, 0xd8, 0x6b, 0x67, 0x75, - 0x25, 0x7e, 0x04, 0xae, 0xdc, 0xec, 0x11, 0xeb, 0x74, 0xe8, 0xe7, 0xd6, 0xe2, 0x1f, 0xb0, 0x26, - 0x17, 0x70, 0x94, 0xab, 0xba, 0x34, 0x32, 0xcb, 0xd7, 0x94, 0x8b, 0xac, 0x52, 0x3c, 0xfd, 0xc0, - 0xb9, 0x0c, 0x3d, 0x7f, 0x6d, 0xe9, 0x9f, 0x14, 0xb7, 0x8a, 0x83, 0xa7, 0x51, 0x54, 0x68, 0x9a, - 0x1b, 0x2e, 0x45, 0xc6, 0x59, 0x7a, 0xe4, 0x15, 0x7b, 0xe3, 0x7c, 0x67, 0xbb, 0x65, 0xe4, 0x25, - 0xf4, 0x43, 0xcc, 0x52, 0xc9, 0x22, 0x3d, 0x76, 0x9e, 0xe0, 0xa9, 0x1b, 0x25, 0x0b, 0x7b, 0x33, - 0x31, 0xa9, 0x4c, 0x49, 0xb8, 0x54, 0x9f, 0x48, 0x8e, 0x1e, 0xe0, 0x70, 0x86, 0xbf, 0x56, 0xa8, - 0xcd, 0x6b, 0xa4, 0x0c, 0x95, 0x4d, 0xa7, 0x90, 0x32, 0xdb, 0x00, 0x7b, 0x53, 0x8d, 0xf3, 0x96, - 0x4d, 0x67, 0x29, 0xff, 0x54, 0xed, 0x45, 0xbd, 0x55, 0xdc, 0x60, 0xf4, 0x68, 0x3a, 0x8f, 0xbe, - 0xe3, 0xbc, 0xcb, 0xe8, 0x47, 0x48, 0x1e, 0xf8, 0x4a, 0x50, 0x53, 0x29, 0xb4, 0x23, 0xe5, 0x1f, - 0xab, 0xa9, 0xc3, 0xb4, 0x6f, 0x31, 0x39, 0x82, 0x96, 0xed, 0x97, 0x7f, 0x71, 0xf6, 0x68, 0xc7, - 0xf5, 0x89, 0x6e, 0x38, 0x73, 0x43, 0xdc, 0x9b, 0x79, 0x30, 0xd2, 0xd0, 0xba, 0xbb, 0x99, 0xdb, - 0x54, 0x0a, 0x57, 0x5c, 0xdb, 0xf9, 0x08, 0xa9, 0x22, 0xb6, 0x81, 0xf2, 0xad, 0x40, 0x15, 0xa7, - 0xdf, 0x01, 0x7b, 0x2b, 0xc6, 0xee, 0x16, 0xdb, 0x43, 0xff, 0x95, 0x03, 0x87, 0x7d, 0xdf, 0x82, - 0x49, 0x30, 0x7c, 0x17, 0x9e, 0x07, 0x78, 0xab, 0x65, 0x46, 0x7f, 0x36, 0xa0, 0xbf, 0xb7, 0x9e, - 0xc8, 0x67, 0x00, 0x7b, 0x5b, 0xc7, 0x7f, 0x7f, 0x8f, 0xd9, 0x2d, 0xae, 0xb8, 0x92, 0x22, 0xdc, - 0x5f, 0x69, 0xad, 0xf7, 0x5a, 0x69, 0xed, 0xff, 0x5c, 0x69, 0xbf, 0x37, 0x61, 0x38, 0x43, 0x5d, - 0x4a, 0xa1, 0x71, 0x77, 0x63, 0x7b, 0x39, 0xa2, 0xb0, 0x5d, 0x2c, 0xf9, 0x14, 0xe0, 0x09, 0x95, - 0x0e, 0xa3, 0xe4, 0xb5, 0x25, 0x81, 0xb9, 0x65, 0x6e, 0x17, 0x54, 0x66, 0x2d, 0x63, 0xeb, 0x02, - 0xb2, 0x61, 0xb9, 0x42, 0x6a, 0x90, 0x65, 0xd4, 0x04, 0x49, 0x49, 0x60, 0xa6, 0xe6, 0x9f, 0x83, - 0xd2, 0xf9, 0xdf, 0x41, 0xe9, 0xfe, 0x6b, 0x50, 0xc8, 0x29, 0x74, 0xdf, 0xc8, 0x85, 0x55, 0xe5, - 0xf7, 0x49, 0xe7, 0x8d, 0x5c, 0xdc, 0x32, 0xf2, 0x0a, 0xda, 0x62, 0x69, 0x74, 0xda, 0x3b, 0x6f, - 0x5d, 0xf4, 0x2f, 0x0f, 0xc7, 0xb1, 0x96, 0xf1, 0xdd, 0xcd, 0x7c, 0xe6, 0x4c, 0xa3, 0xef, 0x21, - 0x99, 0x1a, 0xa3, 0xf8, 0xa2, 0x32, 0x18, 0xc7, 0xa8, 0xb1, 0x1b, 0x23, 0x02, 0xed, 0xbd, 0x5d, - 0xee, 0xce, 0x61, 0xb4, 0xaa, 0xed, 0x7e, 0x74, 0xe0, 0xea, 0x0b, 0x18, 0xe4, 0xb2, 0xd8, 0x7e, - 0xe2, 0x6a, 0xf0, 0xe0, 0xff, 0xf6, 0xee, 0xed, 0xba, 0xbd, 0x6f, 0xfc, 0xbc, 0x6d, 0x64, 0xb9, - 0x58, 0x74, 0xdd, 0x0e, 0xfe, 0xea, 0xef, 0x00, 0x00, 0x00, 0xff, 0xff, 0x61, 0x0e, 0x5d, 0x2d, - 0x24, 0x07, 0x00, 0x00, -} diff --git a/protobufs/gen/go/entity/service.pb.go b/protobufs/gen/go/entity/service.pb.go deleted file mode 100644 index 566e09a0d..000000000 --- a/protobufs/gen/go/entity/service.pb.go +++ /dev/null @@ -1,1000 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// source: entity/service.proto - -package entitypb - -import ( - context "context" - fmt "fmt" - math "math" - - entity "github.com/centrifuge/centrifuge-protobufs/gen/go/entity" - document "github.com/centrifuge/go-centrifuge/protobufs/gen/go/document" - proto "github.com/golang/protobuf/proto" - _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/options" - _ "google.golang.org/genproto/googleapis/api/annotations" - grpc "google.golang.org/grpc" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package - -type GetRequest struct { - DocumentId string `protobuf:"bytes,1,opt,name=document_id,json=documentId,proto3" json:"document_id,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *GetRequest) Reset() { *m = GetRequest{} } -func (m *GetRequest) String() string { return proto.CompactTextString(m) } -func (*GetRequest) ProtoMessage() {} -func (*GetRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b437217b9e14a2, []int{0} -} - -func (m *GetRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_GetRequest.Unmarshal(m, b) -} -func (m *GetRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_GetRequest.Marshal(b, m, deterministic) -} -func (m *GetRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetRequest.Merge(m, src) -} -func (m *GetRequest) XXX_Size() int { - return xxx_messageInfo_GetRequest.Size(m) -} -func (m *GetRequest) XXX_DiscardUnknown() { - xxx_messageInfo_GetRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_GetRequest proto.InternalMessageInfo - -func (m *GetRequest) GetDocumentId() string { - if m != nil { - return m.DocumentId - } - return "" -} - -type GetRequestRelationship struct { - RelationshipId string `protobuf:"bytes,1,opt,name=relationship_id,json=relationshipId,proto3" json:"relationship_id,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *GetRequestRelationship) Reset() { *m = GetRequestRelationship{} } -func (m *GetRequestRelationship) String() string { return proto.CompactTextString(m) } -func (*GetRequestRelationship) ProtoMessage() {} -func (*GetRequestRelationship) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b437217b9e14a2, []int{1} -} - -func (m *GetRequestRelationship) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_GetRequestRelationship.Unmarshal(m, b) -} -func (m *GetRequestRelationship) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_GetRequestRelationship.Marshal(b, m, deterministic) -} -func (m *GetRequestRelationship) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetRequestRelationship.Merge(m, src) -} -func (m *GetRequestRelationship) XXX_Size() int { - return xxx_messageInfo_GetRequestRelationship.Size(m) -} -func (m *GetRequestRelationship) XXX_DiscardUnknown() { - xxx_messageInfo_GetRequestRelationship.DiscardUnknown(m) -} - -var xxx_messageInfo_GetRequestRelationship proto.InternalMessageInfo - -func (m *GetRequestRelationship) GetRelationshipId() string { - if m != nil { - return m.RelationshipId - } - return "" -} - -type GetVersionRequest struct { - Identifier string `protobuf:"bytes,1,opt,name=identifier,proto3" json:"identifier,omitempty"` - Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *GetVersionRequest) Reset() { *m = GetVersionRequest{} } -func (m *GetVersionRequest) String() string { return proto.CompactTextString(m) } -func (*GetVersionRequest) ProtoMessage() {} -func (*GetVersionRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b437217b9e14a2, []int{2} -} - -func (m *GetVersionRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_GetVersionRequest.Unmarshal(m, b) -} -func (m *GetVersionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_GetVersionRequest.Marshal(b, m, deterministic) -} -func (m *GetVersionRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetVersionRequest.Merge(m, src) -} -func (m *GetVersionRequest) XXX_Size() int { - return xxx_messageInfo_GetVersionRequest.Size(m) -} -func (m *GetVersionRequest) XXX_DiscardUnknown() { - xxx_messageInfo_GetVersionRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_GetVersionRequest proto.InternalMessageInfo - -func (m *GetVersionRequest) GetIdentifier() string { - if m != nil { - return m.Identifier - } - return "" -} - -func (m *GetVersionRequest) GetVersion() string { - if m != nil { - return m.Version - } - return "" -} - -type EntityCreatePayload struct { - ReadAccess []string `protobuf:"bytes,1,rep,name=read_access,json=readAccess,proto3" json:"read_access,omitempty"` - WriteAccess []string `protobuf:"bytes,2,rep,name=write_access,json=writeAccess,proto3" json:"write_access,omitempty"` - Data *EntityData `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` - // custom attributes - Attributes map[string]*document.Attribute `protobuf:"bytes,4,rep,name=attributes,proto3" json:"attributes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *EntityCreatePayload) Reset() { *m = EntityCreatePayload{} } -func (m *EntityCreatePayload) String() string { return proto.CompactTextString(m) } -func (*EntityCreatePayload) ProtoMessage() {} -func (*EntityCreatePayload) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b437217b9e14a2, []int{3} -} - -func (m *EntityCreatePayload) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_EntityCreatePayload.Unmarshal(m, b) -} -func (m *EntityCreatePayload) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_EntityCreatePayload.Marshal(b, m, deterministic) -} -func (m *EntityCreatePayload) XXX_Merge(src proto.Message) { - xxx_messageInfo_EntityCreatePayload.Merge(m, src) -} -func (m *EntityCreatePayload) XXX_Size() int { - return xxx_messageInfo_EntityCreatePayload.Size(m) -} -func (m *EntityCreatePayload) XXX_DiscardUnknown() { - xxx_messageInfo_EntityCreatePayload.DiscardUnknown(m) -} - -var xxx_messageInfo_EntityCreatePayload proto.InternalMessageInfo - -func (m *EntityCreatePayload) GetReadAccess() []string { - if m != nil { - return m.ReadAccess - } - return nil -} - -func (m *EntityCreatePayload) GetWriteAccess() []string { - if m != nil { - return m.WriteAccess - } - return nil -} - -func (m *EntityCreatePayload) GetData() *EntityData { - if m != nil { - return m.Data - } - return nil -} - -func (m *EntityCreatePayload) GetAttributes() map[string]*document.Attribute { - if m != nil { - return m.Attributes - } - return nil -} - -type EntityUpdatePayload struct { - DocumentId string `protobuf:"bytes,1,opt,name=document_id,json=documentId,proto3" json:"document_id,omitempty"` - ReadAccess []string `protobuf:"bytes,2,rep,name=read_access,json=readAccess,proto3" json:"read_access,omitempty"` - WriteAccess []string `protobuf:"bytes,3,rep,name=write_access,json=writeAccess,proto3" json:"write_access,omitempty"` - Data *EntityData `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"` - // custom attributes - Attributes map[string]*document.Attribute `protobuf:"bytes,5,rep,name=attributes,proto3" json:"attributes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *EntityUpdatePayload) Reset() { *m = EntityUpdatePayload{} } -func (m *EntityUpdatePayload) String() string { return proto.CompactTextString(m) } -func (*EntityUpdatePayload) ProtoMessage() {} -func (*EntityUpdatePayload) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b437217b9e14a2, []int{4} -} - -func (m *EntityUpdatePayload) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_EntityUpdatePayload.Unmarshal(m, b) -} -func (m *EntityUpdatePayload) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_EntityUpdatePayload.Marshal(b, m, deterministic) -} -func (m *EntityUpdatePayload) XXX_Merge(src proto.Message) { - xxx_messageInfo_EntityUpdatePayload.Merge(m, src) -} -func (m *EntityUpdatePayload) XXX_Size() int { - return xxx_messageInfo_EntityUpdatePayload.Size(m) -} -func (m *EntityUpdatePayload) XXX_DiscardUnknown() { - xxx_messageInfo_EntityUpdatePayload.DiscardUnknown(m) -} - -var xxx_messageInfo_EntityUpdatePayload proto.InternalMessageInfo - -func (m *EntityUpdatePayload) GetDocumentId() string { - if m != nil { - return m.DocumentId - } - return "" -} - -func (m *EntityUpdatePayload) GetReadAccess() []string { - if m != nil { - return m.ReadAccess - } - return nil -} - -func (m *EntityUpdatePayload) GetWriteAccess() []string { - if m != nil { - return m.WriteAccess - } - return nil -} - -func (m *EntityUpdatePayload) GetData() *EntityData { - if m != nil { - return m.Data - } - return nil -} - -func (m *EntityUpdatePayload) GetAttributes() map[string]*document.Attribute { - if m != nil { - return m.Attributes - } - return nil -} - -type EntityResponse struct { - Header *document.ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"` - Data *EntityDataResponse `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` - // custom attributes - Attributes map[string]*document.Attribute `protobuf:"bytes,3,rep,name=attributes,proto3" json:"attributes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *EntityResponse) Reset() { *m = EntityResponse{} } -func (m *EntityResponse) String() string { return proto.CompactTextString(m) } -func (*EntityResponse) ProtoMessage() {} -func (*EntityResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b437217b9e14a2, []int{5} -} - -func (m *EntityResponse) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_EntityResponse.Unmarshal(m, b) -} -func (m *EntityResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_EntityResponse.Marshal(b, m, deterministic) -} -func (m *EntityResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_EntityResponse.Merge(m, src) -} -func (m *EntityResponse) XXX_Size() int { - return xxx_messageInfo_EntityResponse.Size(m) -} -func (m *EntityResponse) XXX_DiscardUnknown() { - xxx_messageInfo_EntityResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_EntityResponse proto.InternalMessageInfo - -func (m *EntityResponse) GetHeader() *document.ResponseHeader { - if m != nil { - return m.Header - } - return nil -} - -func (m *EntityResponse) GetData() *EntityDataResponse { - if m != nil { - return m.Data - } - return nil -} - -func (m *EntityResponse) GetAttributes() map[string]*document.Attribute { - if m != nil { - return m.Attributes - } - return nil -} - -type Relationship struct { - Identity string `protobuf:"bytes,1,opt,name=identity,proto3" json:"identity,omitempty"` - Active bool `protobuf:"varint,2,opt,name=active,proto3" json:"active,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Relationship) Reset() { *m = Relationship{} } -func (m *Relationship) String() string { return proto.CompactTextString(m) } -func (*Relationship) ProtoMessage() {} -func (*Relationship) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b437217b9e14a2, []int{6} -} - -func (m *Relationship) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Relationship.Unmarshal(m, b) -} -func (m *Relationship) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Relationship.Marshal(b, m, deterministic) -} -func (m *Relationship) XXX_Merge(src proto.Message) { - xxx_messageInfo_Relationship.Merge(m, src) -} -func (m *Relationship) XXX_Size() int { - return xxx_messageInfo_Relationship.Size(m) -} -func (m *Relationship) XXX_DiscardUnknown() { - xxx_messageInfo_Relationship.DiscardUnknown(m) -} - -var xxx_messageInfo_Relationship proto.InternalMessageInfo - -func (m *Relationship) GetIdentity() string { - if m != nil { - return m.Identity - } - return "" -} - -func (m *Relationship) GetActive() bool { - if m != nil { - return m.Active - } - return false -} - -// EntityData is the default entity schema -type EntityData struct { - Identity string `protobuf:"bytes,1,opt,name=identity,proto3" json:"identity,omitempty"` - LegalName string `protobuf:"bytes,2,opt,name=legal_name,json=legalName,proto3" json:"legal_name,omitempty"` - // address - Addresses []*entity.Address `protobuf:"bytes,3,rep,name=addresses,proto3" json:"addresses,omitempty"` - // tax information - PaymentDetails []*entity.PaymentDetail `protobuf:"bytes,4,rep,name=payment_details,json=paymentDetails,proto3" json:"payment_details,omitempty"` - // Entity contact list - Contacts []*entity.Contact `protobuf:"bytes,5,rep,name=contacts,proto3" json:"contacts,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *EntityData) Reset() { *m = EntityData{} } -func (m *EntityData) String() string { return proto.CompactTextString(m) } -func (*EntityData) ProtoMessage() {} -func (*EntityData) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b437217b9e14a2, []int{7} -} - -func (m *EntityData) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_EntityData.Unmarshal(m, b) -} -func (m *EntityData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_EntityData.Marshal(b, m, deterministic) -} -func (m *EntityData) XXX_Merge(src proto.Message) { - xxx_messageInfo_EntityData.Merge(m, src) -} -func (m *EntityData) XXX_Size() int { - return xxx_messageInfo_EntityData.Size(m) -} -func (m *EntityData) XXX_DiscardUnknown() { - xxx_messageInfo_EntityData.DiscardUnknown(m) -} - -var xxx_messageInfo_EntityData proto.InternalMessageInfo - -func (m *EntityData) GetIdentity() string { - if m != nil { - return m.Identity - } - return "" -} - -func (m *EntityData) GetLegalName() string { - if m != nil { - return m.LegalName - } - return "" -} - -func (m *EntityData) GetAddresses() []*entity.Address { - if m != nil { - return m.Addresses - } - return nil -} - -func (m *EntityData) GetPaymentDetails() []*entity.PaymentDetail { - if m != nil { - return m.PaymentDetails - } - return nil -} - -func (m *EntityData) GetContacts() []*entity.Contact { - if m != nil { - return m.Contacts - } - return nil -} - -// Entity Relationships -type EntityDataResponse struct { - Entity *EntityData `protobuf:"bytes,1,opt,name=entity,proto3" json:"entity,omitempty"` - Relationships []*Relationship `protobuf:"bytes,2,rep,name=relationships,proto3" json:"relationships,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *EntityDataResponse) Reset() { *m = EntityDataResponse{} } -func (m *EntityDataResponse) String() string { return proto.CompactTextString(m) } -func (*EntityDataResponse) ProtoMessage() {} -func (*EntityDataResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b437217b9e14a2, []int{8} -} - -func (m *EntityDataResponse) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_EntityDataResponse.Unmarshal(m, b) -} -func (m *EntityDataResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_EntityDataResponse.Marshal(b, m, deterministic) -} -func (m *EntityDataResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_EntityDataResponse.Merge(m, src) -} -func (m *EntityDataResponse) XXX_Size() int { - return xxx_messageInfo_EntityDataResponse.Size(m) -} -func (m *EntityDataResponse) XXX_DiscardUnknown() { - xxx_messageInfo_EntityDataResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_EntityDataResponse proto.InternalMessageInfo - -func (m *EntityDataResponse) GetEntity() *EntityData { - if m != nil { - return m.Entity - } - return nil -} - -func (m *EntityDataResponse) GetRelationships() []*Relationship { - if m != nil { - return m.Relationships - } - return nil -} - -type RelationshipPayload struct { - // entity identifier - DocumentId string `protobuf:"bytes,1,opt,name=document_id,json=documentId,proto3" json:"document_id,omitempty"` - TargetIdentity string `protobuf:"bytes,2,opt,name=target_identity,json=targetIdentity,proto3" json:"target_identity,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *RelationshipPayload) Reset() { *m = RelationshipPayload{} } -func (m *RelationshipPayload) String() string { return proto.CompactTextString(m) } -func (*RelationshipPayload) ProtoMessage() {} -func (*RelationshipPayload) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b437217b9e14a2, []int{9} -} - -func (m *RelationshipPayload) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_RelationshipPayload.Unmarshal(m, b) -} -func (m *RelationshipPayload) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_RelationshipPayload.Marshal(b, m, deterministic) -} -func (m *RelationshipPayload) XXX_Merge(src proto.Message) { - xxx_messageInfo_RelationshipPayload.Merge(m, src) -} -func (m *RelationshipPayload) XXX_Size() int { - return xxx_messageInfo_RelationshipPayload.Size(m) -} -func (m *RelationshipPayload) XXX_DiscardUnknown() { - xxx_messageInfo_RelationshipPayload.DiscardUnknown(m) -} - -var xxx_messageInfo_RelationshipPayload proto.InternalMessageInfo - -func (m *RelationshipPayload) GetDocumentId() string { - if m != nil { - return m.DocumentId - } - return "" -} - -func (m *RelationshipPayload) GetTargetIdentity() string { - if m != nil { - return m.TargetIdentity - } - return "" -} - -type RelationshipData struct { - // DID of relationship owner - OwnerIdentity string `protobuf:"bytes,1,opt,name=owner_identity,json=ownerIdentity,proto3" json:"owner_identity,omitempty"` - // DID of target identity - TargetIdentity string `protobuf:"bytes,2,opt,name=target_identity,json=targetIdentity,proto3" json:"target_identity,omitempty"` - // identifier of Entity whose data can be accessed via this relationship - EntityIdentifier string `protobuf:"bytes,3,opt,name=entity_identifier,json=entityIdentifier,proto3" json:"entity_identifier,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *RelationshipData) Reset() { *m = RelationshipData{} } -func (m *RelationshipData) String() string { return proto.CompactTextString(m) } -func (*RelationshipData) ProtoMessage() {} -func (*RelationshipData) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b437217b9e14a2, []int{10} -} - -func (m *RelationshipData) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_RelationshipData.Unmarshal(m, b) -} -func (m *RelationshipData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_RelationshipData.Marshal(b, m, deterministic) -} -func (m *RelationshipData) XXX_Merge(src proto.Message) { - xxx_messageInfo_RelationshipData.Merge(m, src) -} -func (m *RelationshipData) XXX_Size() int { - return xxx_messageInfo_RelationshipData.Size(m) -} -func (m *RelationshipData) XXX_DiscardUnknown() { - xxx_messageInfo_RelationshipData.DiscardUnknown(m) -} - -var xxx_messageInfo_RelationshipData proto.InternalMessageInfo - -func (m *RelationshipData) GetOwnerIdentity() string { - if m != nil { - return m.OwnerIdentity - } - return "" -} - -func (m *RelationshipData) GetTargetIdentity() string { - if m != nil { - return m.TargetIdentity - } - return "" -} - -func (m *RelationshipData) GetEntityIdentifier() string { - if m != nil { - return m.EntityIdentifier - } - return "" -} - -type RelationshipResponse struct { - Header *document.ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"` - Relationship []*RelationshipData `protobuf:"bytes,2,rep,name=relationship,proto3" json:"relationship,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *RelationshipResponse) Reset() { *m = RelationshipResponse{} } -func (m *RelationshipResponse) String() string { return proto.CompactTextString(m) } -func (*RelationshipResponse) ProtoMessage() {} -func (*RelationshipResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_c1b437217b9e14a2, []int{11} -} - -func (m *RelationshipResponse) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_RelationshipResponse.Unmarshal(m, b) -} -func (m *RelationshipResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_RelationshipResponse.Marshal(b, m, deterministic) -} -func (m *RelationshipResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_RelationshipResponse.Merge(m, src) -} -func (m *RelationshipResponse) XXX_Size() int { - return xxx_messageInfo_RelationshipResponse.Size(m) -} -func (m *RelationshipResponse) XXX_DiscardUnknown() { - xxx_messageInfo_RelationshipResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_RelationshipResponse proto.InternalMessageInfo - -func (m *RelationshipResponse) GetHeader() *document.ResponseHeader { - if m != nil { - return m.Header - } - return nil -} - -func (m *RelationshipResponse) GetRelationship() []*RelationshipData { - if m != nil { - return m.Relationship - } - return nil -} - -func init() { - proto.RegisterType((*GetRequest)(nil), "entity.GetRequest") - proto.RegisterType((*GetRequestRelationship)(nil), "entity.GetRequestRelationship") - proto.RegisterType((*GetVersionRequest)(nil), "entity.GetVersionRequest") - proto.RegisterType((*EntityCreatePayload)(nil), "entity.EntityCreatePayload") - proto.RegisterMapType((map[string]*document.Attribute)(nil), "entity.EntityCreatePayload.AttributesEntry") - proto.RegisterType((*EntityUpdatePayload)(nil), "entity.EntityUpdatePayload") - proto.RegisterMapType((map[string]*document.Attribute)(nil), "entity.EntityUpdatePayload.AttributesEntry") - proto.RegisterType((*EntityResponse)(nil), "entity.EntityResponse") - proto.RegisterMapType((map[string]*document.Attribute)(nil), "entity.EntityResponse.AttributesEntry") - proto.RegisterType((*Relationship)(nil), "entity.Relationship") - proto.RegisterType((*EntityData)(nil), "entity.EntityData") - proto.RegisterType((*EntityDataResponse)(nil), "entity.EntityDataResponse") - proto.RegisterType((*RelationshipPayload)(nil), "entity.RelationshipPayload") - proto.RegisterType((*RelationshipData)(nil), "entity.RelationshipData") - proto.RegisterType((*RelationshipResponse)(nil), "entity.RelationshipResponse") -} - -func init() { proto.RegisterFile("entity/service.proto", fileDescriptor_c1b437217b9e14a2) } - -var fileDescriptor_c1b437217b9e14a2 = []byte{ - // 1029 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x56, 0xcd, 0x6e, 0x23, 0x45, - 0x10, 0xd6, 0xd8, 0x1b, 0x93, 0x94, 0xf3, 0xdb, 0x09, 0xc1, 0x1a, 0xc2, 0x66, 0x32, 0x52, 0x7e, - 0x48, 0xd6, 0x36, 0x98, 0x0b, 0x0a, 0x3f, 0x92, 0xb3, 0xbb, 0x84, 0x08, 0x81, 0xac, 0x59, 0x2d, - 0x48, 0x5c, 0xac, 0xce, 0x4c, 0xad, 0x3d, 0x5a, 0x7b, 0x66, 0xe8, 0x6e, 0x3b, 0x32, 0xcb, 0x5e, - 0x38, 0x70, 0x04, 0xc9, 0x88, 0x03, 0x57, 0x8e, 0xbc, 0x06, 0x57, 0x6e, 0x3c, 0x02, 0x3c, 0x08, - 0x9a, 0xee, 0x1e, 0xcf, 0x8c, 0x63, 0x9b, 0x05, 0xa4, 0x3d, 0xd9, 0x5d, 0xf5, 0x55, 0x7d, 0x35, - 0x5f, 0x75, 0x75, 0x37, 0xec, 0x60, 0x20, 0x7c, 0x31, 0xaa, 0x73, 0x64, 0x43, 0xdf, 0xc5, 0x5a, - 0xc4, 0x42, 0x11, 0x92, 0x92, 0xb2, 0x9a, 0xbb, 0x5e, 0xe8, 0x0e, 0xfa, 0x18, 0x88, 0xbc, 0xdf, - 0xdc, 0xd6, 0x51, 0xea, 0x47, 0x1b, 0xf7, 0x3a, 0x61, 0xd8, 0xe9, 0x61, 0x9d, 0x46, 0x7e, 0x9d, - 0x06, 0x41, 0x28, 0xa8, 0xf0, 0xc3, 0x80, 0x6b, 0xef, 0x3d, 0xf9, 0xe3, 0x56, 0x3b, 0x18, 0x54, - 0xf9, 0x0d, 0xed, 0x74, 0x90, 0xd5, 0xc3, 0x48, 0x22, 0x6e, 0xa3, 0xed, 0x2a, 0xc0, 0x25, 0x0a, - 0x07, 0xbf, 0x1a, 0x20, 0x17, 0x64, 0x1f, 0xca, 0x49, 0x21, 0x6d, 0xdf, 0xab, 0x18, 0x96, 0x71, - 0xb2, 0xe2, 0x40, 0x62, 0xba, 0xf2, 0xec, 0x26, 0xec, 0xa6, 0x70, 0x07, 0x7b, 0x2a, 0x57, 0xd7, - 0x8f, 0xc8, 0x31, 0x6c, 0xb0, 0xcc, 0x3a, 0x0d, 0x5f, 0xcf, 0x9a, 0xaf, 0x3c, 0xfb, 0x53, 0xd8, - 0xba, 0x44, 0xf1, 0x39, 0x32, 0xee, 0x87, 0x41, 0x42, 0x7c, 0x17, 0xc0, 0xf7, 0xe2, 0x8f, 0x7c, - 0xe2, 0x23, 0x4b, 0x78, 0x53, 0x0b, 0xa9, 0xc0, 0x2b, 0x43, 0x15, 0x51, 0x29, 0x48, 0x67, 0xb2, - 0xb4, 0x7f, 0x29, 0xc0, 0xf6, 0x43, 0xa9, 0xce, 0x7d, 0x86, 0x54, 0x60, 0x8b, 0x8e, 0x7a, 0x21, - 0xf5, 0xe2, 0x4f, 0x61, 0x48, 0xbd, 0x36, 0x75, 0x5d, 0xe4, 0xbc, 0x62, 0x58, 0xc5, 0x38, 0x65, - 0x6c, 0x6a, 0x4a, 0x0b, 0x39, 0x80, 0xd5, 0x1b, 0xe6, 0x0b, 0x4c, 0x10, 0x05, 0x89, 0x28, 0x4b, - 0x9b, 0x86, 0x1c, 0xc1, 0x1d, 0x8f, 0x0a, 0x5a, 0x29, 0x5a, 0xc6, 0x49, 0xb9, 0x41, 0x6a, 0xba, - 0x0b, 0x8a, 0xee, 0x01, 0x15, 0xd4, 0x91, 0x7e, 0xf2, 0x09, 0x00, 0x15, 0x82, 0xf9, 0xd7, 0x03, - 0x81, 0xbc, 0x72, 0xc7, 0x2a, 0x9e, 0x94, 0x1b, 0x67, 0x79, 0x74, 0xae, 0xb8, 0x5a, 0x73, 0x82, - 0x7e, 0x18, 0x08, 0x36, 0x72, 0x32, 0xe1, 0xa6, 0x03, 0x1b, 0x53, 0x6e, 0xb2, 0x09, 0xc5, 0xa7, - 0x38, 0xd2, 0xb2, 0xc4, 0x7f, 0xc9, 0x9b, 0xb0, 0x34, 0xa4, 0xbd, 0x01, 0x4a, 0x35, 0xca, 0x8d, - 0xed, 0x5a, 0xd2, 0xa3, 0x34, 0xb5, 0xa3, 0x10, 0xe7, 0x85, 0x77, 0x0d, 0xfb, 0xb7, 0x89, 0x48, - 0x8f, 0x23, 0x2f, 0x2f, 0xd2, 0xc2, 0x7e, 0x4f, 0xab, 0x58, 0xf8, 0x47, 0x15, 0x8b, 0xf3, 0x55, - 0xbc, 0xf3, 0xaf, 0x54, 0x5c, 0x9a, 0xa5, 0x62, 0xae, 0xfa, 0x97, 0xae, 0xe2, 0xf7, 0x05, 0x58, - 0x57, 0x75, 0x38, 0xc8, 0xa3, 0x30, 0xe0, 0x48, 0xde, 0x82, 0x52, 0x17, 0xa9, 0xa7, 0xf7, 0x6c, - 0xb9, 0x51, 0x49, 0x53, 0x24, 0x98, 0x8f, 0xa5, 0xdf, 0xd1, 0x38, 0x52, 0xd3, 0x6a, 0x28, 0x4a, - 0x73, 0x86, 0x1a, 0x3a, 0x4e, 0xab, 0xf2, 0x51, 0x4e, 0x95, 0xa2, 0x54, 0xe5, 0x28, 0x1f, 0x95, - 0x44, 0xbc, 0x74, 0x41, 0x2e, 0x60, 0x35, 0x77, 0x06, 0x98, 0xb0, 0xac, 0x66, 0x56, 0x24, 0x59, - 0x27, 0x6b, 0xb2, 0x0b, 0x25, 0xea, 0x0a, 0x7f, 0xa8, 0x72, 0x2f, 0x3b, 0x7a, 0x65, 0xff, 0x69, - 0x00, 0xa4, 0x1f, 0xbf, 0x30, 0xc5, 0x1b, 0x00, 0x3d, 0xec, 0xd0, 0x5e, 0x3b, 0xa0, 0x7d, 0xd4, - 0xe7, 0xc0, 0x8a, 0xb4, 0x7c, 0x46, 0xfb, 0x48, 0xaa, 0xb0, 0x42, 0x3d, 0x8f, 0x21, 0xe7, 0x13, - 0xa1, 0x36, 0x12, 0xa1, 0x9a, 0xca, 0xe1, 0xa4, 0x08, 0xf2, 0x21, 0x6c, 0x44, 0x74, 0x24, 0xb7, - 0xbe, 0x87, 0x82, 0xfa, 0xbd, 0x64, 0x72, 0x5f, 0x4d, 0x82, 0x5a, 0xca, 0xfd, 0x40, 0x7a, 0x9d, - 0xf5, 0x28, 0xbb, 0xe4, 0xe4, 0x0c, 0x96, 0xdd, 0x30, 0x10, 0xd4, 0x15, 0xc9, 0x66, 0x9d, 0xb0, - 0xdd, 0x57, 0x76, 0x67, 0x02, 0xb0, 0xbf, 0x01, 0x72, 0xbb, 0xc3, 0xe4, 0x14, 0x4a, 0x99, 0x4f, - 0x9d, 0x3d, 0x1b, 0x1a, 0x41, 0xce, 0x61, 0x2d, 0x7b, 0x90, 0xaa, 0x59, 0x2c, 0x37, 0x76, 0x92, - 0x90, 0x6c, 0x23, 0x9c, 0x3c, 0xd4, 0x6e, 0xc3, 0x76, 0xd6, 0xfd, 0xc2, 0xd3, 0x7f, 0x0c, 0x1b, - 0x82, 0xb2, 0x0e, 0xc6, 0x6e, 0x5d, 0xa8, 0x52, 0x7d, 0x5d, 0x99, 0xaf, 0xb4, 0xd5, 0xfe, 0xc1, - 0x80, 0xcd, 0x2c, 0x83, 0x6c, 0xe5, 0x21, 0xac, 0x87, 0x37, 0x01, 0xb2, 0xf6, 0x54, 0x43, 0xd7, - 0xa4, 0x35, 0x89, 0x7d, 0x61, 0x12, 0x72, 0x06, 0x5b, 0xea, 0x5f, 0x3b, 0x73, 0x55, 0x14, 0x25, - 0x74, 0x53, 0x39, 0xae, 0x26, 0x76, 0xfb, 0x3b, 0x03, 0x76, 0x72, 0x92, 0xfc, 0xf7, 0x89, 0x7d, - 0x1f, 0x56, 0xb3, 0x72, 0x6a, 0xe1, 0x2b, 0xb3, 0x84, 0x97, 0x1d, 0xcb, 0xa1, 0x1b, 0xbf, 0x97, - 0x60, 0x4d, 0xb5, 0xf3, 0x91, 0xba, 0xd9, 0x49, 0x0f, 0x4a, 0xea, 0x36, 0x20, 0xaf, 0x2f, 0xb8, - 0x23, 0xcc, 0xdd, 0xd9, 0x43, 0x6e, 0x57, 0xc7, 0xcd, 0x6d, 0x73, 0x4b, 0x61, 0xb9, 0x45, 0x03, - 0x4b, 0xc1, 0xbe, 0xfd, 0xe3, 0xaf, 0x1f, 0x0b, 0x5b, 0xf6, 0x6a, 0x7d, 0xf8, 0xb6, 0x7a, 0x2d, - 0xf8, 0xc8, 0xcf, 0x8d, 0x53, 0xf2, 0x35, 0x94, 0xd4, 0xa9, 0x39, 0xcd, 0x96, 0x3b, 0x4b, 0xe7, - 0xb2, 0xbd, 0x27, 0xd9, 0x14, 0x76, 0x9a, 0x6d, 0xdf, 0x34, 0xb3, 0x6c, 0xf5, 0x67, 0x99, 0xdd, - 0xf4, 0x3c, 0xe6, 0x8e, 0xa0, 0x78, 0x89, 0x82, 0x4c, 0xb6, 0x75, 0xfa, 0x74, 0x98, 0xcb, 0xf7, - 0xc1, 0xb8, 0x59, 0x31, 0xe3, 0x37, 0x86, 0x25, 0xba, 0x68, 0xb9, 0x03, 0xc6, 0x30, 0x10, 0x59, - 0xd2, 0x3d, 0xb2, 0x80, 0x94, 0xfc, 0x6a, 0xc0, 0x6b, 0x97, 0x28, 0x54, 0xd2, 0x8b, 0x51, 0xee, - 0x74, 0xba, 0x7b, 0xbb, 0x8c, 0xac, 0x7f, 0x6e, 0x49, 0x5f, 0x8c, 0x9b, 0xb6, 0x69, 0xc5, 0x25, - 0x29, 0xbf, 0xf5, 0x84, 0x85, 0x7d, 0xeb, 0x7a, 0xc0, 0xfd, 0x00, 0x39, 0xb7, 0x22, 0xca, 0x44, - 0x80, 0x4c, 0x16, 0x77, 0x8f, 0x9c, 0xc6, 0xc5, 0xe5, 0xa6, 0xaf, 0xfe, 0x6c, 0xea, 0x5d, 0xf4, - 0x5c, 0xbf, 0xe6, 0xc8, 0xcf, 0x06, 0x2c, 0x3d, 0xea, 0x52, 0x96, 0x69, 0xcd, 0x8c, 0x31, 0x35, - 0xf7, 0x66, 0x8e, 0x78, 0x52, 0xdd, 0xe3, 0x71, 0xf3, 0xd8, 0x3c, 0x94, 0x69, 0xa4, 0x64, 0xba, - 0xc6, 0x44, 0x15, 0xeb, 0xc6, 0x17, 0x5d, 0x2b, 0x14, 0x5d, 0x64, 0x5c, 0x96, 0x78, 0x68, 0x5b, - 0xf3, 0xf5, 0xab, 0xf3, 0x38, 0x4f, 0xdc, 0xba, 0x9f, 0x0c, 0x28, 0x39, 0x38, 0x0c, 0x9f, 0xfe, - 0xaf, 0xe2, 0x5a, 0xe3, 0xe6, 0x81, 0xb9, 0xcf, 0x64, 0x9e, 0x74, 0xf3, 0xa4, 0xc5, 0x49, 0x3a, - 0x59, 0xd6, 0x91, 0x7d, 0xb0, 0xa0, 0x2c, 0x95, 0xe1, 0xdc, 0x38, 0xbd, 0x38, 0x02, 0x70, 0xc3, - 0xbe, 0x26, 0xbd, 0x58, 0xd5, 0x33, 0xd5, 0x8a, 0xdf, 0xb2, 0x2d, 0xe3, 0xcb, 0x65, 0x65, 0x8f, - 0xae, 0xaf, 0x4b, 0xf2, 0x79, 0xfb, 0xce, 0xdf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x3c, 0x43, 0x3f, - 0xe7, 0x77, 0x0b, 0x00, 0x00, -} - -// Reference imports to suppress errors if they are not otherwise used. -var _ context.Context -var _ grpc.ClientConn - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion4 - -// EntityServiceClient is the client API for EntityService service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. -type EntityServiceClient interface { - Create(ctx context.Context, in *EntityCreatePayload, opts ...grpc.CallOption) (*EntityResponse, error) - Update(ctx context.Context, in *EntityUpdatePayload, opts ...grpc.CallOption) (*EntityResponse, error) - Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*EntityResponse, error) - // Entity Relation Get - GetEntityByRelationship(ctx context.Context, in *GetRequestRelationship, opts ...grpc.CallOption) (*EntityResponse, error) - // Entity Relation Share - Share(ctx context.Context, in *RelationshipPayload, opts ...grpc.CallOption) (*RelationshipResponse, error) - // Entity Relation Revoke - Revoke(ctx context.Context, in *RelationshipPayload, opts ...grpc.CallOption) (*RelationshipResponse, error) -} - -type entityServiceClient struct { - cc *grpc.ClientConn -} - -func NewEntityServiceClient(cc *grpc.ClientConn) EntityServiceClient { - return &entityServiceClient{cc} -} - -func (c *entityServiceClient) Create(ctx context.Context, in *EntityCreatePayload, opts ...grpc.CallOption) (*EntityResponse, error) { - out := new(EntityResponse) - err := c.cc.Invoke(ctx, "/entity.EntityService/Create", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *entityServiceClient) Update(ctx context.Context, in *EntityUpdatePayload, opts ...grpc.CallOption) (*EntityResponse, error) { - out := new(EntityResponse) - err := c.cc.Invoke(ctx, "/entity.EntityService/Update", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *entityServiceClient) Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*EntityResponse, error) { - out := new(EntityResponse) - err := c.cc.Invoke(ctx, "/entity.EntityService/Get", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *entityServiceClient) GetEntityByRelationship(ctx context.Context, in *GetRequestRelationship, opts ...grpc.CallOption) (*EntityResponse, error) { - out := new(EntityResponse) - err := c.cc.Invoke(ctx, "/entity.EntityService/GetEntityByRelationship", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *entityServiceClient) Share(ctx context.Context, in *RelationshipPayload, opts ...grpc.CallOption) (*RelationshipResponse, error) { - out := new(RelationshipResponse) - err := c.cc.Invoke(ctx, "/entity.EntityService/Share", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *entityServiceClient) Revoke(ctx context.Context, in *RelationshipPayload, opts ...grpc.CallOption) (*RelationshipResponse, error) { - out := new(RelationshipResponse) - err := c.cc.Invoke(ctx, "/entity.EntityService/Revoke", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// EntityServiceServer is the server API for EntityService service. -type EntityServiceServer interface { - Create(context.Context, *EntityCreatePayload) (*EntityResponse, error) - Update(context.Context, *EntityUpdatePayload) (*EntityResponse, error) - Get(context.Context, *GetRequest) (*EntityResponse, error) - // Entity Relation Get - GetEntityByRelationship(context.Context, *GetRequestRelationship) (*EntityResponse, error) - // Entity Relation Share - Share(context.Context, *RelationshipPayload) (*RelationshipResponse, error) - // Entity Relation Revoke - Revoke(context.Context, *RelationshipPayload) (*RelationshipResponse, error) -} - -func RegisterEntityServiceServer(s *grpc.Server, srv EntityServiceServer) { - s.RegisterService(&_EntityService_serviceDesc, srv) -} - -func _EntityService_Create_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(EntityCreatePayload) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(EntityServiceServer).Create(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/entity.EntityService/Create", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(EntityServiceServer).Create(ctx, req.(*EntityCreatePayload)) - } - return interceptor(ctx, in, info, handler) -} - -func _EntityService_Update_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(EntityUpdatePayload) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(EntityServiceServer).Update(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/entity.EntityService/Update", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(EntityServiceServer).Update(ctx, req.(*EntityUpdatePayload)) - } - return interceptor(ctx, in, info, handler) -} - -func _EntityService_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(EntityServiceServer).Get(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/entity.EntityService/Get", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(EntityServiceServer).Get(ctx, req.(*GetRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _EntityService_GetEntityByRelationship_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetRequestRelationship) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(EntityServiceServer).GetEntityByRelationship(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/entity.EntityService/GetEntityByRelationship", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(EntityServiceServer).GetEntityByRelationship(ctx, req.(*GetRequestRelationship)) - } - return interceptor(ctx, in, info, handler) -} - -func _EntityService_Share_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(RelationshipPayload) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(EntityServiceServer).Share(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/entity.EntityService/Share", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(EntityServiceServer).Share(ctx, req.(*RelationshipPayload)) - } - return interceptor(ctx, in, info, handler) -} - -func _EntityService_Revoke_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(RelationshipPayload) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(EntityServiceServer).Revoke(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/entity.EntityService/Revoke", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(EntityServiceServer).Revoke(ctx, req.(*RelationshipPayload)) - } - return interceptor(ctx, in, info, handler) -} - -var _EntityService_serviceDesc = grpc.ServiceDesc{ - ServiceName: "entity.EntityService", - HandlerType: (*EntityServiceServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "Create", - Handler: _EntityService_Create_Handler, - }, - { - MethodName: "Update", - Handler: _EntityService_Update_Handler, - }, - { - MethodName: "Get", - Handler: _EntityService_Get_Handler, - }, - { - MethodName: "GetEntityByRelationship", - Handler: _EntityService_GetEntityByRelationship_Handler, - }, - { - MethodName: "Share", - Handler: _EntityService_Share_Handler, - }, - { - MethodName: "Revoke", - Handler: _EntityService_Revoke_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "entity/service.proto", -} diff --git a/protobufs/gen/go/entity/service.pb.gw.go b/protobufs/gen/go/entity/service.pb.gw.go deleted file mode 100644 index 2ed71254e..000000000 --- a/protobufs/gen/go/entity/service.pb.gw.go +++ /dev/null @@ -1,432 +0,0 @@ -// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. -// source: entity/service.proto - -/* -Package entitypb is a reverse proxy. - -It translates gRPC into RESTful JSON APIs. -*/ -package entitypb - -import ( - "io" - "net/http" - - "github.com/golang/protobuf/proto" - "github.com/grpc-ecosystem/grpc-gateway/runtime" - "github.com/grpc-ecosystem/grpc-gateway/utilities" - "golang.org/x/net/context" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/grpclog" - "google.golang.org/grpc/status" -) - -var _ codes.Code -var _ io.Reader -var _ status.Status -var _ = runtime.String -var _ = utilities.NewDoubleArray - -func request_EntityService_Create_0(ctx context.Context, marshaler runtime.Marshaler, client EntityServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq EntityCreatePayload - var metadata runtime.ServerMetadata - - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - msg, err := client.Create(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func request_EntityService_Update_0(ctx context.Context, marshaler runtime.Marshaler, client EntityServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq EntityUpdatePayload - var metadata runtime.ServerMetadata - - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["document_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "document_id") - } - - protoReq.DocumentId, err = runtime.String(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "document_id", err) - } - - msg, err := client.Update(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func request_EntityService_Get_0(ctx context.Context, marshaler runtime.Marshaler, client EntityServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq GetRequest - var metadata runtime.ServerMetadata - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["document_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "document_id") - } - - protoReq.DocumentId, err = runtime.String(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "document_id", err) - } - - msg, err := client.Get(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func request_EntityService_GetEntityByRelationship_0(ctx context.Context, marshaler runtime.Marshaler, client EntityServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq GetRequestRelationship - var metadata runtime.ServerMetadata - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["relationship_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "relationship_id") - } - - protoReq.RelationshipId, err = runtime.String(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "relationship_id", err) - } - - msg, err := client.GetEntityByRelationship(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func request_EntityService_Share_0(ctx context.Context, marshaler runtime.Marshaler, client EntityServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq RelationshipPayload - var metadata runtime.ServerMetadata - - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["document_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "document_id") - } - - protoReq.DocumentId, err = runtime.String(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "document_id", err) - } - - msg, err := client.Share(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func request_EntityService_Revoke_0(ctx context.Context, marshaler runtime.Marshaler, client EntityServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq RelationshipPayload - var metadata runtime.ServerMetadata - - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["document_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "document_id") - } - - protoReq.DocumentId, err = runtime.String(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "document_id", err) - } - - msg, err := client.Revoke(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -// RegisterEntityServiceHandlerFromEndpoint is same as RegisterEntityServiceHandler but -// automatically dials to "endpoint" and closes the connection when "ctx" gets done. -func RegisterEntityServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { - conn, err := grpc.Dial(endpoint, opts...) - if err != nil { - return err - } - defer func() { - if err != nil { - if cerr := conn.Close(); cerr != nil { - grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) - } - return - } - go func() { - <-ctx.Done() - if cerr := conn.Close(); cerr != nil { - grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) - } - }() - }() - - return RegisterEntityServiceHandler(ctx, mux, conn) -} - -// RegisterEntityServiceHandler registers the http handlers for service EntityService to "mux". -// The handlers forward requests to the grpc endpoint over "conn". -func RegisterEntityServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { - return RegisterEntityServiceHandlerClient(ctx, mux, NewEntityServiceClient(conn)) -} - -// RegisterEntityServiceHandlerClient registers the http handlers for service EntityService -// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "EntityServiceClient". -// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "EntityServiceClient" -// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in -// "EntityServiceClient" to call the correct interceptors. -func RegisterEntityServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client EntityServiceClient) error { - - mux.Handle("POST", pattern_EntityService_Create_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - if cn, ok := w.(http.CloseNotifier); ok { - go func(done <-chan struct{}, closed <-chan bool) { - select { - case <-done: - case <-closed: - cancel() - } - }(ctx.Done(), cn.CloseNotify()) - } - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_EntityService_Create_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_EntityService_Create_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("PUT", pattern_EntityService_Update_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - if cn, ok := w.(http.CloseNotifier); ok { - go func(done <-chan struct{}, closed <-chan bool) { - select { - case <-done: - case <-closed: - cancel() - } - }(ctx.Done(), cn.CloseNotify()) - } - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_EntityService_Update_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_EntityService_Update_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("GET", pattern_EntityService_Get_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - if cn, ok := w.(http.CloseNotifier); ok { - go func(done <-chan struct{}, closed <-chan bool) { - select { - case <-done: - case <-closed: - cancel() - } - }(ctx.Done(), cn.CloseNotify()) - } - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_EntityService_Get_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_EntityService_Get_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("GET", pattern_EntityService_GetEntityByRelationship_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - if cn, ok := w.(http.CloseNotifier); ok { - go func(done <-chan struct{}, closed <-chan bool) { - select { - case <-done: - case <-closed: - cancel() - } - }(ctx.Done(), cn.CloseNotify()) - } - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_EntityService_GetEntityByRelationship_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_EntityService_GetEntityByRelationship_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("POST", pattern_EntityService_Share_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - if cn, ok := w.(http.CloseNotifier); ok { - go func(done <-chan struct{}, closed <-chan bool) { - select { - case <-done: - case <-closed: - cancel() - } - }(ctx.Done(), cn.CloseNotify()) - } - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_EntityService_Share_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_EntityService_Share_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("POST", pattern_EntityService_Revoke_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - if cn, ok := w.(http.CloseNotifier); ok { - go func(done <-chan struct{}, closed <-chan bool) { - select { - case <-done: - case <-closed: - cancel() - } - }(ctx.Done(), cn.CloseNotify()) - } - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_EntityService_Revoke_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_EntityService_Revoke_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - return nil -} - -var ( - pattern_EntityService_Create_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "entities"}, "")) - - pattern_EntityService_Update_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "entities", "document_id"}, "")) - - pattern_EntityService_Get_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "entities", "document_id"}, "")) - - pattern_EntityService_GetEntityByRelationship_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3}, []string{"v1", "relationships", "relationship_id", "entity"}, "")) - - pattern_EntityService_Share_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3}, []string{"v1", "entities", "document_id", "share"}, "")) - - pattern_EntityService_Revoke_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3}, []string{"v1", "entities", "document_id", "revoke"}, "")) -) - -var ( - forward_EntityService_Create_0 = runtime.ForwardResponseMessage - - forward_EntityService_Update_0 = runtime.ForwardResponseMessage - - forward_EntityService_Get_0 = runtime.ForwardResponseMessage - - forward_EntityService_GetEntityByRelationship_0 = runtime.ForwardResponseMessage - - forward_EntityService_Share_0 = runtime.ForwardResponseMessage - - forward_EntityService_Revoke_0 = runtime.ForwardResponseMessage -) diff --git a/protobufs/gen/go/funding/funding.pb.go b/protobufs/gen/go/funding/funding.pb.go deleted file mode 100644 index a99fcf868..000000000 --- a/protobufs/gen/go/funding/funding.pb.go +++ /dev/null @@ -1,1009 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// source: funding/funding.proto - -package funpb - -import ( - context "context" - fmt "fmt" - math "math" - - document "github.com/centrifuge/go-centrifuge/protobufs/gen/go/document" - proto "github.com/golang/protobuf/proto" - _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/options" - _ "google.golang.org/genproto/googleapis/api/annotations" - grpc "google.golang.org/grpc" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package - -type FundingCreatePayload struct { - DocumentId string `protobuf:"bytes,1,opt,name=document_id,json=documentId,proto3" json:"document_id,omitempty"` - Data *FundingData `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *FundingCreatePayload) Reset() { *m = FundingCreatePayload{} } -func (m *FundingCreatePayload) String() string { return proto.CompactTextString(m) } -func (*FundingCreatePayload) ProtoMessage() {} -func (*FundingCreatePayload) Descriptor() ([]byte, []int) { - return fileDescriptor_e74e73a4c22d632c, []int{0} -} - -func (m *FundingCreatePayload) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_FundingCreatePayload.Unmarshal(m, b) -} -func (m *FundingCreatePayload) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_FundingCreatePayload.Marshal(b, m, deterministic) -} -func (m *FundingCreatePayload) XXX_Merge(src proto.Message) { - xxx_messageInfo_FundingCreatePayload.Merge(m, src) -} -func (m *FundingCreatePayload) XXX_Size() int { - return xxx_messageInfo_FundingCreatePayload.Size(m) -} -func (m *FundingCreatePayload) XXX_DiscardUnknown() { - xxx_messageInfo_FundingCreatePayload.DiscardUnknown(m) -} - -var xxx_messageInfo_FundingCreatePayload proto.InternalMessageInfo - -func (m *FundingCreatePayload) GetDocumentId() string { - if m != nil { - return m.DocumentId - } - return "" -} - -func (m *FundingCreatePayload) GetData() *FundingData { - if m != nil { - return m.Data - } - return nil -} - -type FundingUpdatePayload struct { - DocumentId string `protobuf:"bytes,1,opt,name=document_id,json=documentId,proto3" json:"document_id,omitempty"` - AgreementId string `protobuf:"bytes,2,opt,name=agreement_id,json=agreementId,proto3" json:"agreement_id,omitempty"` - Data *FundingData `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *FundingUpdatePayload) Reset() { *m = FundingUpdatePayload{} } -func (m *FundingUpdatePayload) String() string { return proto.CompactTextString(m) } -func (*FundingUpdatePayload) ProtoMessage() {} -func (*FundingUpdatePayload) Descriptor() ([]byte, []int) { - return fileDescriptor_e74e73a4c22d632c, []int{1} -} - -func (m *FundingUpdatePayload) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_FundingUpdatePayload.Unmarshal(m, b) -} -func (m *FundingUpdatePayload) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_FundingUpdatePayload.Marshal(b, m, deterministic) -} -func (m *FundingUpdatePayload) XXX_Merge(src proto.Message) { - xxx_messageInfo_FundingUpdatePayload.Merge(m, src) -} -func (m *FundingUpdatePayload) XXX_Size() int { - return xxx_messageInfo_FundingUpdatePayload.Size(m) -} -func (m *FundingUpdatePayload) XXX_DiscardUnknown() { - xxx_messageInfo_FundingUpdatePayload.DiscardUnknown(m) -} - -var xxx_messageInfo_FundingUpdatePayload proto.InternalMessageInfo - -func (m *FundingUpdatePayload) GetDocumentId() string { - if m != nil { - return m.DocumentId - } - return "" -} - -func (m *FundingUpdatePayload) GetAgreementId() string { - if m != nil { - return m.AgreementId - } - return "" -} - -func (m *FundingUpdatePayload) GetData() *FundingData { - if m != nil { - return m.Data - } - return nil -} - -type FundingResponse struct { - Header *document.ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"` - Data *FundingResponseData `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *FundingResponse) Reset() { *m = FundingResponse{} } -func (m *FundingResponse) String() string { return proto.CompactTextString(m) } -func (*FundingResponse) ProtoMessage() {} -func (*FundingResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_e74e73a4c22d632c, []int{2} -} - -func (m *FundingResponse) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_FundingResponse.Unmarshal(m, b) -} -func (m *FundingResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_FundingResponse.Marshal(b, m, deterministic) -} -func (m *FundingResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_FundingResponse.Merge(m, src) -} -func (m *FundingResponse) XXX_Size() int { - return xxx_messageInfo_FundingResponse.Size(m) -} -func (m *FundingResponse) XXX_DiscardUnknown() { - xxx_messageInfo_FundingResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_FundingResponse proto.InternalMessageInfo - -func (m *FundingResponse) GetHeader() *document.ResponseHeader { - if m != nil { - return m.Header - } - return nil -} - -func (m *FundingResponse) GetData() *FundingResponseData { - if m != nil { - return m.Data - } - return nil -} - -type FundingListResponse struct { - Header *document.ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"` - Data []*FundingResponseData `protobuf:"bytes,2,rep,name=data,proto3" json:"data,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *FundingListResponse) Reset() { *m = FundingListResponse{} } -func (m *FundingListResponse) String() string { return proto.CompactTextString(m) } -func (*FundingListResponse) ProtoMessage() {} -func (*FundingListResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_e74e73a4c22d632c, []int{3} -} - -func (m *FundingListResponse) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_FundingListResponse.Unmarshal(m, b) -} -func (m *FundingListResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_FundingListResponse.Marshal(b, m, deterministic) -} -func (m *FundingListResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_FundingListResponse.Merge(m, src) -} -func (m *FundingListResponse) XXX_Size() int { - return xxx_messageInfo_FundingListResponse.Size(m) -} -func (m *FundingListResponse) XXX_DiscardUnknown() { - xxx_messageInfo_FundingListResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_FundingListResponse proto.InternalMessageInfo - -func (m *FundingListResponse) GetHeader() *document.ResponseHeader { - if m != nil { - return m.Header - } - return nil -} - -func (m *FundingListResponse) GetData() []*FundingResponseData { - if m != nil { - return m.Data - } - return nil -} - -type Request struct { - DocumentId string `protobuf:"bytes,1,opt,name=document_id,json=documentId,proto3" json:"document_id,omitempty"` - AgreementId string `protobuf:"bytes,2,opt,name=agreement_id,json=agreementId,proto3" json:"agreement_id,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Request) Reset() { *m = Request{} } -func (m *Request) String() string { return proto.CompactTextString(m) } -func (*Request) ProtoMessage() {} -func (*Request) Descriptor() ([]byte, []int) { - return fileDescriptor_e74e73a4c22d632c, []int{4} -} - -func (m *Request) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Request.Unmarshal(m, b) -} -func (m *Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Request.Marshal(b, m, deterministic) -} -func (m *Request) XXX_Merge(src proto.Message) { - xxx_messageInfo_Request.Merge(m, src) -} -func (m *Request) XXX_Size() int { - return xxx_messageInfo_Request.Size(m) -} -func (m *Request) XXX_DiscardUnknown() { - xxx_messageInfo_Request.DiscardUnknown(m) -} - -var xxx_messageInfo_Request proto.InternalMessageInfo - -func (m *Request) GetDocumentId() string { - if m != nil { - return m.DocumentId - } - return "" -} - -func (m *Request) GetAgreementId() string { - if m != nil { - return m.AgreementId - } - return "" -} - -type GetVersionRequest struct { - DocumentId string `protobuf:"bytes,1,opt,name=document_id,json=documentId,proto3" json:"document_id,omitempty"` - VersionId string `protobuf:"bytes,2,opt,name=version_id,json=versionId,proto3" json:"version_id,omitempty"` - AgreementId string `protobuf:"bytes,3,opt,name=agreement_id,json=agreementId,proto3" json:"agreement_id,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *GetVersionRequest) Reset() { *m = GetVersionRequest{} } -func (m *GetVersionRequest) String() string { return proto.CompactTextString(m) } -func (*GetVersionRequest) ProtoMessage() {} -func (*GetVersionRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_e74e73a4c22d632c, []int{5} -} - -func (m *GetVersionRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_GetVersionRequest.Unmarshal(m, b) -} -func (m *GetVersionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_GetVersionRequest.Marshal(b, m, deterministic) -} -func (m *GetVersionRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetVersionRequest.Merge(m, src) -} -func (m *GetVersionRequest) XXX_Size() int { - return xxx_messageInfo_GetVersionRequest.Size(m) -} -func (m *GetVersionRequest) XXX_DiscardUnknown() { - xxx_messageInfo_GetVersionRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_GetVersionRequest proto.InternalMessageInfo - -func (m *GetVersionRequest) GetDocumentId() string { - if m != nil { - return m.DocumentId - } - return "" -} - -func (m *GetVersionRequest) GetVersionId() string { - if m != nil { - return m.VersionId - } - return "" -} - -func (m *GetVersionRequest) GetAgreementId() string { - if m != nil { - return m.AgreementId - } - return "" -} - -type GetListRequest struct { - DocumentId string `protobuf:"bytes,1,opt,name=document_id,json=documentId,proto3" json:"document_id,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *GetListRequest) Reset() { *m = GetListRequest{} } -func (m *GetListRequest) String() string { return proto.CompactTextString(m) } -func (*GetListRequest) ProtoMessage() {} -func (*GetListRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_e74e73a4c22d632c, []int{6} -} - -func (m *GetListRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_GetListRequest.Unmarshal(m, b) -} -func (m *GetListRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_GetListRequest.Marshal(b, m, deterministic) -} -func (m *GetListRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetListRequest.Merge(m, src) -} -func (m *GetListRequest) XXX_Size() int { - return xxx_messageInfo_GetListRequest.Size(m) -} -func (m *GetListRequest) XXX_DiscardUnknown() { - xxx_messageInfo_GetListRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_GetListRequest proto.InternalMessageInfo - -func (m *GetListRequest) GetDocumentId() string { - if m != nil { - return m.DocumentId - } - return "" -} - -type GetListVersionRequest struct { - DocumentId string `protobuf:"bytes,1,opt,name=document_id,json=documentId,proto3" json:"document_id,omitempty"` - VersionId string `protobuf:"bytes,2,opt,name=version_id,json=versionId,proto3" json:"version_id,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *GetListVersionRequest) Reset() { *m = GetListVersionRequest{} } -func (m *GetListVersionRequest) String() string { return proto.CompactTextString(m) } -func (*GetListVersionRequest) ProtoMessage() {} -func (*GetListVersionRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_e74e73a4c22d632c, []int{7} -} - -func (m *GetListVersionRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_GetListVersionRequest.Unmarshal(m, b) -} -func (m *GetListVersionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_GetListVersionRequest.Marshal(b, m, deterministic) -} -func (m *GetListVersionRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetListVersionRequest.Merge(m, src) -} -func (m *GetListVersionRequest) XXX_Size() int { - return xxx_messageInfo_GetListVersionRequest.Size(m) -} -func (m *GetListVersionRequest) XXX_DiscardUnknown() { - xxx_messageInfo_GetListVersionRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_GetListVersionRequest proto.InternalMessageInfo - -func (m *GetListVersionRequest) GetDocumentId() string { - if m != nil { - return m.DocumentId - } - return "" -} - -func (m *GetListVersionRequest) GetVersionId() string { - if m != nil { - return m.VersionId - } - return "" -} - -// FundingData is the default funding extension schema -type FundingData struct { - AgreementId string `protobuf:"bytes,1,opt,name=agreement_id,json=agreementId,proto3" json:"agreement_id,omitempty"` - Amount string `protobuf:"bytes,2,opt,name=amount,proto3" json:"amount,omitempty"` - Apr string `protobuf:"bytes,3,opt,name=apr,proto3" json:"apr,omitempty"` - Days string `protobuf:"bytes,4,opt,name=days,proto3" json:"days,omitempty"` - Fee string `protobuf:"bytes,5,opt,name=fee,proto3" json:"fee,omitempty"` - RepaymentDueDate string `protobuf:"bytes,7,opt,name=repayment_due_date,json=repaymentDueDate,proto3" json:"repayment_due_date,omitempty"` - RepaymentOccurredDate string `protobuf:"bytes,8,opt,name=repayment_occurred_date,json=repaymentOccurredDate,proto3" json:"repayment_occurred_date,omitempty"` - RepaymentAmount string `protobuf:"bytes,9,opt,name=repayment_amount,json=repaymentAmount,proto3" json:"repayment_amount,omitempty"` - Currency string `protobuf:"bytes,10,opt,name=currency,proto3" json:"currency,omitempty"` - NftAddress string `protobuf:"bytes,11,opt,name=nft_address,json=nftAddress,proto3" json:"nft_address,omitempty"` - PaymentDetailsId string `protobuf:"bytes,12,opt,name=payment_details_id,json=paymentDetailsId,proto3" json:"payment_details_id,omitempty"` - FunderId string `protobuf:"bytes,13,opt,name=funder_id,json=funderId,proto3" json:"funder_id,omitempty"` - BorrowerId string `protobuf:"bytes,14,opt,name=borrower_id,json=borrowerId,proto3" json:"borrower_id,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *FundingData) Reset() { *m = FundingData{} } -func (m *FundingData) String() string { return proto.CompactTextString(m) } -func (*FundingData) ProtoMessage() {} -func (*FundingData) Descriptor() ([]byte, []int) { - return fileDescriptor_e74e73a4c22d632c, []int{8} -} - -func (m *FundingData) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_FundingData.Unmarshal(m, b) -} -func (m *FundingData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_FundingData.Marshal(b, m, deterministic) -} -func (m *FundingData) XXX_Merge(src proto.Message) { - xxx_messageInfo_FundingData.Merge(m, src) -} -func (m *FundingData) XXX_Size() int { - return xxx_messageInfo_FundingData.Size(m) -} -func (m *FundingData) XXX_DiscardUnknown() { - xxx_messageInfo_FundingData.DiscardUnknown(m) -} - -var xxx_messageInfo_FundingData proto.InternalMessageInfo - -func (m *FundingData) GetAgreementId() string { - if m != nil { - return m.AgreementId - } - return "" -} - -func (m *FundingData) GetAmount() string { - if m != nil { - return m.Amount - } - return "" -} - -func (m *FundingData) GetApr() string { - if m != nil { - return m.Apr - } - return "" -} - -func (m *FundingData) GetDays() string { - if m != nil { - return m.Days - } - return "" -} - -func (m *FundingData) GetFee() string { - if m != nil { - return m.Fee - } - return "" -} - -func (m *FundingData) GetRepaymentDueDate() string { - if m != nil { - return m.RepaymentDueDate - } - return "" -} - -func (m *FundingData) GetRepaymentOccurredDate() string { - if m != nil { - return m.RepaymentOccurredDate - } - return "" -} - -func (m *FundingData) GetRepaymentAmount() string { - if m != nil { - return m.RepaymentAmount - } - return "" -} - -func (m *FundingData) GetCurrency() string { - if m != nil { - return m.Currency - } - return "" -} - -func (m *FundingData) GetNftAddress() string { - if m != nil { - return m.NftAddress - } - return "" -} - -func (m *FundingData) GetPaymentDetailsId() string { - if m != nil { - return m.PaymentDetailsId - } - return "" -} - -func (m *FundingData) GetFunderId() string { - if m != nil { - return m.FunderId - } - return "" -} - -func (m *FundingData) GetBorrowerId() string { - if m != nil { - return m.BorrowerId - } - return "" -} - -type FundingResponseData struct { - Funding *FundingData `protobuf:"bytes,1,opt,name=funding,proto3" json:"funding,omitempty"` - Signatures []*FundingSignature `protobuf:"bytes,2,rep,name=signatures,proto3" json:"signatures,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *FundingResponseData) Reset() { *m = FundingResponseData{} } -func (m *FundingResponseData) String() string { return proto.CompactTextString(m) } -func (*FundingResponseData) ProtoMessage() {} -func (*FundingResponseData) Descriptor() ([]byte, []int) { - return fileDescriptor_e74e73a4c22d632c, []int{9} -} - -func (m *FundingResponseData) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_FundingResponseData.Unmarshal(m, b) -} -func (m *FundingResponseData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_FundingResponseData.Marshal(b, m, deterministic) -} -func (m *FundingResponseData) XXX_Merge(src proto.Message) { - xxx_messageInfo_FundingResponseData.Merge(m, src) -} -func (m *FundingResponseData) XXX_Size() int { - return xxx_messageInfo_FundingResponseData.Size(m) -} -func (m *FundingResponseData) XXX_DiscardUnknown() { - xxx_messageInfo_FundingResponseData.DiscardUnknown(m) -} - -var xxx_messageInfo_FundingResponseData proto.InternalMessageInfo - -func (m *FundingResponseData) GetFunding() *FundingData { - if m != nil { - return m.Funding - } - return nil -} - -func (m *FundingResponseData) GetSignatures() []*FundingSignature { - if m != nil { - return m.Signatures - } - return nil -} - -type FundingSignature struct { - Valid string `protobuf:"bytes,1,opt,name=valid,proto3" json:"valid,omitempty"` - OutdatedSignature string `protobuf:"bytes,2,opt,name=outdated_signature,json=outdatedSignature,proto3" json:"outdated_signature,omitempty"` - Identity string `protobuf:"bytes,3,opt,name=identity,proto3" json:"identity,omitempty"` - SignedVersion string `protobuf:"bytes,4,opt,name=signed_version,json=signedVersion,proto3" json:"signed_version,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *FundingSignature) Reset() { *m = FundingSignature{} } -func (m *FundingSignature) String() string { return proto.CompactTextString(m) } -func (*FundingSignature) ProtoMessage() {} -func (*FundingSignature) Descriptor() ([]byte, []int) { - return fileDescriptor_e74e73a4c22d632c, []int{10} -} - -func (m *FundingSignature) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_FundingSignature.Unmarshal(m, b) -} -func (m *FundingSignature) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_FundingSignature.Marshal(b, m, deterministic) -} -func (m *FundingSignature) XXX_Merge(src proto.Message) { - xxx_messageInfo_FundingSignature.Merge(m, src) -} -func (m *FundingSignature) XXX_Size() int { - return xxx_messageInfo_FundingSignature.Size(m) -} -func (m *FundingSignature) XXX_DiscardUnknown() { - xxx_messageInfo_FundingSignature.DiscardUnknown(m) -} - -var xxx_messageInfo_FundingSignature proto.InternalMessageInfo - -func (m *FundingSignature) GetValid() string { - if m != nil { - return m.Valid - } - return "" -} - -func (m *FundingSignature) GetOutdatedSignature() string { - if m != nil { - return m.OutdatedSignature - } - return "" -} - -func (m *FundingSignature) GetIdentity() string { - if m != nil { - return m.Identity - } - return "" -} - -func (m *FundingSignature) GetSignedVersion() string { - if m != nil { - return m.SignedVersion - } - return "" -} - -func init() { - proto.RegisterType((*FundingCreatePayload)(nil), "fun.FundingCreatePayload") - proto.RegisterType((*FundingUpdatePayload)(nil), "fun.FundingUpdatePayload") - proto.RegisterType((*FundingResponse)(nil), "fun.FundingResponse") - proto.RegisterType((*FundingListResponse)(nil), "fun.FundingListResponse") - proto.RegisterType((*Request)(nil), "fun.Request") - proto.RegisterType((*GetVersionRequest)(nil), "fun.GetVersionRequest") - proto.RegisterType((*GetListRequest)(nil), "fun.GetListRequest") - proto.RegisterType((*GetListVersionRequest)(nil), "fun.GetListVersionRequest") - proto.RegisterType((*FundingData)(nil), "fun.FundingData") - proto.RegisterType((*FundingResponseData)(nil), "fun.FundingResponseData") - proto.RegisterType((*FundingSignature)(nil), "fun.FundingSignature") -} - -func init() { proto.RegisterFile("funding/funding.proto", fileDescriptor_e74e73a4c22d632c) } - -var fileDescriptor_e74e73a4c22d632c = []byte{ - // 986 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0x4f, 0x6f, 0x1b, 0x45, - 0x14, 0xd7, 0xd6, 0x89, 0x1d, 0x3f, 0xa7, 0x6e, 0x3a, 0x4d, 0x82, 0x59, 0x0a, 0x2c, 0x2b, 0x2a, - 0xda, 0x92, 0x78, 0x93, 0x54, 0x70, 0x40, 0x42, 0x68, 0x43, 0x54, 0x13, 0x09, 0x44, 0xe4, 0xaa, - 0x20, 0x55, 0x42, 0xd6, 0xc4, 0x33, 0x6b, 0x16, 0x39, 0x33, 0xee, 0xce, 0xac, 0x83, 0x15, 0x55, - 0x02, 0xee, 0x5c, 0x8c, 0xb8, 0x70, 0x43, 0xf0, 0x05, 0xb8, 0xf2, 0x11, 0xb8, 0xf2, 0x15, 0x90, - 0xf8, 0x1a, 0x68, 0xfe, 0xec, 0x7a, 0x1d, 0xc7, 0xc1, 0x51, 0x73, 0xb2, 0xe7, 0xbd, 0xdf, 0xbc, - 0xf7, 0x7b, 0x6f, 0xde, 0x9f, 0x85, 0x8d, 0x28, 0x65, 0x24, 0x66, 0xbd, 0xc0, 0xfe, 0x36, 0x07, - 0x09, 0x97, 0x1c, 0x95, 0xa2, 0x94, 0xb9, 0x9b, 0x84, 0x77, 0xd3, 0x13, 0xca, 0x64, 0x20, 0x68, - 0x32, 0x8c, 0xbb, 0xd4, 0x28, 0xdd, 0xbb, 0x3d, 0xce, 0x7b, 0x7d, 0x1a, 0xe0, 0x41, 0x1c, 0x60, - 0xc6, 0xb8, 0xc4, 0x32, 0xe6, 0x4c, 0x58, 0xed, 0x96, 0xfe, 0xe9, 0x6e, 0xf7, 0x28, 0xdb, 0x16, - 0xa7, 0xb8, 0xd7, 0xa3, 0x49, 0xc0, 0x07, 0x1a, 0x31, 0x8b, 0xf6, 0xbf, 0x82, 0xf5, 0xc7, 0xc6, - 0xf3, 0xc7, 0x09, 0xc5, 0x92, 0x1e, 0xe1, 0x51, 0x9f, 0x63, 0x82, 0xde, 0x84, 0x5a, 0xe6, 0xbd, - 0x13, 0x93, 0x86, 0xe3, 0x39, 0xf7, 0xab, 0x6d, 0xc8, 0x44, 0x87, 0x04, 0xbd, 0x0d, 0x4b, 0x04, - 0x4b, 0xdc, 0xb8, 0xe1, 0x39, 0xf7, 0x6b, 0x7b, 0x6b, 0xcd, 0x28, 0x65, 0x4d, 0x6b, 0xe9, 0x00, - 0x4b, 0xdc, 0xd6, 0x5a, 0xff, 0x3b, 0x27, 0xb7, 0xff, 0x74, 0x40, 0xae, 0x62, 0xff, 0x2d, 0x58, - 0xc5, 0xbd, 0x84, 0xd2, 0x0c, 0x71, 0x43, 0x23, 0x6a, 0xb9, 0xac, 0x40, 0xa1, 0x74, 0x29, 0x85, - 0xe7, 0x70, 0xcb, 0x0a, 0xdb, 0x54, 0x0c, 0x38, 0x13, 0x14, 0xed, 0x40, 0xf9, 0x6b, 0x8a, 0x09, - 0x4d, 0xb4, 0xdf, 0xda, 0x5e, 0xa3, 0x99, 0x39, 0x6e, 0x66, 0x98, 0x4f, 0xb4, 0xbe, 0x6d, 0x71, - 0x68, 0x6b, 0x2a, 0xda, 0x46, 0xd1, 0x55, 0x76, 0xa3, 0xe0, 0x32, 0x85, 0x3b, 0x56, 0xf9, 0x69, - 0x2c, 0xe4, 0xb5, 0xb8, 0x2d, 0x2d, 0xe0, 0xf6, 0x33, 0xa8, 0xb4, 0xe9, 0xf3, 0x94, 0x0a, 0x79, - 0x1d, 0xe9, 0xf5, 0x87, 0x70, 0xbb, 0x45, 0xe5, 0x17, 0x34, 0x11, 0x31, 0x67, 0x0b, 0x1b, 0x7e, - 0x1d, 0x60, 0x68, 0xae, 0x4c, 0xcc, 0x56, 0xad, 0xe4, 0x02, 0xbf, 0xa5, 0x59, 0xbf, 0xbb, 0x50, - 0x6f, 0x51, 0x69, 0x32, 0xb7, 0x98, 0x53, 0xff, 0x4b, 0xd8, 0xb0, 0x57, 0xae, 0x97, 0xae, 0xff, - 0x67, 0x09, 0x6a, 0x85, 0x92, 0x9a, 0xa1, 0xef, 0xcc, 0x56, 0xe5, 0x26, 0x94, 0xf1, 0x09, 0x4f, - 0x99, 0xb4, 0xd6, 0xec, 0x09, 0xad, 0x41, 0x09, 0x0f, 0x12, 0x1b, 0xb0, 0xfa, 0x8b, 0x90, 0x7a, - 0xdd, 0x91, 0x68, 0x2c, 0x69, 0x91, 0xfe, 0xaf, 0x50, 0x11, 0xa5, 0x8d, 0x65, 0x83, 0x8a, 0x28, - 0x45, 0x5b, 0x80, 0x12, 0x3a, 0xc0, 0x23, 0xed, 0x92, 0xa4, 0xb4, 0xa3, 0xda, 0xa8, 0x51, 0xd1, - 0x80, 0xb5, 0x5c, 0x73, 0x90, 0xaa, 0x4a, 0xa0, 0xe8, 0x7d, 0x78, 0x65, 0x82, 0xe6, 0xdd, 0x6e, - 0x9a, 0x24, 0x94, 0x98, 0x2b, 0x2b, 0xfa, 0xca, 0x46, 0xae, 0xfe, 0xdc, 0x6a, 0xf5, 0xbd, 0x07, - 0x30, 0xb1, 0xd5, 0xb1, 0xfc, 0xab, 0xfa, 0xc2, 0xad, 0x5c, 0x1e, 0x9a, 0x40, 0x5c, 0x58, 0xd1, - 0x17, 0x59, 0x77, 0xd4, 0x00, 0x0d, 0xc9, 0xcf, 0x2a, 0xdf, 0x2c, 0x92, 0x1d, 0x4c, 0x48, 0x42, - 0x85, 0x68, 0xd4, 0x4c, 0xbe, 0x59, 0x24, 0x43, 0x23, 0x51, 0xd1, 0xe4, 0xb1, 0x50, 0x89, 0xe3, - 0xbe, 0x50, 0x69, 0x5c, 0x35, 0xd1, 0x64, 0xb1, 0x18, 0xc5, 0x21, 0x41, 0xaf, 0x41, 0x55, 0xcd, - 0x45, 0x9a, 0x28, 0xd0, 0x4d, 0xe3, 0xcb, 0x08, 0x0e, 0xf5, 0x08, 0x39, 0xe6, 0x49, 0xc2, 0x4f, - 0x8d, 0xba, 0x6e, 0x7c, 0x65, 0xa2, 0x43, 0xe2, 0x7f, 0x9b, 0xb7, 0x61, 0xb1, 0x59, 0xd0, 0x43, - 0xa8, 0xd8, 0x61, 0x6b, 0xfb, 0x70, 0x76, 0x72, 0x64, 0x00, 0xf4, 0x1e, 0x80, 0x88, 0x7b, 0x0c, - 0xcb, 0x34, 0xa1, 0xc2, 0xb6, 0xe1, 0x46, 0x11, 0xfe, 0x24, 0xd3, 0xb6, 0x0b, 0x40, 0xff, 0x17, - 0x07, 0xd6, 0xce, 0x03, 0xd0, 0x3a, 0x2c, 0x0f, 0x71, 0x3f, 0x2f, 0x1a, 0x73, 0x40, 0xdb, 0x80, - 0x78, 0x2a, 0xd5, 0x03, 0x91, 0x4e, 0x6e, 0xc1, 0x96, 0xce, 0xed, 0x4c, 0x33, 0x31, 0xe2, 0xc2, - 0x4a, 0x4c, 0x28, 0x93, 0xb1, 0x1c, 0xd9, 0x52, 0xca, 0xcf, 0xe8, 0x1e, 0xd4, 0x95, 0x05, 0x4a, - 0x3a, 0xb6, 0x80, 0x6d, 0x65, 0xdd, 0x34, 0x52, 0xdb, 0x1a, 0x7b, 0xff, 0x56, 0xa1, 0x9e, 0x91, - 0x33, 0x7b, 0x05, 0xfd, 0xec, 0x40, 0xd9, 0xcc, 0x7f, 0xf4, 0x6a, 0x31, 0xba, 0xa9, 0x9d, 0xe0, - 0xae, 0x5f, 0x34, 0x7f, 0xfc, 0x67, 0xe3, 0xf0, 0x0d, 0xf7, 0x6e, 0x48, 0x88, 0xf0, 0xb0, 0x67, - 0xf3, 0xe6, 0x49, 0xee, 0x61, 0x2f, 0x6b, 0xb3, 0x1f, 0xfe, 0xfe, 0xe7, 0xa7, 0x1b, 0x8f, 0xfc, - 0x66, 0x30, 0xdc, 0x0d, 0x32, 0x99, 0x08, 0xce, 0x0a, 0x8d, 0xf9, 0x22, 0x5b, 0x7f, 0x9d, 0xbc, - 0x93, 0xc4, 0x07, 0xce, 0x43, 0xf4, 0x87, 0x03, 0x65, 0xb3, 0x37, 0xa6, 0x79, 0x4d, 0xed, 0x92, - 0x39, 0xbc, 0x86, 0xe3, 0xf0, 0x5d, 0xf7, 0x81, 0x41, 0x16, 0xa9, 0xe5, 0x1e, 0xbc, 0x98, 0x9d, - 0x27, 0xb9, 0xef, 0x7e, 0x78, 0x35, 0x92, 0xc1, 0x59, 0x71, 0x1c, 0xbc, 0x50, 0x9c, 0x7f, 0x73, - 0x60, 0x49, 0xbd, 0x17, 0x5a, 0xd5, 0xb4, 0xec, 0x24, 0x9a, 0x43, 0xf2, 0x74, 0x1c, 0xbe, 0xe3, - 0xde, 0x53, 0x70, 0xb1, 0x10, 0xc1, 0x96, 0xbf, 0xff, 0x52, 0x04, 0x03, 0x55, 0x0a, 0x8a, 0xe5, - 0xaf, 0x0e, 0x94, 0x5a, 0x54, 0x2e, 0x44, 0x72, 0x38, 0x0e, 0x9b, 0xee, 0x56, 0x8b, 0xca, 0x0b, - 0xb3, 0xc8, 0x23, 0x0f, 0x7b, 0x7d, 0x95, 0x64, 0x39, 0xcd, 0xf5, 0x23, 0xf4, 0x72, 0xc9, 0x44, - 0x7f, 0x39, 0x00, 0x93, 0x0d, 0x84, 0x36, 0x35, 0xb9, 0x99, 0x95, 0x34, 0x87, 0xf4, 0x8f, 0xce, - 0x38, 0x0c, 0xdc, 0xed, 0x4b, 0x59, 0x67, 0x7c, 0x3c, 0xdb, 0x2f, 0x9a, 0xf6, 0x53, 0xf4, 0xe4, - 0x32, 0xda, 0x16, 0x2a, 0x82, 0xb3, 0xc9, 0xde, 0x58, 0x24, 0x98, 0xdf, 0x1d, 0xa8, 0xd8, 0x1d, - 0x85, 0xee, 0x64, 0x91, 0x14, 0x96, 0x9c, 0x3b, 0xb5, 0xdd, 0x8b, 0xdf, 0x0d, 0xfe, 0x37, 0xe3, - 0x70, 0xd7, 0x0d, 0x74, 0x24, 0xfd, 0xfe, 0x6c, 0x2c, 0x62, 0xfe, 0x13, 0xec, 0xa0, 0x2b, 0x36, - 0x9d, 0xca, 0x79, 0x7d, 0x7a, 0x95, 0x22, 0xb7, 0xc8, 0xf6, 0x5c, 0xee, 0xe7, 0x93, 0xfe, 0xde, - 0x19, 0x87, 0x7b, 0xee, 0xce, 0xff, 0xb1, 0xbe, 0xf0, 0x09, 0x1e, 0xa3, 0x83, 0xeb, 0x78, 0x82, - 0x7d, 0x0f, 0x2a, 0x5d, 0x7e, 0xa2, 0x28, 0xee, 0xaf, 0x5a, 0x8e, 0x47, 0xea, 0xab, 0xf7, 0xc8, - 0x79, 0xb6, 0x1c, 0xa5, 0x6c, 0x70, 0x7c, 0x5c, 0xd6, 0x5f, 0xc1, 0x8f, 0xfe, 0x0b, 0x00, 0x00, - 0xff, 0xff, 0xf2, 0xff, 0xc2, 0xce, 0x87, 0x0b, 0x00, 0x00, -} - -// Reference imports to suppress errors if they are not otherwise used. -var _ context.Context -var _ grpc.ClientConn - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion4 - -// FundingServiceClient is the client API for FundingService service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. -type FundingServiceClient interface { - Create(ctx context.Context, in *FundingCreatePayload, opts ...grpc.CallOption) (*FundingResponse, error) - Update(ctx context.Context, in *FundingUpdatePayload, opts ...grpc.CallOption) (*FundingResponse, error) - Sign(ctx context.Context, in *Request, opts ...grpc.CallOption) (*FundingResponse, error) - Get(ctx context.Context, in *Request, opts ...grpc.CallOption) (*FundingResponse, error) - GetVersion(ctx context.Context, in *GetVersionRequest, opts ...grpc.CallOption) (*FundingResponse, error) - GetList(ctx context.Context, in *GetListRequest, opts ...grpc.CallOption) (*FundingListResponse, error) - GetListVersion(ctx context.Context, in *GetListVersionRequest, opts ...grpc.CallOption) (*FundingListResponse, error) -} - -type fundingServiceClient struct { - cc *grpc.ClientConn -} - -func NewFundingServiceClient(cc *grpc.ClientConn) FundingServiceClient { - return &fundingServiceClient{cc} -} - -func (c *fundingServiceClient) Create(ctx context.Context, in *FundingCreatePayload, opts ...grpc.CallOption) (*FundingResponse, error) { - out := new(FundingResponse) - err := c.cc.Invoke(ctx, "/fun.FundingService/Create", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *fundingServiceClient) Update(ctx context.Context, in *FundingUpdatePayload, opts ...grpc.CallOption) (*FundingResponse, error) { - out := new(FundingResponse) - err := c.cc.Invoke(ctx, "/fun.FundingService/Update", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *fundingServiceClient) Sign(ctx context.Context, in *Request, opts ...grpc.CallOption) (*FundingResponse, error) { - out := new(FundingResponse) - err := c.cc.Invoke(ctx, "/fun.FundingService/Sign", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *fundingServiceClient) Get(ctx context.Context, in *Request, opts ...grpc.CallOption) (*FundingResponse, error) { - out := new(FundingResponse) - err := c.cc.Invoke(ctx, "/fun.FundingService/Get", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *fundingServiceClient) GetVersion(ctx context.Context, in *GetVersionRequest, opts ...grpc.CallOption) (*FundingResponse, error) { - out := new(FundingResponse) - err := c.cc.Invoke(ctx, "/fun.FundingService/GetVersion", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *fundingServiceClient) GetList(ctx context.Context, in *GetListRequest, opts ...grpc.CallOption) (*FundingListResponse, error) { - out := new(FundingListResponse) - err := c.cc.Invoke(ctx, "/fun.FundingService/GetList", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *fundingServiceClient) GetListVersion(ctx context.Context, in *GetListVersionRequest, opts ...grpc.CallOption) (*FundingListResponse, error) { - out := new(FundingListResponse) - err := c.cc.Invoke(ctx, "/fun.FundingService/GetListVersion", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// FundingServiceServer is the server API for FundingService service. -type FundingServiceServer interface { - Create(context.Context, *FundingCreatePayload) (*FundingResponse, error) - Update(context.Context, *FundingUpdatePayload) (*FundingResponse, error) - Sign(context.Context, *Request) (*FundingResponse, error) - Get(context.Context, *Request) (*FundingResponse, error) - GetVersion(context.Context, *GetVersionRequest) (*FundingResponse, error) - GetList(context.Context, *GetListRequest) (*FundingListResponse, error) - GetListVersion(context.Context, *GetListVersionRequest) (*FundingListResponse, error) -} - -func RegisterFundingServiceServer(s *grpc.Server, srv FundingServiceServer) { - s.RegisterService(&_FundingService_serviceDesc, srv) -} - -func _FundingService_Create_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(FundingCreatePayload) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(FundingServiceServer).Create(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/fun.FundingService/Create", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(FundingServiceServer).Create(ctx, req.(*FundingCreatePayload)) - } - return interceptor(ctx, in, info, handler) -} - -func _FundingService_Update_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(FundingUpdatePayload) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(FundingServiceServer).Update(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/fun.FundingService/Update", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(FundingServiceServer).Update(ctx, req.(*FundingUpdatePayload)) - } - return interceptor(ctx, in, info, handler) -} - -func _FundingService_Sign_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(Request) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(FundingServiceServer).Sign(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/fun.FundingService/Sign", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(FundingServiceServer).Sign(ctx, req.(*Request)) - } - return interceptor(ctx, in, info, handler) -} - -func _FundingService_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(Request) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(FundingServiceServer).Get(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/fun.FundingService/Get", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(FundingServiceServer).Get(ctx, req.(*Request)) - } - return interceptor(ctx, in, info, handler) -} - -func _FundingService_GetVersion_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetVersionRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(FundingServiceServer).GetVersion(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/fun.FundingService/GetVersion", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(FundingServiceServer).GetVersion(ctx, req.(*GetVersionRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _FundingService_GetList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetListRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(FundingServiceServer).GetList(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/fun.FundingService/GetList", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(FundingServiceServer).GetList(ctx, req.(*GetListRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _FundingService_GetListVersion_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetListVersionRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(FundingServiceServer).GetListVersion(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/fun.FundingService/GetListVersion", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(FundingServiceServer).GetListVersion(ctx, req.(*GetListVersionRequest)) - } - return interceptor(ctx, in, info, handler) -} - -var _FundingService_serviceDesc = grpc.ServiceDesc{ - ServiceName: "fun.FundingService", - HandlerType: (*FundingServiceServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "Create", - Handler: _FundingService_Create_Handler, - }, - { - MethodName: "Update", - Handler: _FundingService_Update_Handler, - }, - { - MethodName: "Sign", - Handler: _FundingService_Sign_Handler, - }, - { - MethodName: "Get", - Handler: _FundingService_Get_Handler, - }, - { - MethodName: "GetVersion", - Handler: _FundingService_GetVersion_Handler, - }, - { - MethodName: "GetList", - Handler: _FundingService_GetList_Handler, - }, - { - MethodName: "GetListVersion", - Handler: _FundingService_GetListVersion_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "funding/funding.proto", -} diff --git a/protobufs/gen/go/funding/funding.pb.gw.go b/protobufs/gen/go/funding/funding.pb.gw.go deleted file mode 100644 index 043798285..000000000 --- a/protobufs/gen/go/funding/funding.pb.gw.go +++ /dev/null @@ -1,572 +0,0 @@ -// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. -// source: funding/funding.proto - -/* -Package funpb is a reverse proxy. - -It translates gRPC into RESTful JSON APIs. -*/ -package funpb - -import ( - "io" - "net/http" - - "github.com/golang/protobuf/proto" - "github.com/grpc-ecosystem/grpc-gateway/runtime" - "github.com/grpc-ecosystem/grpc-gateway/utilities" - "golang.org/x/net/context" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/grpclog" - "google.golang.org/grpc/status" -) - -var _ codes.Code -var _ io.Reader -var _ status.Status -var _ = runtime.String -var _ = utilities.NewDoubleArray - -func request_FundingService_Create_0(ctx context.Context, marshaler runtime.Marshaler, client FundingServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq FundingCreatePayload - var metadata runtime.ServerMetadata - - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["document_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "document_id") - } - - protoReq.DocumentId, err = runtime.String(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "document_id", err) - } - - msg, err := client.Create(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func request_FundingService_Update_0(ctx context.Context, marshaler runtime.Marshaler, client FundingServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq FundingUpdatePayload - var metadata runtime.ServerMetadata - - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["document_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "document_id") - } - - protoReq.DocumentId, err = runtime.String(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "document_id", err) - } - - val, ok = pathParams["agreement_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "agreement_id") - } - - protoReq.AgreementId, err = runtime.String(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "agreement_id", err) - } - - msg, err := client.Update(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func request_FundingService_Sign_0(ctx context.Context, marshaler runtime.Marshaler, client FundingServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq Request - var metadata runtime.ServerMetadata - - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["document_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "document_id") - } - - protoReq.DocumentId, err = runtime.String(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "document_id", err) - } - - val, ok = pathParams["agreement_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "agreement_id") - } - - protoReq.AgreementId, err = runtime.String(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "agreement_id", err) - } - - msg, err := client.Sign(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func request_FundingService_Get_0(ctx context.Context, marshaler runtime.Marshaler, client FundingServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq Request - var metadata runtime.ServerMetadata - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["document_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "document_id") - } - - protoReq.DocumentId, err = runtime.String(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "document_id", err) - } - - val, ok = pathParams["agreement_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "agreement_id") - } - - protoReq.AgreementId, err = runtime.String(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "agreement_id", err) - } - - msg, err := client.Get(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func request_FundingService_GetVersion_0(ctx context.Context, marshaler runtime.Marshaler, client FundingServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq GetVersionRequest - var metadata runtime.ServerMetadata - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["document_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "document_id") - } - - protoReq.DocumentId, err = runtime.String(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "document_id", err) - } - - val, ok = pathParams["version_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "version_id") - } - - protoReq.VersionId, err = runtime.String(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "version_id", err) - } - - val, ok = pathParams["agreement_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "agreement_id") - } - - protoReq.AgreementId, err = runtime.String(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "agreement_id", err) - } - - msg, err := client.GetVersion(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func request_FundingService_GetList_0(ctx context.Context, marshaler runtime.Marshaler, client FundingServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq GetListRequest - var metadata runtime.ServerMetadata - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["document_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "document_id") - } - - protoReq.DocumentId, err = runtime.String(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "document_id", err) - } - - msg, err := client.GetList(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func request_FundingService_GetListVersion_0(ctx context.Context, marshaler runtime.Marshaler, client FundingServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq GetListVersionRequest - var metadata runtime.ServerMetadata - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["document_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "document_id") - } - - protoReq.DocumentId, err = runtime.String(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "document_id", err) - } - - val, ok = pathParams["version_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "version_id") - } - - protoReq.VersionId, err = runtime.String(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "version_id", err) - } - - msg, err := client.GetListVersion(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -// RegisterFundingServiceHandlerFromEndpoint is same as RegisterFundingServiceHandler but -// automatically dials to "endpoint" and closes the connection when "ctx" gets done. -func RegisterFundingServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { - conn, err := grpc.Dial(endpoint, opts...) - if err != nil { - return err - } - defer func() { - if err != nil { - if cerr := conn.Close(); cerr != nil { - grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) - } - return - } - go func() { - <-ctx.Done() - if cerr := conn.Close(); cerr != nil { - grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) - } - }() - }() - - return RegisterFundingServiceHandler(ctx, mux, conn) -} - -// RegisterFundingServiceHandler registers the http handlers for service FundingService to "mux". -// The handlers forward requests to the grpc endpoint over "conn". -func RegisterFundingServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { - return RegisterFundingServiceHandlerClient(ctx, mux, NewFundingServiceClient(conn)) -} - -// RegisterFundingServiceHandlerClient registers the http handlers for service FundingService -// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "FundingServiceClient". -// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "FundingServiceClient" -// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in -// "FundingServiceClient" to call the correct interceptors. -func RegisterFundingServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client FundingServiceClient) error { - - mux.Handle("POST", pattern_FundingService_Create_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - if cn, ok := w.(http.CloseNotifier); ok { - go func(done <-chan struct{}, closed <-chan bool) { - select { - case <-done: - case <-closed: - cancel() - } - }(ctx.Done(), cn.CloseNotify()) - } - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_FundingService_Create_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_FundingService_Create_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("PUT", pattern_FundingService_Update_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - if cn, ok := w.(http.CloseNotifier); ok { - go func(done <-chan struct{}, closed <-chan bool) { - select { - case <-done: - case <-closed: - cancel() - } - }(ctx.Done(), cn.CloseNotify()) - } - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_FundingService_Update_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_FundingService_Update_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("POST", pattern_FundingService_Sign_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - if cn, ok := w.(http.CloseNotifier); ok { - go func(done <-chan struct{}, closed <-chan bool) { - select { - case <-done: - case <-closed: - cancel() - } - }(ctx.Done(), cn.CloseNotify()) - } - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_FundingService_Sign_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_FundingService_Sign_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("GET", pattern_FundingService_Get_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - if cn, ok := w.(http.CloseNotifier); ok { - go func(done <-chan struct{}, closed <-chan bool) { - select { - case <-done: - case <-closed: - cancel() - } - }(ctx.Done(), cn.CloseNotify()) - } - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_FundingService_Get_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_FundingService_Get_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("GET", pattern_FundingService_GetVersion_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - if cn, ok := w.(http.CloseNotifier); ok { - go func(done <-chan struct{}, closed <-chan bool) { - select { - case <-done: - case <-closed: - cancel() - } - }(ctx.Done(), cn.CloseNotify()) - } - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_FundingService_GetVersion_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_FundingService_GetVersion_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("GET", pattern_FundingService_GetList_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - if cn, ok := w.(http.CloseNotifier); ok { - go func(done <-chan struct{}, closed <-chan bool) { - select { - case <-done: - case <-closed: - cancel() - } - }(ctx.Done(), cn.CloseNotify()) - } - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_FundingService_GetList_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_FundingService_GetList_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("GET", pattern_FundingService_GetListVersion_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - if cn, ok := w.(http.CloseNotifier); ok { - go func(done <-chan struct{}, closed <-chan bool) { - select { - case <-done: - case <-closed: - cancel() - } - }(ctx.Done(), cn.CloseNotify()) - } - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_FundingService_GetListVersion_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_FundingService_GetListVersion_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - return nil -} - -var ( - pattern_FundingService_Create_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3}, []string{"v1", "documents", "document_id", "funding_agreements"}, "")) - - pattern_FundingService_Update_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"v1", "documents", "document_id", "funding_agreements", "agreement_id"}, "")) - - pattern_FundingService_Sign_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"v1", "documents", "document_id", "funding_agreements", "agreement_id", "sign"}, "")) - - pattern_FundingService_Get_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"v1", "documents", "document_id", "funding_agreements", "agreement_id"}, "")) - - pattern_FundingService_GetVersion_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5, 1, 0, 4, 1, 5, 6}, []string{"v1", "documents", "document_id", "versions", "version_id", "funding_agreements", "agreement_id"}, "")) - - pattern_FundingService_GetList_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3}, []string{"v1", "documents", "document_id", "funding_agreements"}, "")) - - pattern_FundingService_GetListVersion_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"v1", "documents", "document_id", "versions", "version_id", "funding_agreements"}, "")) -) - -var ( - forward_FundingService_Create_0 = runtime.ForwardResponseMessage - - forward_FundingService_Update_0 = runtime.ForwardResponseMessage - - forward_FundingService_Sign_0 = runtime.ForwardResponseMessage - - forward_FundingService_Get_0 = runtime.ForwardResponseMessage - - forward_FundingService_GetVersion_0 = runtime.ForwardResponseMessage - - forward_FundingService_GetList_0 = runtime.ForwardResponseMessage - - forward_FundingService_GetListVersion_0 = runtime.ForwardResponseMessage -) diff --git a/protobufs/gen/go/invoice/service.pb.go b/protobufs/gen/go/invoice/service.pb.go deleted file mode 100644 index 8edf63b6f..000000000 --- a/protobufs/gen/go/invoice/service.pb.go +++ /dev/null @@ -1,1414 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// source: invoice/service.proto - -package invpb - -import ( - context "context" - fmt "fmt" - math "math" - - document "github.com/centrifuge/go-centrifuge/protobufs/gen/go/document" - proto "github.com/golang/protobuf/proto" - timestamp "github.com/golang/protobuf/ptypes/timestamp" - _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/options" - _ "google.golang.org/genproto/googleapis/api/annotations" - grpc "google.golang.org/grpc" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package - -type GetRequest struct { - DocumentId string `protobuf:"bytes,1,opt,name=document_id,json=documentId,proto3" json:"document_id,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *GetRequest) Reset() { *m = GetRequest{} } -func (m *GetRequest) String() string { return proto.CompactTextString(m) } -func (*GetRequest) ProtoMessage() {} -func (*GetRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_a2539fddd216e73e, []int{0} -} - -func (m *GetRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_GetRequest.Unmarshal(m, b) -} -func (m *GetRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_GetRequest.Marshal(b, m, deterministic) -} -func (m *GetRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetRequest.Merge(m, src) -} -func (m *GetRequest) XXX_Size() int { - return xxx_messageInfo_GetRequest.Size(m) -} -func (m *GetRequest) XXX_DiscardUnknown() { - xxx_messageInfo_GetRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_GetRequest proto.InternalMessageInfo - -func (m *GetRequest) GetDocumentId() string { - if m != nil { - return m.DocumentId - } - return "" -} - -type GetVersionRequest struct { - DocumentId string `protobuf:"bytes,1,opt,name=document_id,json=documentId,proto3" json:"document_id,omitempty"` - VersionId string `protobuf:"bytes,2,opt,name=version_id,json=versionId,proto3" json:"version_id,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *GetVersionRequest) Reset() { *m = GetVersionRequest{} } -func (m *GetVersionRequest) String() string { return proto.CompactTextString(m) } -func (*GetVersionRequest) ProtoMessage() {} -func (*GetVersionRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_a2539fddd216e73e, []int{1} -} - -func (m *GetVersionRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_GetVersionRequest.Unmarshal(m, b) -} -func (m *GetVersionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_GetVersionRequest.Marshal(b, m, deterministic) -} -func (m *GetVersionRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetVersionRequest.Merge(m, src) -} -func (m *GetVersionRequest) XXX_Size() int { - return xxx_messageInfo_GetVersionRequest.Size(m) -} -func (m *GetVersionRequest) XXX_DiscardUnknown() { - xxx_messageInfo_GetVersionRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_GetVersionRequest proto.InternalMessageInfo - -func (m *GetVersionRequest) GetDocumentId() string { - if m != nil { - return m.DocumentId - } - return "" -} - -func (m *GetVersionRequest) GetVersionId() string { - if m != nil { - return m.VersionId - } - return "" -} - -type InvoiceCreatePayload struct { - ReadAccess []string `protobuf:"bytes,1,rep,name=read_access,json=readAccess,proto3" json:"read_access,omitempty"` - WriteAccess []string `protobuf:"bytes,2,rep,name=write_access,json=writeAccess,proto3" json:"write_access,omitempty"` - Data *InvoiceData `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` - // custom attributes - Attributes map[string]*document.Attribute `protobuf:"bytes,4,rep,name=attributes,proto3" json:"attributes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *InvoiceCreatePayload) Reset() { *m = InvoiceCreatePayload{} } -func (m *InvoiceCreatePayload) String() string { return proto.CompactTextString(m) } -func (*InvoiceCreatePayload) ProtoMessage() {} -func (*InvoiceCreatePayload) Descriptor() ([]byte, []int) { - return fileDescriptor_a2539fddd216e73e, []int{2} -} - -func (m *InvoiceCreatePayload) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_InvoiceCreatePayload.Unmarshal(m, b) -} -func (m *InvoiceCreatePayload) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_InvoiceCreatePayload.Marshal(b, m, deterministic) -} -func (m *InvoiceCreatePayload) XXX_Merge(src proto.Message) { - xxx_messageInfo_InvoiceCreatePayload.Merge(m, src) -} -func (m *InvoiceCreatePayload) XXX_Size() int { - return xxx_messageInfo_InvoiceCreatePayload.Size(m) -} -func (m *InvoiceCreatePayload) XXX_DiscardUnknown() { - xxx_messageInfo_InvoiceCreatePayload.DiscardUnknown(m) -} - -var xxx_messageInfo_InvoiceCreatePayload proto.InternalMessageInfo - -func (m *InvoiceCreatePayload) GetReadAccess() []string { - if m != nil { - return m.ReadAccess - } - return nil -} - -func (m *InvoiceCreatePayload) GetWriteAccess() []string { - if m != nil { - return m.WriteAccess - } - return nil -} - -func (m *InvoiceCreatePayload) GetData() *InvoiceData { - if m != nil { - return m.Data - } - return nil -} - -func (m *InvoiceCreatePayload) GetAttributes() map[string]*document.Attribute { - if m != nil { - return m.Attributes - } - return nil -} - -type InvoiceUpdatePayload struct { - DocumentId string `protobuf:"bytes,1,opt,name=document_id,json=documentId,proto3" json:"document_id,omitempty"` - ReadAccess []string `protobuf:"bytes,2,rep,name=read_access,json=readAccess,proto3" json:"read_access,omitempty"` - WriteAccess []string `protobuf:"bytes,3,rep,name=write_access,json=writeAccess,proto3" json:"write_access,omitempty"` - Data *InvoiceData `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"` - // custom attributes - Attributes map[string]*document.Attribute `protobuf:"bytes,5,rep,name=attributes,proto3" json:"attributes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *InvoiceUpdatePayload) Reset() { *m = InvoiceUpdatePayload{} } -func (m *InvoiceUpdatePayload) String() string { return proto.CompactTextString(m) } -func (*InvoiceUpdatePayload) ProtoMessage() {} -func (*InvoiceUpdatePayload) Descriptor() ([]byte, []int) { - return fileDescriptor_a2539fddd216e73e, []int{3} -} - -func (m *InvoiceUpdatePayload) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_InvoiceUpdatePayload.Unmarshal(m, b) -} -func (m *InvoiceUpdatePayload) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_InvoiceUpdatePayload.Marshal(b, m, deterministic) -} -func (m *InvoiceUpdatePayload) XXX_Merge(src proto.Message) { - xxx_messageInfo_InvoiceUpdatePayload.Merge(m, src) -} -func (m *InvoiceUpdatePayload) XXX_Size() int { - return xxx_messageInfo_InvoiceUpdatePayload.Size(m) -} -func (m *InvoiceUpdatePayload) XXX_DiscardUnknown() { - xxx_messageInfo_InvoiceUpdatePayload.DiscardUnknown(m) -} - -var xxx_messageInfo_InvoiceUpdatePayload proto.InternalMessageInfo - -func (m *InvoiceUpdatePayload) GetDocumentId() string { - if m != nil { - return m.DocumentId - } - return "" -} - -func (m *InvoiceUpdatePayload) GetReadAccess() []string { - if m != nil { - return m.ReadAccess - } - return nil -} - -func (m *InvoiceUpdatePayload) GetWriteAccess() []string { - if m != nil { - return m.WriteAccess - } - return nil -} - -func (m *InvoiceUpdatePayload) GetData() *InvoiceData { - if m != nil { - return m.Data - } - return nil -} - -func (m *InvoiceUpdatePayload) GetAttributes() map[string]*document.Attribute { - if m != nil { - return m.Attributes - } - return nil -} - -type InvoiceResponse struct { - Header *document.ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"` - Data *InvoiceData `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` - // custom attributes - Attributes map[string]*document.Attribute `protobuf:"bytes,3,rep,name=attributes,proto3" json:"attributes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *InvoiceResponse) Reset() { *m = InvoiceResponse{} } -func (m *InvoiceResponse) String() string { return proto.CompactTextString(m) } -func (*InvoiceResponse) ProtoMessage() {} -func (*InvoiceResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_a2539fddd216e73e, []int{4} -} - -func (m *InvoiceResponse) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_InvoiceResponse.Unmarshal(m, b) -} -func (m *InvoiceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_InvoiceResponse.Marshal(b, m, deterministic) -} -func (m *InvoiceResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_InvoiceResponse.Merge(m, src) -} -func (m *InvoiceResponse) XXX_Size() int { - return xxx_messageInfo_InvoiceResponse.Size(m) -} -func (m *InvoiceResponse) XXX_DiscardUnknown() { - xxx_messageInfo_InvoiceResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_InvoiceResponse proto.InternalMessageInfo - -func (m *InvoiceResponse) GetHeader() *document.ResponseHeader { - if m != nil { - return m.Header - } - return nil -} - -func (m *InvoiceResponse) GetData() *InvoiceData { - if m != nil { - return m.Data - } - return nil -} - -func (m *InvoiceResponse) GetAttributes() map[string]*document.Attribute { - if m != nil { - return m.Attributes - } - return nil -} - -// InvoiceData is the default invoice schema -type InvoiceData struct { - // invoice number or reference number - Number string `protobuf:"bytes,1,opt,name=number,proto3" json:"number,omitempty"` - // invoice status - Status string `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` - SenderInvoiceId string `protobuf:"bytes,3,opt,name=sender_invoice_id,json=senderInvoiceId,proto3" json:"sender_invoice_id,omitempty"` - RecipientInvoiceId string `protobuf:"bytes,4,opt,name=recipient_invoice_id,json=recipientInvoiceId,proto3" json:"recipient_invoice_id,omitempty"` - SenderCompanyName string `protobuf:"bytes,5,opt,name=sender_company_name,json=senderCompanyName,proto3" json:"sender_company_name,omitempty"` - SenderContactPersonName string `protobuf:"bytes,6,opt,name=sender_contact_person_name,json=senderContactPersonName,proto3" json:"sender_contact_person_name,omitempty"` - // street and address details of the sender company - SenderStreet1 string `protobuf:"bytes,7,opt,name=sender_street1,json=senderStreet1,proto3" json:"sender_street1,omitempty"` - SenderStreet2 string `protobuf:"bytes,8,opt,name=sender_street2,json=senderStreet2,proto3" json:"sender_street2,omitempty"` - SenderCity string `protobuf:"bytes,9,opt,name=sender_city,json=senderCity,proto3" json:"sender_city,omitempty"` - SenderZipcode string `protobuf:"bytes,10,opt,name=sender_zipcode,json=senderZipcode,proto3" json:"sender_zipcode,omitempty"` - SenderState string `protobuf:"bytes,11,opt,name=sender_state,json=senderState,proto3" json:"sender_state,omitempty"` - // country ISO code of the sender of this invoice - SenderCountry string `protobuf:"bytes,12,opt,name=sender_country,json=senderCountry,proto3" json:"sender_country,omitempty"` - BillToCompanyName string `protobuf:"bytes,43,opt,name=bill_to_company_name,json=billToCompanyName,proto3" json:"bill_to_company_name,omitempty"` - BillToContactPersonName string `protobuf:"bytes,44,opt,name=bill_to_contact_person_name,json=billToContactPersonName,proto3" json:"bill_to_contact_person_name,omitempty"` - BillToStreet1 string `protobuf:"bytes,15,opt,name=bill_to_street1,json=billToStreet1,proto3" json:"bill_to_street1,omitempty"` - BillToStreet2 string `protobuf:"bytes,16,opt,name=bill_to_street2,json=billToStreet2,proto3" json:"bill_to_street2,omitempty"` - BillToCity string `protobuf:"bytes,17,opt,name=bill_to_city,json=billToCity,proto3" json:"bill_to_city,omitempty"` - BillToZipcode string `protobuf:"bytes,18,opt,name=bill_to_zipcode,json=billToZipcode,proto3" json:"bill_to_zipcode,omitempty"` - BillToState string `protobuf:"bytes,50,opt,name=bill_to_state,json=billToState,proto3" json:"bill_to_state,omitempty"` - BillToCountry string `protobuf:"bytes,20,opt,name=bill_to_country,json=billToCountry,proto3" json:"bill_to_country,omitempty"` - BillToVatNumber string `protobuf:"bytes,21,opt,name=bill_to_vat_number,json=billToVatNumber,proto3" json:"bill_to_vat_number,omitempty"` - BillToLocalTaxId string `protobuf:"bytes,60,opt,name=bill_to_local_tax_id,json=billToLocalTaxId,proto3" json:"bill_to_local_tax_id,omitempty"` - RemitToCompanyName string `protobuf:"bytes,23,opt,name=remit_to_company_name,json=remitToCompanyName,proto3" json:"remit_to_company_name,omitempty"` - RemitToContactPersonName string `protobuf:"bytes,24,opt,name=remit_to_contact_person_name,json=remitToContactPersonName,proto3" json:"remit_to_contact_person_name,omitempty"` - RemitToStreet1 string `protobuf:"bytes,25,opt,name=remit_to_street1,json=remitToStreet1,proto3" json:"remit_to_street1,omitempty"` - RemitToStreet2 string `protobuf:"bytes,26,opt,name=remit_to_street2,json=remitToStreet2,proto3" json:"remit_to_street2,omitempty"` - RemitToCity string `protobuf:"bytes,27,opt,name=remit_to_city,json=remitToCity,proto3" json:"remit_to_city,omitempty"` - RemitToZipcode string `protobuf:"bytes,28,opt,name=remit_to_zipcode,json=remitToZipcode,proto3" json:"remit_to_zipcode,omitempty"` - RemitToState string `protobuf:"bytes,30,opt,name=remit_to_state,json=remitToState,proto3" json:"remit_to_state,omitempty"` - RemitToCountry string `protobuf:"bytes,31,opt,name=remit_to_country,json=remitToCountry,proto3" json:"remit_to_country,omitempty"` - RemitToVatNumber string `protobuf:"bytes,32,opt,name=remit_to_vat_number,json=remitToVatNumber,proto3" json:"remit_to_vat_number,omitempty"` - RemitToLocalTaxId string `protobuf:"bytes,33,opt,name=remit_to_local_tax_id,json=remitToLocalTaxId,proto3" json:"remit_to_local_tax_id,omitempty"` - RemitToTaxCountry string `protobuf:"bytes,34,opt,name=remit_to_tax_country,json=remitToTaxCountry,proto3" json:"remit_to_tax_country,omitempty"` - ShipToCompanyName string `protobuf:"bytes,35,opt,name=ship_to_company_name,json=shipToCompanyName,proto3" json:"ship_to_company_name,omitempty"` - ShipToContactPersonName string `protobuf:"bytes,36,opt,name=ship_to_contact_person_name,json=shipToContactPersonName,proto3" json:"ship_to_contact_person_name,omitempty"` - ShipToStreet1 string `protobuf:"bytes,37,opt,name=ship_to_street1,json=shipToStreet1,proto3" json:"ship_to_street1,omitempty"` - ShipToStreet2 string `protobuf:"bytes,38,opt,name=ship_to_street2,json=shipToStreet2,proto3" json:"ship_to_street2,omitempty"` - ShipToCity string `protobuf:"bytes,39,opt,name=ship_to_city,json=shipToCity,proto3" json:"ship_to_city,omitempty"` - ShipToZipcode string `protobuf:"bytes,40,opt,name=ship_to_zipcode,json=shipToZipcode,proto3" json:"ship_to_zipcode,omitempty"` - ShipToState string `protobuf:"bytes,41,opt,name=ship_to_state,json=shipToState,proto3" json:"ship_to_state,omitempty"` - ShipToCountry string `protobuf:"bytes,42,opt,name=ship_to_country,json=shipToCountry,proto3" json:"ship_to_country,omitempty"` - // ISO currency code - Currency string `protobuf:"bytes,13,opt,name=currency,proto3" json:"currency,omitempty"` - // invoice amount including tax - GrossAmount string `protobuf:"bytes,14,opt,name=gross_amount,json=grossAmount,proto3" json:"gross_amount,omitempty"` - // invoice amount excluding tax - NetAmount string `protobuf:"bytes,45,opt,name=net_amount,json=netAmount,proto3" json:"net_amount,omitempty"` - TaxAmount string `protobuf:"bytes,46,opt,name=tax_amount,json=taxAmount,proto3" json:"tax_amount,omitempty"` - TaxRate string `protobuf:"bytes,47,opt,name=tax_rate,json=taxRate,proto3" json:"tax_rate,omitempty"` - TaxOnLineLevel bool `protobuf:"varint,48,opt,name=tax_on_line_level,json=taxOnLineLevel,proto3" json:"tax_on_line_level,omitempty"` - // centrifuge ID of the recipient - Recipient string `protobuf:"bytes,49,opt,name=recipient,proto3" json:"recipient,omitempty"` - // centrifuge ID of the sender - Sender string `protobuf:"bytes,19,opt,name=sender,proto3" json:"sender,omitempty"` - // centrifuge ID of the payee - Payee string `protobuf:"bytes,51,opt,name=payee,proto3" json:"payee,omitempty"` - Comment string `protobuf:"bytes,52,opt,name=comment,proto3" json:"comment,omitempty"` - ShippingTerms string `protobuf:"bytes,53,opt,name=shipping_terms,json=shippingTerms,proto3" json:"shipping_terms,omitempty"` - RequesterEmail string `protobuf:"bytes,54,opt,name=requester_email,json=requesterEmail,proto3" json:"requester_email,omitempty"` - RequesterName string `protobuf:"bytes,55,opt,name=requester_name,json=requesterName,proto3" json:"requester_name,omitempty"` - //number of the delivery note - DeliveryNumber string `protobuf:"bytes,56,opt,name=delivery_number,json=deliveryNumber,proto3" json:"delivery_number,omitempty"` - IsCreditNote bool `protobuf:"varint,57,opt,name=is_credit_note,json=isCreditNote,proto3" json:"is_credit_note,omitempty"` - CreditNoteInvoiceNumber string `protobuf:"bytes,58,opt,name=credit_note_invoice_number,json=creditNoteInvoiceNumber,proto3" json:"credit_note_invoice_number,omitempty"` - CreditForInvoiceDate *timestamp.Timestamp `protobuf:"bytes,59,opt,name=credit_for_invoice_date,json=creditForInvoiceDate,proto3" json:"credit_for_invoice_date,omitempty"` - DateDue *timestamp.Timestamp `protobuf:"bytes,22,opt,name=date_due,json=dateDue,proto3" json:"date_due,omitempty"` - DatePaid *timestamp.Timestamp `protobuf:"bytes,61,opt,name=date_paid,json=datePaid,proto3" json:"date_paid,omitempty"` - DateUpdated *timestamp.Timestamp `protobuf:"bytes,62,opt,name=date_updated,json=dateUpdated,proto3" json:"date_updated,omitempty"` - DateCreated *timestamp.Timestamp `protobuf:"bytes,63,opt,name=date_created,json=dateCreated,proto3" json:"date_created,omitempty"` - Attachments []*document.BinaryAttachment `protobuf:"bytes,64,rep,name=attachments,proto3" json:"attachments,omitempty"` - LineItems []*LineItem `protobuf:"bytes,65,rep,name=line_items,json=lineItems,proto3" json:"line_items,omitempty"` - PaymentDetails []*document.PaymentDetails `protobuf:"bytes,66,rep,name=payment_details,json=paymentDetails,proto3" json:"payment_details,omitempty"` - TaxItems []*TaxItem `protobuf:"bytes,67,rep,name=tax_items,json=taxItems,proto3" json:"tax_items,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *InvoiceData) Reset() { *m = InvoiceData{} } -func (m *InvoiceData) String() string { return proto.CompactTextString(m) } -func (*InvoiceData) ProtoMessage() {} -func (*InvoiceData) Descriptor() ([]byte, []int) { - return fileDescriptor_a2539fddd216e73e, []int{5} -} - -func (m *InvoiceData) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_InvoiceData.Unmarshal(m, b) -} -func (m *InvoiceData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_InvoiceData.Marshal(b, m, deterministic) -} -func (m *InvoiceData) XXX_Merge(src proto.Message) { - xxx_messageInfo_InvoiceData.Merge(m, src) -} -func (m *InvoiceData) XXX_Size() int { - return xxx_messageInfo_InvoiceData.Size(m) -} -func (m *InvoiceData) XXX_DiscardUnknown() { - xxx_messageInfo_InvoiceData.DiscardUnknown(m) -} - -var xxx_messageInfo_InvoiceData proto.InternalMessageInfo - -func (m *InvoiceData) GetNumber() string { - if m != nil { - return m.Number - } - return "" -} - -func (m *InvoiceData) GetStatus() string { - if m != nil { - return m.Status - } - return "" -} - -func (m *InvoiceData) GetSenderInvoiceId() string { - if m != nil { - return m.SenderInvoiceId - } - return "" -} - -func (m *InvoiceData) GetRecipientInvoiceId() string { - if m != nil { - return m.RecipientInvoiceId - } - return "" -} - -func (m *InvoiceData) GetSenderCompanyName() string { - if m != nil { - return m.SenderCompanyName - } - return "" -} - -func (m *InvoiceData) GetSenderContactPersonName() string { - if m != nil { - return m.SenderContactPersonName - } - return "" -} - -func (m *InvoiceData) GetSenderStreet1() string { - if m != nil { - return m.SenderStreet1 - } - return "" -} - -func (m *InvoiceData) GetSenderStreet2() string { - if m != nil { - return m.SenderStreet2 - } - return "" -} - -func (m *InvoiceData) GetSenderCity() string { - if m != nil { - return m.SenderCity - } - return "" -} - -func (m *InvoiceData) GetSenderZipcode() string { - if m != nil { - return m.SenderZipcode - } - return "" -} - -func (m *InvoiceData) GetSenderState() string { - if m != nil { - return m.SenderState - } - return "" -} - -func (m *InvoiceData) GetSenderCountry() string { - if m != nil { - return m.SenderCountry - } - return "" -} - -func (m *InvoiceData) GetBillToCompanyName() string { - if m != nil { - return m.BillToCompanyName - } - return "" -} - -func (m *InvoiceData) GetBillToContactPersonName() string { - if m != nil { - return m.BillToContactPersonName - } - return "" -} - -func (m *InvoiceData) GetBillToStreet1() string { - if m != nil { - return m.BillToStreet1 - } - return "" -} - -func (m *InvoiceData) GetBillToStreet2() string { - if m != nil { - return m.BillToStreet2 - } - return "" -} - -func (m *InvoiceData) GetBillToCity() string { - if m != nil { - return m.BillToCity - } - return "" -} - -func (m *InvoiceData) GetBillToZipcode() string { - if m != nil { - return m.BillToZipcode - } - return "" -} - -func (m *InvoiceData) GetBillToState() string { - if m != nil { - return m.BillToState - } - return "" -} - -func (m *InvoiceData) GetBillToCountry() string { - if m != nil { - return m.BillToCountry - } - return "" -} - -func (m *InvoiceData) GetBillToVatNumber() string { - if m != nil { - return m.BillToVatNumber - } - return "" -} - -func (m *InvoiceData) GetBillToLocalTaxId() string { - if m != nil { - return m.BillToLocalTaxId - } - return "" -} - -func (m *InvoiceData) GetRemitToCompanyName() string { - if m != nil { - return m.RemitToCompanyName - } - return "" -} - -func (m *InvoiceData) GetRemitToContactPersonName() string { - if m != nil { - return m.RemitToContactPersonName - } - return "" -} - -func (m *InvoiceData) GetRemitToStreet1() string { - if m != nil { - return m.RemitToStreet1 - } - return "" -} - -func (m *InvoiceData) GetRemitToStreet2() string { - if m != nil { - return m.RemitToStreet2 - } - return "" -} - -func (m *InvoiceData) GetRemitToCity() string { - if m != nil { - return m.RemitToCity - } - return "" -} - -func (m *InvoiceData) GetRemitToZipcode() string { - if m != nil { - return m.RemitToZipcode - } - return "" -} - -func (m *InvoiceData) GetRemitToState() string { - if m != nil { - return m.RemitToState - } - return "" -} - -func (m *InvoiceData) GetRemitToCountry() string { - if m != nil { - return m.RemitToCountry - } - return "" -} - -func (m *InvoiceData) GetRemitToVatNumber() string { - if m != nil { - return m.RemitToVatNumber - } - return "" -} - -func (m *InvoiceData) GetRemitToLocalTaxId() string { - if m != nil { - return m.RemitToLocalTaxId - } - return "" -} - -func (m *InvoiceData) GetRemitToTaxCountry() string { - if m != nil { - return m.RemitToTaxCountry - } - return "" -} - -func (m *InvoiceData) GetShipToCompanyName() string { - if m != nil { - return m.ShipToCompanyName - } - return "" -} - -func (m *InvoiceData) GetShipToContactPersonName() string { - if m != nil { - return m.ShipToContactPersonName - } - return "" -} - -func (m *InvoiceData) GetShipToStreet1() string { - if m != nil { - return m.ShipToStreet1 - } - return "" -} - -func (m *InvoiceData) GetShipToStreet2() string { - if m != nil { - return m.ShipToStreet2 - } - return "" -} - -func (m *InvoiceData) GetShipToCity() string { - if m != nil { - return m.ShipToCity - } - return "" -} - -func (m *InvoiceData) GetShipToZipcode() string { - if m != nil { - return m.ShipToZipcode - } - return "" -} - -func (m *InvoiceData) GetShipToState() string { - if m != nil { - return m.ShipToState - } - return "" -} - -func (m *InvoiceData) GetShipToCountry() string { - if m != nil { - return m.ShipToCountry - } - return "" -} - -func (m *InvoiceData) GetCurrency() string { - if m != nil { - return m.Currency - } - return "" -} - -func (m *InvoiceData) GetGrossAmount() string { - if m != nil { - return m.GrossAmount - } - return "" -} - -func (m *InvoiceData) GetNetAmount() string { - if m != nil { - return m.NetAmount - } - return "" -} - -func (m *InvoiceData) GetTaxAmount() string { - if m != nil { - return m.TaxAmount - } - return "" -} - -func (m *InvoiceData) GetTaxRate() string { - if m != nil { - return m.TaxRate - } - return "" -} - -func (m *InvoiceData) GetTaxOnLineLevel() bool { - if m != nil { - return m.TaxOnLineLevel - } - return false -} - -func (m *InvoiceData) GetRecipient() string { - if m != nil { - return m.Recipient - } - return "" -} - -func (m *InvoiceData) GetSender() string { - if m != nil { - return m.Sender - } - return "" -} - -func (m *InvoiceData) GetPayee() string { - if m != nil { - return m.Payee - } - return "" -} - -func (m *InvoiceData) GetComment() string { - if m != nil { - return m.Comment - } - return "" -} - -func (m *InvoiceData) GetShippingTerms() string { - if m != nil { - return m.ShippingTerms - } - return "" -} - -func (m *InvoiceData) GetRequesterEmail() string { - if m != nil { - return m.RequesterEmail - } - return "" -} - -func (m *InvoiceData) GetRequesterName() string { - if m != nil { - return m.RequesterName - } - return "" -} - -func (m *InvoiceData) GetDeliveryNumber() string { - if m != nil { - return m.DeliveryNumber - } - return "" -} - -func (m *InvoiceData) GetIsCreditNote() bool { - if m != nil { - return m.IsCreditNote - } - return false -} - -func (m *InvoiceData) GetCreditNoteInvoiceNumber() string { - if m != nil { - return m.CreditNoteInvoiceNumber - } - return "" -} - -func (m *InvoiceData) GetCreditForInvoiceDate() *timestamp.Timestamp { - if m != nil { - return m.CreditForInvoiceDate - } - return nil -} - -func (m *InvoiceData) GetDateDue() *timestamp.Timestamp { - if m != nil { - return m.DateDue - } - return nil -} - -func (m *InvoiceData) GetDatePaid() *timestamp.Timestamp { - if m != nil { - return m.DatePaid - } - return nil -} - -func (m *InvoiceData) GetDateUpdated() *timestamp.Timestamp { - if m != nil { - return m.DateUpdated - } - return nil -} - -func (m *InvoiceData) GetDateCreated() *timestamp.Timestamp { - if m != nil { - return m.DateCreated - } - return nil -} - -func (m *InvoiceData) GetAttachments() []*document.BinaryAttachment { - if m != nil { - return m.Attachments - } - return nil -} - -func (m *InvoiceData) GetLineItems() []*LineItem { - if m != nil { - return m.LineItems - } - return nil -} - -func (m *InvoiceData) GetPaymentDetails() []*document.PaymentDetails { - if m != nil { - return m.PaymentDetails - } - return nil -} - -func (m *InvoiceData) GetTaxItems() []*TaxItem { - if m != nil { - return m.TaxItems - } - return nil -} - -type TaxItem struct { - ItemNumber string `protobuf:"bytes,1,opt,name=item_number,json=itemNumber,proto3" json:"item_number,omitempty"` - InvoiceItemNumber string `protobuf:"bytes,2,opt,name=invoice_item_number,json=invoiceItemNumber,proto3" json:"invoice_item_number,omitempty"` - TaxAmount string `protobuf:"bytes,3,opt,name=tax_amount,json=taxAmount,proto3" json:"tax_amount,omitempty"` - TaxRate string `protobuf:"bytes,4,opt,name=tax_rate,json=taxRate,proto3" json:"tax_rate,omitempty"` - TaxCode string `protobuf:"bytes,5,opt,name=tax_code,json=taxCode,proto3" json:"tax_code,omitempty"` - TaxBaseAmount string `protobuf:"bytes,6,opt,name=tax_base_amount,json=taxBaseAmount,proto3" json:"tax_base_amount,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *TaxItem) Reset() { *m = TaxItem{} } -func (m *TaxItem) String() string { return proto.CompactTextString(m) } -func (*TaxItem) ProtoMessage() {} -func (*TaxItem) Descriptor() ([]byte, []int) { - return fileDescriptor_a2539fddd216e73e, []int{6} -} - -func (m *TaxItem) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_TaxItem.Unmarshal(m, b) -} -func (m *TaxItem) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_TaxItem.Marshal(b, m, deterministic) -} -func (m *TaxItem) XXX_Merge(src proto.Message) { - xxx_messageInfo_TaxItem.Merge(m, src) -} -func (m *TaxItem) XXX_Size() int { - return xxx_messageInfo_TaxItem.Size(m) -} -func (m *TaxItem) XXX_DiscardUnknown() { - xxx_messageInfo_TaxItem.DiscardUnknown(m) -} - -var xxx_messageInfo_TaxItem proto.InternalMessageInfo - -func (m *TaxItem) GetItemNumber() string { - if m != nil { - return m.ItemNumber - } - return "" -} - -func (m *TaxItem) GetInvoiceItemNumber() string { - if m != nil { - return m.InvoiceItemNumber - } - return "" -} - -func (m *TaxItem) GetTaxAmount() string { - if m != nil { - return m.TaxAmount - } - return "" -} - -func (m *TaxItem) GetTaxRate() string { - if m != nil { - return m.TaxRate - } - return "" -} - -func (m *TaxItem) GetTaxCode() string { - if m != nil { - return m.TaxCode - } - return "" -} - -func (m *TaxItem) GetTaxBaseAmount() string { - if m != nil { - return m.TaxBaseAmount - } - return "" -} - -type LineItem struct { - ItemNumber string `protobuf:"bytes,1,opt,name=item_number,json=itemNumber,proto3" json:"item_number,omitempty"` - Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` - SenderPartNo string `protobuf:"bytes,3,opt,name=sender_part_no,json=senderPartNo,proto3" json:"sender_part_no,omitempty"` - PricePerUnit string `protobuf:"bytes,4,opt,name=price_per_unit,json=pricePerUnit,proto3" json:"price_per_unit,omitempty"` - Quantity string `protobuf:"bytes,5,opt,name=quantity,proto3" json:"quantity,omitempty"` - UnitOfMeasure string `protobuf:"bytes,6,opt,name=unit_of_measure,json=unitOfMeasure,proto3" json:"unit_of_measure,omitempty"` - NetWeight string `protobuf:"bytes,7,opt,name=net_weight,json=netWeight,proto3" json:"net_weight,omitempty"` - TaxAmount string `protobuf:"bytes,8,opt,name=tax_amount,json=taxAmount,proto3" json:"tax_amount,omitempty"` - TaxRate string `protobuf:"bytes,9,opt,name=tax_rate,json=taxRate,proto3" json:"tax_rate,omitempty"` - TaxCode string `protobuf:"bytes,10,opt,name=tax_code,json=taxCode,proto3" json:"tax_code,omitempty"` - //the total amount of the line item - TotalAmount string `protobuf:"bytes,11,opt,name=total_amount,json=totalAmount,proto3" json:"total_amount,omitempty"` - PurchaseOrderNumber string `protobuf:"bytes,12,opt,name=purchase_order_number,json=purchaseOrderNumber,proto3" json:"purchase_order_number,omitempty"` - PurchaseOrderItemNumber string `protobuf:"bytes,13,opt,name=purchase_order_item_number,json=purchaseOrderItemNumber,proto3" json:"purchase_order_item_number,omitempty"` - DeliveryNoteNumber string `protobuf:"bytes,14,opt,name=delivery_note_number,json=deliveryNoteNumber,proto3" json:"delivery_note_number,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *LineItem) Reset() { *m = LineItem{} } -func (m *LineItem) String() string { return proto.CompactTextString(m) } -func (*LineItem) ProtoMessage() {} -func (*LineItem) Descriptor() ([]byte, []int) { - return fileDescriptor_a2539fddd216e73e, []int{7} -} - -func (m *LineItem) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_LineItem.Unmarshal(m, b) -} -func (m *LineItem) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_LineItem.Marshal(b, m, deterministic) -} -func (m *LineItem) XXX_Merge(src proto.Message) { - xxx_messageInfo_LineItem.Merge(m, src) -} -func (m *LineItem) XXX_Size() int { - return xxx_messageInfo_LineItem.Size(m) -} -func (m *LineItem) XXX_DiscardUnknown() { - xxx_messageInfo_LineItem.DiscardUnknown(m) -} - -var xxx_messageInfo_LineItem proto.InternalMessageInfo - -func (m *LineItem) GetItemNumber() string { - if m != nil { - return m.ItemNumber - } - return "" -} - -func (m *LineItem) GetDescription() string { - if m != nil { - return m.Description - } - return "" -} - -func (m *LineItem) GetSenderPartNo() string { - if m != nil { - return m.SenderPartNo - } - return "" -} - -func (m *LineItem) GetPricePerUnit() string { - if m != nil { - return m.PricePerUnit - } - return "" -} - -func (m *LineItem) GetQuantity() string { - if m != nil { - return m.Quantity - } - return "" -} - -func (m *LineItem) GetUnitOfMeasure() string { - if m != nil { - return m.UnitOfMeasure - } - return "" -} - -func (m *LineItem) GetNetWeight() string { - if m != nil { - return m.NetWeight - } - return "" -} - -func (m *LineItem) GetTaxAmount() string { - if m != nil { - return m.TaxAmount - } - return "" -} - -func (m *LineItem) GetTaxRate() string { - if m != nil { - return m.TaxRate - } - return "" -} - -func (m *LineItem) GetTaxCode() string { - if m != nil { - return m.TaxCode - } - return "" -} - -func (m *LineItem) GetTotalAmount() string { - if m != nil { - return m.TotalAmount - } - return "" -} - -func (m *LineItem) GetPurchaseOrderNumber() string { - if m != nil { - return m.PurchaseOrderNumber - } - return "" -} - -func (m *LineItem) GetPurchaseOrderItemNumber() string { - if m != nil { - return m.PurchaseOrderItemNumber - } - return "" -} - -func (m *LineItem) GetDeliveryNoteNumber() string { - if m != nil { - return m.DeliveryNoteNumber - } - return "" -} - -func init() { - proto.RegisterType((*GetRequest)(nil), "inv.GetRequest") - proto.RegisterType((*GetVersionRequest)(nil), "inv.GetVersionRequest") - proto.RegisterType((*InvoiceCreatePayload)(nil), "inv.InvoiceCreatePayload") - proto.RegisterMapType((map[string]*document.Attribute)(nil), "inv.InvoiceCreatePayload.AttributesEntry") - proto.RegisterType((*InvoiceUpdatePayload)(nil), "inv.InvoiceUpdatePayload") - proto.RegisterMapType((map[string]*document.Attribute)(nil), "inv.InvoiceUpdatePayload.AttributesEntry") - proto.RegisterType((*InvoiceResponse)(nil), "inv.InvoiceResponse") - proto.RegisterMapType((map[string]*document.Attribute)(nil), "inv.InvoiceResponse.AttributesEntry") - proto.RegisterType((*InvoiceData)(nil), "inv.InvoiceData") - proto.RegisterType((*TaxItem)(nil), "inv.TaxItem") - proto.RegisterType((*LineItem)(nil), "inv.LineItem") -} - -func init() { proto.RegisterFile("invoice/service.proto", fileDescriptor_a2539fddd216e73e) } - -var fileDescriptor_a2539fddd216e73e = []byte{ - // 1955 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0xcd, 0x72, 0x1b, 0xc7, - 0x11, 0x2e, 0x10, 0x24, 0x45, 0x0e, 0x40, 0x82, 0x1c, 0x42, 0xd4, 0x08, 0x66, 0x22, 0x08, 0xa1, - 0x25, 0x52, 0x16, 0x01, 0x0a, 0x8e, 0x63, 0x47, 0x92, 0x95, 0x80, 0x94, 0xa3, 0xb0, 0x4a, 0x91, - 0x18, 0x88, 0x76, 0xaa, 0x7c, 0xd9, 0x1a, 0xee, 0x36, 0xc9, 0xa9, 0x00, 0xbb, 0xeb, 0xd9, 0x01, - 0x44, 0xc4, 0xe5, 0x8b, 0xf3, 0x06, 0xca, 0x21, 0xa7, 0x54, 0x9e, 0x20, 0xef, 0x90, 0x77, 0xc8, - 0x21, 0x2f, 0x90, 0x07, 0x49, 0xf5, 0xfc, 0xec, 0x0f, 0x21, 0x92, 0xf6, 0xc5, 0x27, 0x72, 0xbb, - 0xbf, 0xaf, 0x7b, 0xf6, 0xeb, 0xed, 0xde, 0xc6, 0x92, 0x9b, 0x22, 0x1c, 0x47, 0xc2, 0x87, 0x4e, - 0x02, 0x72, 0x2c, 0x7c, 0x68, 0xc7, 0x32, 0x52, 0x11, 0x2d, 0x8b, 0x70, 0xdc, 0x58, 0x0f, 0x22, - 0x7f, 0x34, 0x84, 0x50, 0x15, 0x9d, 0x8d, 0x8d, 0xd3, 0x28, 0x3a, 0x1d, 0x40, 0x87, 0xc7, 0xa2, - 0xc3, 0xc3, 0x30, 0x52, 0x5c, 0x89, 0x28, 0x4c, 0xac, 0xf7, 0x8e, 0xf5, 0xea, 0xab, 0xe3, 0xd1, - 0x49, 0x47, 0x89, 0x21, 0x24, 0x8a, 0x0f, 0x63, 0x0b, 0x78, 0xa8, 0xff, 0xf8, 0x3b, 0xa7, 0x10, - 0xee, 0x24, 0x6f, 0xf9, 0xe9, 0x29, 0xc8, 0x4e, 0x14, 0xeb, 0x10, 0xd3, 0xe1, 0x5a, 0x3b, 0x84, - 0xbc, 0x00, 0xd5, 0x87, 0x6f, 0x46, 0x90, 0x28, 0x7a, 0x87, 0x54, 0xdc, 0xa1, 0x3c, 0x11, 0xb0, - 0x52, 0xb3, 0xb4, 0xb5, 0xd8, 0x27, 0xce, 0x74, 0x10, 0xb4, 0xde, 0x90, 0xd5, 0x17, 0xa0, 0xbe, - 0x02, 0x99, 0x88, 0x28, 0xfc, 0xa1, 0x2c, 0xfa, 0x33, 0x42, 0xc6, 0x86, 0x82, 0xfe, 0x19, 0xed, - 0x5f, 0xb4, 0x96, 0x83, 0xa0, 0xf5, 0xcf, 0x19, 0x52, 0x3f, 0x30, 0x3a, 0xed, 0x4b, 0xe0, 0x0a, - 0x0e, 0xf9, 0x64, 0x10, 0xf1, 0x00, 0x03, 0x4b, 0xe0, 0x81, 0xc7, 0x7d, 0x1f, 0x92, 0x84, 0x95, - 0x9a, 0x65, 0x0c, 0x8c, 0xa6, 0x9e, 0xb6, 0xd0, 0xbb, 0xa4, 0xfa, 0x56, 0x0a, 0x05, 0x0e, 0x31, - 0xa3, 0x11, 0x15, 0x6d, 0xb3, 0x90, 0x4d, 0x32, 0x1b, 0x70, 0xc5, 0x59, 0xb9, 0x59, 0xda, 0xaa, - 0x74, 0x57, 0xda, 0x22, 0x1c, 0xb7, 0x6d, 0xb2, 0xe7, 0x5c, 0xf1, 0xbe, 0xf6, 0xd2, 0x03, 0x42, - 0xb8, 0x52, 0x52, 0x1c, 0x8f, 0x14, 0x24, 0x6c, 0xb6, 0x59, 0xde, 0xaa, 0x74, 0xb7, 0xf3, 0xd8, - 0xc2, 0xc1, 0xda, 0xbd, 0x14, 0xfb, 0x45, 0xa8, 0xe4, 0xa4, 0x9f, 0x23, 0x37, 0xfa, 0xa4, 0x76, - 0xc1, 0x4d, 0x57, 0x48, 0xf9, 0xcf, 0x30, 0xb1, 0xc2, 0xe0, 0xbf, 0x74, 0x9b, 0xcc, 0x8d, 0xf9, - 0x60, 0x04, 0x5a, 0x8c, 0x4a, 0x77, 0xad, 0xed, 0xd4, 0xca, 0x42, 0xf7, 0x0d, 0xe2, 0xf1, 0xcc, - 0x67, 0xa5, 0xd6, 0xbf, 0x33, 0x85, 0xbe, 0x8c, 0x83, 0xa2, 0x42, 0x57, 0x4b, 0x7f, 0x41, 0xc2, - 0x99, 0x6b, 0x25, 0x2c, 0x5f, 0x2e, 0xe1, 0xec, 0x8f, 0x90, 0x70, 0x6e, 0x5a, 0xc2, 0xc2, 0xc9, - 0x7f, 0x72, 0x09, 0xff, 0x3a, 0x43, 0x6a, 0xf6, 0x20, 0x7d, 0x48, 0xe2, 0x28, 0x4c, 0x80, 0xee, - 0x92, 0xf9, 0x33, 0xe0, 0x01, 0x48, 0x1d, 0xb7, 0xd2, 0x65, 0x59, 0x0c, 0x87, 0xf9, 0xbd, 0xf6, - 0xf7, 0x2d, 0x2e, 0x95, 0x62, 0xe6, 0x4a, 0x29, 0x9e, 0x17, 0xa4, 0x28, 0x6b, 0x29, 0x36, 0xf3, - 0x58, 0x17, 0xfd, 0x27, 0x57, 0xe1, 0x5f, 0xeb, 0xa4, 0x92, 0x3b, 0x2f, 0x5d, 0x27, 0xf3, 0xe1, - 0x68, 0x78, 0x6c, 0x15, 0x58, 0xec, 0xdb, 0x2b, 0xb4, 0x27, 0x8a, 0xab, 0x51, 0x62, 0xbb, 0xd5, - 0x5e, 0xd1, 0x07, 0x64, 0x35, 0x81, 0x30, 0x00, 0xe9, 0xd9, 0xc1, 0x86, 0x4f, 0x5d, 0x59, 0x43, - 0x6a, 0xc6, 0x61, 0xa3, 0x1f, 0x04, 0x74, 0x97, 0xd4, 0x25, 0xf8, 0x22, 0x16, 0xfa, 0xe1, 0xcc, - 0xe0, 0xb3, 0x1a, 0x4e, 0x53, 0x5f, 0xc6, 0x68, 0x93, 0x35, 0x1b, 0xdd, 0x8f, 0x86, 0x31, 0x0f, - 0x27, 0x5e, 0xc8, 0x87, 0xc0, 0xe6, 0x34, 0xc1, 0x26, 0xde, 0x37, 0x9e, 0x57, 0x7c, 0x08, 0xf4, - 0x09, 0x69, 0xa4, 0xf8, 0x50, 0x71, 0x5f, 0x79, 0x31, 0xc8, 0x24, 0x0a, 0x0d, 0x6d, 0x5e, 0xd3, - 0x6e, 0x39, 0x9a, 0x06, 0x1c, 0x6a, 0xbf, 0x26, 0x7f, 0x48, 0x96, 0x2d, 0x39, 0x51, 0x12, 0x40, - 0x3d, 0x62, 0x37, 0x34, 0x61, 0xc9, 0x58, 0xdf, 0x18, 0xe3, 0x14, 0xac, 0xcb, 0x16, 0xa6, 0x61, - 0x5d, 0xec, 0x33, 0x77, 0x14, 0xa1, 0x26, 0x6c, 0xd1, 0x34, 0xa2, 0xcd, 0x2d, 0xd4, 0x24, 0x17, - 0xe7, 0x2f, 0x22, 0xf6, 0xa3, 0x00, 0x18, 0xc9, 0xc7, 0xf9, 0xda, 0x18, 0xb1, 0x1d, 0xd3, 0x74, - 0x5c, 0x01, 0xab, 0x68, 0x50, 0xc5, 0x25, 0xe3, 0x2a, 0x7f, 0x70, 0x3f, 0x1a, 0xe1, 0x63, 0xc1, - 0xaa, 0xf9, 0x48, 0xfb, 0xc6, 0x48, 0x3b, 0xa4, 0x7e, 0x2c, 0x06, 0x03, 0x4f, 0x45, 0x45, 0x35, - 0x3f, 0x32, 0x6a, 0xa2, 0xef, 0x28, 0xca, 0xab, 0xf9, 0x94, 0x7c, 0x90, 0x11, 0xa6, 0xe5, 0x7c, - 0x68, 0xe4, 0x74, 0xbc, 0x8b, 0x72, 0xde, 0x23, 0x35, 0xc7, 0x76, 0x7a, 0xd6, 0xcc, 0xb1, 0x0c, - 0xc3, 0xe9, 0x39, 0x85, 0xeb, 0xb2, 0x95, 0x69, 0x5c, 0x97, 0x36, 0x49, 0x35, 0x3d, 0x0d, 0x2a, - 0xba, 0x6a, 0x14, 0xb5, 0xe9, 0x51, 0xd1, 0x5c, 0x24, 0x27, 0x29, 0xcd, 0x47, 0x72, 0x92, 0xb6, - 0xc8, 0x52, 0x96, 0x11, 0x35, 0xed, 0x1a, 0x4d, 0x5d, 0x3e, 0xd4, 0x34, 0x17, 0xcb, 0x89, 0x5a, - 0xcf, 0xc7, 0x72, 0xa2, 0x7e, 0x44, 0xa8, 0xc3, 0x8d, 0xb9, 0xf2, 0x6c, 0xef, 0xdc, 0x34, 0x0d, - 0x60, 0xa0, 0x5f, 0x71, 0xf5, 0xca, 0x34, 0x51, 0x3b, 0xab, 0xc0, 0x20, 0xf2, 0xf9, 0xc0, 0x53, - 0xfc, 0x1c, 0x1b, 0xe0, 0xa9, 0x86, 0xaf, 0x18, 0xf8, 0x4b, 0xf4, 0x1c, 0xf1, 0xf3, 0x83, 0x80, - 0x3e, 0x22, 0x37, 0x25, 0x0c, 0x85, 0x9a, 0x2a, 0xd9, 0x2d, 0xd7, 0x31, 0x43, 0xa1, 0x8a, 0x35, - 0x7b, 0x46, 0x36, 0x72, 0x94, 0xe9, 0xa2, 0x31, 0xcd, 0x64, 0x29, 0xf3, 0x62, 0xd5, 0xb6, 0xc8, - 0x4a, 0xca, 0x77, 0x65, 0xbb, 0xad, 0x39, 0xcb, 0x96, 0xe3, 0xea, 0x36, 0x8d, 0xec, 0xb2, 0xc6, - 0x7b, 0x90, 0x5d, 0xd4, 0x3b, 0x3b, 0x13, 0x96, 0xee, 0x03, 0xa3, 0xb7, 0x3b, 0x04, 0xd6, 0x2e, - 0x1f, 0xcd, 0x15, 0x6f, 0xa3, 0x10, 0xcd, 0x55, 0x6f, 0x93, 0x2c, 0xe7, 0xf2, 0x62, 0xf9, 0x7e, - 0xae, 0x71, 0xd5, 0x34, 0x2b, 0xd6, 0x2f, 0x1f, 0xcf, 0x15, 0xf0, 0x4e, 0x21, 0x9e, 0xab, 0xe0, - 0x0e, 0x59, 0x4b, 0x91, 0xb9, 0x12, 0x36, 0x4d, 0x4d, 0x2c, 0x38, 0xab, 0xe1, 0x6e, 0xae, 0x26, - 0x85, 0x22, 0xde, 0x35, 0x6d, 0x64, 0x09, 0xb9, 0x2a, 0x76, 0x70, 0xec, 0x59, 0x06, 0x62, 0xdd, - 0x71, 0x5a, 0x05, 0xc2, 0x11, 0x3f, 0xcf, 0x35, 0x6a, 0x72, 0x26, 0xe2, 0xa9, 0xaa, 0xff, 0xc2, - 0x8e, 0xbd, 0x33, 0x11, 0x4f, 0x35, 0x6a, 0x46, 0x98, 0xae, 0xf9, 0xa6, 0x9d, 0x7b, 0x96, 0xf7, - 0x9e, 0x46, 0x75, 0x6c, 0x57, 0xf1, 0x0f, 0xed, 0xfc, 0xd0, 0x8c, 0x5c, 0xa3, 0x16, 0x71, 0x5d, - 0x76, 0x6f, 0x1a, 0xa7, 0x1b, 0x35, 0x3d, 0x0d, 0x56, 0xfb, 0xbe, 0x1d, 0x7d, 0x26, 0xbd, 0x6d, - 0x54, 0x87, 0x70, 0xb5, 0xde, 0xca, 0x47, 0xca, 0x35, 0x6a, 0x96, 0x11, 0x2b, 0xbd, 0x6d, 0x87, - 0x9f, 0xcd, 0x67, 0x1b, 0x35, 0xbb, 0x77, 0x23, 0xec, 0x83, 0x7c, 0x2c, 0x27, 0x6a, 0x83, 0x2c, - 0xf8, 0x23, 0x29, 0x21, 0xf4, 0x27, 0x6c, 0x49, 0x03, 0xd2, 0x6b, 0x9c, 0xb1, 0xa7, 0x32, 0x4a, - 0x12, 0x8f, 0x0f, 0x11, 0xcd, 0x96, 0x4d, 0x1a, 0x6d, 0xeb, 0x69, 0x13, 0x6e, 0xac, 0x21, 0x28, - 0x07, 0xd8, 0x31, 0x1b, 0x6b, 0x08, 0x2a, 0x73, 0x63, 0x69, 0xad, 0xbb, 0x6d, 0xdc, 0x8a, 0x9f, - 0x5b, 0xf7, 0x6d, 0xb2, 0x80, 0x6e, 0x89, 0xf7, 0xd0, 0xd1, 0xce, 0x1b, 0x8a, 0x9f, 0xf7, 0xf1, - 0xfc, 0xdb, 0x64, 0x15, 0x5d, 0x51, 0xe8, 0x0d, 0x44, 0x08, 0xde, 0x00, 0xc6, 0x30, 0x60, 0xbb, - 0xcd, 0xd2, 0xd6, 0x42, 0x7f, 0x59, 0xf1, 0xf3, 0xd7, 0xe1, 0x4b, 0x11, 0xc2, 0x4b, 0xb4, 0xd2, - 0x0d, 0xb2, 0x98, 0xbe, 0x23, 0xd9, 0x23, 0x93, 0x23, 0x35, 0xe8, 0x37, 0xb4, 0x9e, 0xf7, 0x6c, - 0xcd, 0xbe, 0xa1, 0xf5, 0x15, 0xad, 0x93, 0xb9, 0x98, 0x4f, 0x00, 0xd8, 0xc7, 0xda, 0x6c, 0x2e, - 0x28, 0x23, 0x37, 0xfc, 0x68, 0x88, 0x7b, 0x01, 0xfb, 0xa5, 0x39, 0x90, 0xbd, 0xd4, 0x6f, 0x93, - 0x33, 0x11, 0xc7, 0x22, 0x3c, 0xf5, 0x14, 0xc8, 0x61, 0xc2, 0x3e, 0xc9, 0xf4, 0x44, 0xeb, 0x11, - 0x1a, 0xe9, 0x7d, 0x52, 0x93, 0x66, 0xdd, 0x07, 0xe9, 0xc1, 0x90, 0x8b, 0x01, 0xfb, 0x95, 0xeb, - 0x2f, 0x6b, 0xfe, 0x02, 0xad, 0x18, 0x2f, 0x03, 0xea, 0xe7, 0xf1, 0x53, 0x13, 0x2f, 0xb5, 0xea, - 0xa7, 0xf0, 0x3e, 0xa9, 0x05, 0x30, 0x10, 0x63, 0x90, 0x13, 0xd7, 0x82, 0x9f, 0x99, 0x78, 0xce, - 0x6c, 0x1b, 0x70, 0x93, 0x2c, 0x8b, 0xc4, 0xf3, 0x25, 0x04, 0x42, 0x79, 0x61, 0xa4, 0x80, 0xfd, - 0x5a, 0xab, 0x55, 0x15, 0xc9, 0xbe, 0x36, 0xbe, 0x8a, 0x94, 0xde, 0x04, 0x72, 0x90, 0x74, 0xdb, - 0xb0, 0x91, 0x1f, 0x9b, 0x8e, 0xf0, 0x53, 0xbc, 0x5d, 0x39, 0x6c, 0x8a, 0x3f, 0x12, 0xeb, 0xf2, - 0x4e, 0xa2, 0x6c, 0xb1, 0xc1, 0x65, 0x95, 0x3d, 0xd1, 0x5b, 0x55, 0xa3, 0x6d, 0x7e, 0x74, 0xb5, - 0xdd, 0x8f, 0xae, 0xf6, 0x91, 0xfb, 0xd1, 0xd5, 0xaf, 0x1b, 0xea, 0xef, 0x22, 0x99, 0xed, 0x55, - 0x40, 0x3f, 0x21, 0x0b, 0xc8, 0xf7, 0x82, 0x11, 0xb0, 0xf5, 0x6b, 0x63, 0xdc, 0x40, 0xec, 0xf3, - 0x11, 0xd0, 0x4f, 0xc9, 0xa2, 0xa6, 0xc5, 0x5c, 0x04, 0xec, 0xf3, 0x6b, 0x79, 0x0b, 0x66, 0xa1, - 0x16, 0x01, 0xfd, 0x9c, 0x54, 0x35, 0x71, 0xa4, 0x77, 0xec, 0x80, 0x3d, 0xbb, 0x96, 0x5b, 0x41, - 0xa0, 0x59, 0xc9, 0x33, 0xba, 0xaf, 0x7f, 0xe5, 0x04, 0xec, 0x37, 0x3f, 0x8c, 0x6e, 0x7e, 0x14, - 0x05, 0xf4, 0x29, 0xa9, 0x70, 0xa5, 0xb8, 0x7f, 0x86, 0x4f, 0x54, 0xc2, 0x7e, 0xab, 0x17, 0xde, - 0x46, 0xb6, 0x8a, 0xee, 0x89, 0x90, 0xcb, 0x49, 0x2f, 0x85, 0xf4, 0xf3, 0x70, 0xfa, 0x90, 0x10, - 0xdd, 0x0b, 0x42, 0xc1, 0x30, 0x61, 0x3d, 0x4d, 0x5e, 0xd2, 0xdb, 0x32, 0xf6, 0xc2, 0x81, 0x82, - 0x61, 0x7f, 0x71, 0x60, 0xff, 0x4b, 0x68, 0x8f, 0xd4, 0x62, 0x3e, 0xd1, 0x3f, 0x78, 0x02, 0x50, - 0x5c, 0x0c, 0x12, 0xb6, 0xa7, 0x29, 0xb9, 0xe5, 0xfd, 0xd0, 0x00, 0x9e, 0x1b, 0x7f, 0x7f, 0x39, - 0x2e, 0x5c, 0xd3, 0x6d, 0xb2, 0xa8, 0x87, 0xb8, 0xce, 0xb7, 0xaf, 0xc9, 0x55, 0x9d, 0x0f, 0x07, - 0x38, 0xa6, 0xc3, 0xee, 0xd5, 0xd9, 0x5a, 0xff, 0x2d, 0x91, 0x1b, 0xd6, 0x8a, 0x2b, 0x1e, 0x52, - 0xbc, 0xc2, 0xc2, 0x4c, 0xd0, 0x94, 0xbe, 0xef, 0xd7, 0xd2, 0x35, 0x37, 0x07, 0x34, 0x1b, 0xf4, - 0xaa, 0x75, 0x1d, 0x64, 0xf8, 0xe2, 0x14, 0x29, 0x5f, 0x35, 0x45, 0x66, 0x8b, 0x53, 0xc4, 0xba, - 0xf4, 0x28, 0x9d, 0x4b, 0x5d, 0xfb, 0x38, 0x44, 0xef, 0x91, 0x1a, 0xba, 0x8e, 0x79, 0x02, 0x2e, - 0xb2, 0x59, 0x84, 0x97, 0x14, 0x3f, 0xdf, 0xe3, 0x09, 0x98, 0xe8, 0xad, 0xbf, 0xcf, 0x92, 0x05, - 0xa7, 0xef, 0xf5, 0xb7, 0xd6, 0x24, 0x95, 0x00, 0x12, 0x5f, 0x0a, 0xfd, 0x21, 0xc1, 0xde, 0x52, - 0xde, 0x84, 0x7d, 0x6a, 0xb7, 0xd2, 0x98, 0x4b, 0x6c, 0x43, 0x7b, 0x43, 0x76, 0x9d, 0x3d, 0xe4, - 0x52, 0xbd, 0x8a, 0x10, 0x15, 0x4b, 0x14, 0x28, 0x06, 0xe9, 0x8d, 0x42, 0xa1, 0xec, 0x9d, 0x55, - 0xb5, 0xf5, 0x10, 0xe4, 0x97, 0xa1, 0x50, 0x38, 0xbc, 0xbf, 0x19, 0xf1, 0x50, 0xe1, 0xeb, 0xc4, - 0xdc, 0x5e, 0x7a, 0x8d, 0xf7, 0x87, 0x3c, 0x2f, 0x3a, 0xf1, 0x86, 0xc0, 0x93, 0x91, 0x74, 0x8b, - 0xfe, 0x12, 0x9a, 0x5f, 0x9f, 0xfc, 0xc1, 0x18, 0xdd, 0x04, 0x7f, 0x0b, 0xe2, 0xf4, 0x4c, 0xd9, - 0xd5, 0x1e, 0x27, 0xf8, 0x9f, 0xb4, 0xe1, 0x82, 0xf6, 0x0b, 0x57, 0x69, 0xbf, 0x78, 0xb9, 0xf6, - 0xa4, 0xa8, 0xfd, 0x5d, 0x52, 0x55, 0x91, 0xe2, 0x03, 0x17, 0xd6, 0x2e, 0xef, 0xda, 0x66, 0x03, - 0x77, 0xc9, 0xcd, 0x78, 0x24, 0xfd, 0x33, 0x2c, 0x4f, 0x24, 0x51, 0x2e, 0xab, 0xb9, 0xd9, 0xe1, - 0xd7, 0x9c, 0xf3, 0x35, 0xfa, 0xac, 0xf8, 0x4f, 0x48, 0xe3, 0x02, 0x27, 0x5f, 0x2c, 0xf3, 0x76, - 0xbb, 0x55, 0x20, 0xe6, 0x1e, 0xb2, 0x5d, 0x52, 0xcf, 0x06, 0x2d, 0xce, 0x46, 0x4b, 0x33, 0x2f, - 0x3d, 0x9a, 0x4e, 0xdb, 0x48, 0xd9, 0x71, 0xd8, 0xfd, 0x7e, 0x96, 0x2c, 0xdb, 0x59, 0xf6, 0xc6, - 0x7c, 0x98, 0xa2, 0x82, 0xcc, 0x9b, 0x5e, 0xa7, 0xb7, 0x2f, 0xfd, 0x28, 0xd2, 0xa8, 0xbf, 0xef, - 0x17, 0x6e, 0xab, 0xfd, 0xae, 0x57, 0x6f, 0x50, 0x83, 0x4c, 0x9a, 0x3c, 0x6c, 0xda, 0x3e, 0xf8, - 0xfe, 0x3f, 0xff, 0xfb, 0xdb, 0xcc, 0x6a, 0xab, 0xda, 0x19, 0x3f, 0xea, 0x58, 0x53, 0xf2, 0xb8, - 0xf4, 0x80, 0xbe, 0x25, 0xf3, 0x66, 0x2a, 0x15, 0x53, 0x15, 0x3e, 0x1e, 0x5c, 0x92, 0xea, 0xa9, - 0x4e, 0x65, 0x90, 0x53, 0xa9, 0xee, 0x34, 0x1a, 0xf9, 0x54, 0x9d, 0x6f, 0x73, 0xdf, 0x4d, 0xbe, - 0xc3, 0xc4, 0xff, 0x28, 0xe9, 0x4f, 0x61, 0xf6, 0xdb, 0x16, 0x5d, 0xd7, 0x29, 0xa6, 0x3e, 0x76, - 0x5d, 0x92, 0x9a, 0xbf, 0xeb, 0xdd, 0x6b, 0x6c, 0xbe, 0x00, 0xd5, 0xe4, 0xcd, 0x24, 0x06, 0x5f, - 0x9c, 0x08, 0xbf, 0x69, 0xbf, 0x70, 0x35, 0xa3, 0x93, 0x8b, 0x87, 0xe9, 0xd2, 0xdd, 0xcb, 0x0f, - 0xd3, 0xb1, 0xbc, 0xa4, 0xf3, 0x6d, 0xf6, 0xd5, 0xec, 0x3b, 0x3a, 0x20, 0xe5, 0x17, 0xa0, 0x68, - 0xcd, 0x9d, 0xeb, 0xea, 0x03, 0x3d, 0x7b, 0xd7, 0xbb, 0xdd, 0xb8, 0x85, 0x07, 0x52, 0x67, 0xd0, - 0x34, 0xab, 0x8f, 0x2a, 0x9c, 0x61, 0x83, 0x5e, 0x21, 0xc8, 0x5e, 0x53, 0x2f, 0x0c, 0x18, 0x7a, - 0xaf, 0x6a, 0x9f, 0x82, 0x43, 0x7c, 0x0b, 0x1c, 0x96, 0xbe, 0x9e, 0x13, 0xe1, 0x38, 0x3e, 0x3e, - 0x9e, 0xd7, 0x6f, 0x85, 0x8f, 0xff, 0x1f, 0x00, 0x00, 0xff, 0xff, 0x2d, 0xf0, 0x6d, 0x41, 0xe3, - 0x14, 0x00, 0x00, -} - -// Reference imports to suppress errors if they are not otherwise used. -var _ context.Context -var _ grpc.ClientConn - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion4 - -// InvoiceServiceClient is the client API for InvoiceService service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. -type InvoiceServiceClient interface { - Create(ctx context.Context, in *InvoiceCreatePayload, opts ...grpc.CallOption) (*InvoiceResponse, error) - Update(ctx context.Context, in *InvoiceUpdatePayload, opts ...grpc.CallOption) (*InvoiceResponse, error) - GetVersion(ctx context.Context, in *GetVersionRequest, opts ...grpc.CallOption) (*InvoiceResponse, error) - Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*InvoiceResponse, error) -} - -type invoiceServiceClient struct { - cc *grpc.ClientConn -} - -func NewInvoiceServiceClient(cc *grpc.ClientConn) InvoiceServiceClient { - return &invoiceServiceClient{cc} -} - -func (c *invoiceServiceClient) Create(ctx context.Context, in *InvoiceCreatePayload, opts ...grpc.CallOption) (*InvoiceResponse, error) { - out := new(InvoiceResponse) - err := c.cc.Invoke(ctx, "/inv.InvoiceService/Create", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *invoiceServiceClient) Update(ctx context.Context, in *InvoiceUpdatePayload, opts ...grpc.CallOption) (*InvoiceResponse, error) { - out := new(InvoiceResponse) - err := c.cc.Invoke(ctx, "/inv.InvoiceService/Update", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *invoiceServiceClient) GetVersion(ctx context.Context, in *GetVersionRequest, opts ...grpc.CallOption) (*InvoiceResponse, error) { - out := new(InvoiceResponse) - err := c.cc.Invoke(ctx, "/inv.InvoiceService/GetVersion", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *invoiceServiceClient) Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*InvoiceResponse, error) { - out := new(InvoiceResponse) - err := c.cc.Invoke(ctx, "/inv.InvoiceService/Get", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// InvoiceServiceServer is the server API for InvoiceService service. -type InvoiceServiceServer interface { - Create(context.Context, *InvoiceCreatePayload) (*InvoiceResponse, error) - Update(context.Context, *InvoiceUpdatePayload) (*InvoiceResponse, error) - GetVersion(context.Context, *GetVersionRequest) (*InvoiceResponse, error) - Get(context.Context, *GetRequest) (*InvoiceResponse, error) -} - -func RegisterInvoiceServiceServer(s *grpc.Server, srv InvoiceServiceServer) { - s.RegisterService(&_InvoiceService_serviceDesc, srv) -} - -func _InvoiceService_Create_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(InvoiceCreatePayload) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(InvoiceServiceServer).Create(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/inv.InvoiceService/Create", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(InvoiceServiceServer).Create(ctx, req.(*InvoiceCreatePayload)) - } - return interceptor(ctx, in, info, handler) -} - -func _InvoiceService_Update_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(InvoiceUpdatePayload) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(InvoiceServiceServer).Update(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/inv.InvoiceService/Update", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(InvoiceServiceServer).Update(ctx, req.(*InvoiceUpdatePayload)) - } - return interceptor(ctx, in, info, handler) -} - -func _InvoiceService_GetVersion_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetVersionRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(InvoiceServiceServer).GetVersion(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/inv.InvoiceService/GetVersion", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(InvoiceServiceServer).GetVersion(ctx, req.(*GetVersionRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _InvoiceService_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(InvoiceServiceServer).Get(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/inv.InvoiceService/Get", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(InvoiceServiceServer).Get(ctx, req.(*GetRequest)) - } - return interceptor(ctx, in, info, handler) -} - -var _InvoiceService_serviceDesc = grpc.ServiceDesc{ - ServiceName: "inv.InvoiceService", - HandlerType: (*InvoiceServiceServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "Create", - Handler: _InvoiceService_Create_Handler, - }, - { - MethodName: "Update", - Handler: _InvoiceService_Update_Handler, - }, - { - MethodName: "GetVersion", - Handler: _InvoiceService_GetVersion_Handler, - }, - { - MethodName: "Get", - Handler: _InvoiceService_Get_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "invoice/service.proto", -} diff --git a/protobufs/gen/go/invoice/service.pb.gw.go b/protobufs/gen/go/invoice/service.pb.gw.go deleted file mode 100644 index cae6325bb..000000000 --- a/protobufs/gen/go/invoice/service.pb.gw.go +++ /dev/null @@ -1,315 +0,0 @@ -// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. -// source: invoice/service.proto - -/* -Package invpb is a reverse proxy. - -It translates gRPC into RESTful JSON APIs. -*/ -package invpb - -import ( - "io" - "net/http" - - "github.com/golang/protobuf/proto" - "github.com/grpc-ecosystem/grpc-gateway/runtime" - "github.com/grpc-ecosystem/grpc-gateway/utilities" - "golang.org/x/net/context" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/grpclog" - "google.golang.org/grpc/status" -) - -var _ codes.Code -var _ io.Reader -var _ status.Status -var _ = runtime.String -var _ = utilities.NewDoubleArray - -func request_InvoiceService_Create_0(ctx context.Context, marshaler runtime.Marshaler, client InvoiceServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq InvoiceCreatePayload - var metadata runtime.ServerMetadata - - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - msg, err := client.Create(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func request_InvoiceService_Update_0(ctx context.Context, marshaler runtime.Marshaler, client InvoiceServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq InvoiceUpdatePayload - var metadata runtime.ServerMetadata - - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["document_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "document_id") - } - - protoReq.DocumentId, err = runtime.String(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "document_id", err) - } - - msg, err := client.Update(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func request_InvoiceService_GetVersion_0(ctx context.Context, marshaler runtime.Marshaler, client InvoiceServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq GetVersionRequest - var metadata runtime.ServerMetadata - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["document_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "document_id") - } - - protoReq.DocumentId, err = runtime.String(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "document_id", err) - } - - val, ok = pathParams["version_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "version_id") - } - - protoReq.VersionId, err = runtime.String(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "version_id", err) - } - - msg, err := client.GetVersion(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func request_InvoiceService_Get_0(ctx context.Context, marshaler runtime.Marshaler, client InvoiceServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq GetRequest - var metadata runtime.ServerMetadata - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["document_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "document_id") - } - - protoReq.DocumentId, err = runtime.String(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "document_id", err) - } - - msg, err := client.Get(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -// RegisterInvoiceServiceHandlerFromEndpoint is same as RegisterInvoiceServiceHandler but -// automatically dials to "endpoint" and closes the connection when "ctx" gets done. -func RegisterInvoiceServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { - conn, err := grpc.Dial(endpoint, opts...) - if err != nil { - return err - } - defer func() { - if err != nil { - if cerr := conn.Close(); cerr != nil { - grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) - } - return - } - go func() { - <-ctx.Done() - if cerr := conn.Close(); cerr != nil { - grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) - } - }() - }() - - return RegisterInvoiceServiceHandler(ctx, mux, conn) -} - -// RegisterInvoiceServiceHandler registers the http handlers for service InvoiceService to "mux". -// The handlers forward requests to the grpc endpoint over "conn". -func RegisterInvoiceServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { - return RegisterInvoiceServiceHandlerClient(ctx, mux, NewInvoiceServiceClient(conn)) -} - -// RegisterInvoiceServiceHandlerClient registers the http handlers for service InvoiceService -// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "InvoiceServiceClient". -// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "InvoiceServiceClient" -// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in -// "InvoiceServiceClient" to call the correct interceptors. -func RegisterInvoiceServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client InvoiceServiceClient) error { - - mux.Handle("POST", pattern_InvoiceService_Create_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - if cn, ok := w.(http.CloseNotifier); ok { - go func(done <-chan struct{}, closed <-chan bool) { - select { - case <-done: - case <-closed: - cancel() - } - }(ctx.Done(), cn.CloseNotify()) - } - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_InvoiceService_Create_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_InvoiceService_Create_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("PUT", pattern_InvoiceService_Update_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - if cn, ok := w.(http.CloseNotifier); ok { - go func(done <-chan struct{}, closed <-chan bool) { - select { - case <-done: - case <-closed: - cancel() - } - }(ctx.Done(), cn.CloseNotify()) - } - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_InvoiceService_Update_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_InvoiceService_Update_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("GET", pattern_InvoiceService_GetVersion_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - if cn, ok := w.(http.CloseNotifier); ok { - go func(done <-chan struct{}, closed <-chan bool) { - select { - case <-done: - case <-closed: - cancel() - } - }(ctx.Done(), cn.CloseNotify()) - } - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_InvoiceService_GetVersion_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_InvoiceService_GetVersion_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("GET", pattern_InvoiceService_Get_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - if cn, ok := w.(http.CloseNotifier); ok { - go func(done <-chan struct{}, closed <-chan bool) { - select { - case <-done: - case <-closed: - cancel() - } - }(ctx.Done(), cn.CloseNotify()) - } - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_InvoiceService_Get_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_InvoiceService_Get_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - return nil -} - -var ( - pattern_InvoiceService_Create_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "invoices"}, "")) - - pattern_InvoiceService_Update_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "invoices", "document_id"}, "")) - - pattern_InvoiceService_GetVersion_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"v1", "invoices", "document_id", "versions", "version_id"}, "")) - - pattern_InvoiceService_Get_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "invoices", "document_id"}, "")) -) - -var ( - forward_InvoiceService_Create_0 = runtime.ForwardResponseMessage - - forward_InvoiceService_Update_0 = runtime.ForwardResponseMessage - - forward_InvoiceService_GetVersion_0 = runtime.ForwardResponseMessage - - forward_InvoiceService_Get_0 = runtime.ForwardResponseMessage -) diff --git a/protobufs/gen/go/nft/service.pb.go b/protobufs/gen/go/nft/service.pb.go deleted file mode 100644 index ffc097bfb..000000000 --- a/protobufs/gen/go/nft/service.pb.go +++ /dev/null @@ -1,261 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// source: nft/service.proto - -package nftpb - -import ( - context "context" - fmt "fmt" - math "math" - - proto "github.com/golang/protobuf/proto" - _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/options" - _ "google.golang.org/genproto/googleapis/api/annotations" - grpc "google.golang.org/grpc" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package - -type ResponseHeader struct { - JobId string `protobuf:"bytes,5,opt,name=job_id,json=jobId,proto3" json:"job_id,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ResponseHeader) Reset() { *m = ResponseHeader{} } -func (m *ResponseHeader) String() string { return proto.CompactTextString(m) } -func (*ResponseHeader) ProtoMessage() {} -func (*ResponseHeader) Descriptor() ([]byte, []int) { - return fileDescriptor_a63c52875f346f52, []int{0} -} - -func (m *ResponseHeader) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ResponseHeader.Unmarshal(m, b) -} -func (m *ResponseHeader) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ResponseHeader.Marshal(b, m, deterministic) -} -func (m *ResponseHeader) XXX_Merge(src proto.Message) { - xxx_messageInfo_ResponseHeader.Merge(m, src) -} -func (m *ResponseHeader) XXX_Size() int { - return xxx_messageInfo_ResponseHeader.Size(m) -} -func (m *ResponseHeader) XXX_DiscardUnknown() { - xxx_messageInfo_ResponseHeader.DiscardUnknown(m) -} - -var xxx_messageInfo_ResponseHeader proto.InternalMessageInfo - -func (m *ResponseHeader) GetJobId() string { - if m != nil { - return m.JobId - } - return "" -} - -type NFTMintInvoiceUnpaidRequest struct { - // Invoice Document identifier - DocumentId string `protobuf:"bytes,1,opt,name=document_id,json=documentId,proto3" json:"document_id,omitempty"` - // Deposit address for NFT Token created - DepositAddress string `protobuf:"bytes,2,opt,name=deposit_address,json=depositAddress,proto3" json:"deposit_address,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *NFTMintInvoiceUnpaidRequest) Reset() { *m = NFTMintInvoiceUnpaidRequest{} } -func (m *NFTMintInvoiceUnpaidRequest) String() string { return proto.CompactTextString(m) } -func (*NFTMintInvoiceUnpaidRequest) ProtoMessage() {} -func (*NFTMintInvoiceUnpaidRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_a63c52875f346f52, []int{1} -} - -func (m *NFTMintInvoiceUnpaidRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_NFTMintInvoiceUnpaidRequest.Unmarshal(m, b) -} -func (m *NFTMintInvoiceUnpaidRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_NFTMintInvoiceUnpaidRequest.Marshal(b, m, deterministic) -} -func (m *NFTMintInvoiceUnpaidRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_NFTMintInvoiceUnpaidRequest.Merge(m, src) -} -func (m *NFTMintInvoiceUnpaidRequest) XXX_Size() int { - return xxx_messageInfo_NFTMintInvoiceUnpaidRequest.Size(m) -} -func (m *NFTMintInvoiceUnpaidRequest) XXX_DiscardUnknown() { - xxx_messageInfo_NFTMintInvoiceUnpaidRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_NFTMintInvoiceUnpaidRequest proto.InternalMessageInfo - -func (m *NFTMintInvoiceUnpaidRequest) GetDocumentId() string { - if m != nil { - return m.DocumentId - } - return "" -} - -func (m *NFTMintInvoiceUnpaidRequest) GetDepositAddress() string { - if m != nil { - return m.DepositAddress - } - return "" -} - -type NFTMintResponse struct { - Header *ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *NFTMintResponse) Reset() { *m = NFTMintResponse{} } -func (m *NFTMintResponse) String() string { return proto.CompactTextString(m) } -func (*NFTMintResponse) ProtoMessage() {} -func (*NFTMintResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_a63c52875f346f52, []int{2} -} - -func (m *NFTMintResponse) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_NFTMintResponse.Unmarshal(m, b) -} -func (m *NFTMintResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_NFTMintResponse.Marshal(b, m, deterministic) -} -func (m *NFTMintResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_NFTMintResponse.Merge(m, src) -} -func (m *NFTMintResponse) XXX_Size() int { - return xxx_messageInfo_NFTMintResponse.Size(m) -} -func (m *NFTMintResponse) XXX_DiscardUnknown() { - xxx_messageInfo_NFTMintResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_NFTMintResponse proto.InternalMessageInfo - -func (m *NFTMintResponse) GetHeader() *ResponseHeader { - if m != nil { - return m.Header - } - return nil -} - -func init() { - proto.RegisterType((*ResponseHeader)(nil), "nft.ResponseHeader") - proto.RegisterType((*NFTMintInvoiceUnpaidRequest)(nil), "nft.NFTMintInvoiceUnpaidRequest") - proto.RegisterType((*NFTMintResponse)(nil), "nft.NFTMintResponse") -} - -func init() { proto.RegisterFile("nft/service.proto", fileDescriptor_a63c52875f346f52) } - -var fileDescriptor_a63c52875f346f52 = []byte{ - // 379 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x91, 0xc1, 0xaa, 0xd3, 0x40, - 0x14, 0x86, 0xc9, 0x95, 0x56, 0x9c, 0x2b, 0xf7, 0x62, 0xbc, 0x42, 0x89, 0x82, 0x43, 0x16, 0x56, - 0xac, 0xed, 0x68, 0xdd, 0xb9, 0x10, 0x5a, 0x21, 0xd8, 0x85, 0xa1, 0xc4, 0xb8, 0x71, 0x53, 0x26, - 0x99, 0x93, 0x38, 0xc5, 0x9e, 0x13, 0x33, 0x93, 0xba, 0x10, 0x37, 0x3e, 0x82, 0xbe, 0x8b, 0x6f, - 0xe0, 0x13, 0xf8, 0x0a, 0x3e, 0x88, 0x64, 0x92, 0x42, 0xa5, 0x70, 0x57, 0xc3, 0xfc, 0x7c, 0xe7, - 0x3f, 0xff, 0x39, 0x87, 0xdd, 0xc1, 0xc2, 0x0a, 0x03, 0xf5, 0x5e, 0xe7, 0x30, 0xab, 0x6a, 0xb2, - 0xe4, 0xdf, 0xc0, 0xc2, 0x06, 0x0f, 0x4a, 0xa2, 0xf2, 0x13, 0x08, 0x59, 0x69, 0x21, 0x11, 0xc9, - 0x4a, 0xab, 0x09, 0x4d, 0x87, 0x04, 0x4f, 0xdd, 0x93, 0x4f, 0x4b, 0xc0, 0xa9, 0xf9, 0x22, 0xcb, - 0x12, 0x6a, 0x41, 0x95, 0x23, 0x4e, 0xe9, 0x70, 0xcc, 0x2e, 0x12, 0x30, 0x15, 0xa1, 0x81, 0x37, - 0x20, 0x15, 0xd4, 0xfe, 0x3d, 0x36, 0xdc, 0x52, 0xb6, 0xd1, 0x6a, 0x34, 0xe0, 0xde, 0xe3, 0x5b, - 0xc9, 0x60, 0x4b, 0xd9, 0x4a, 0x85, 0x25, 0xbb, 0x1f, 0x47, 0xe9, 0x5b, 0x8d, 0x76, 0x85, 0x7b, - 0xd2, 0x39, 0xbc, 0xc7, 0x4a, 0x6a, 0x95, 0xc0, 0xe7, 0x06, 0x8c, 0xf5, 0x1f, 0xb2, 0x73, 0x45, - 0x79, 0xb3, 0x03, 0xb4, 0x6d, 0xa9, 0xe7, 0x4a, 0xd9, 0x41, 0x5a, 0x29, 0x7f, 0xcc, 0x2e, 0x15, - 0x54, 0x64, 0xb4, 0xdd, 0x48, 0xa5, 0x6a, 0x30, 0x66, 0x74, 0xe6, 0xa0, 0x8b, 0x5e, 0x5e, 0x74, - 0x6a, 0xf8, 0x8a, 0x5d, 0xf6, 0x8d, 0x0e, 0xc1, 0xfc, 0x09, 0x1b, 0x7e, 0x74, 0xe1, 0x9c, 0xef, - 0xf9, 0xfc, 0xee, 0x0c, 0x0b, 0x3b, 0xfb, 0x3f, 0x77, 0xd2, 0x23, 0xf3, 0xdf, 0x1e, 0x63, 0x71, - 0x94, 0xbe, 0xeb, 0xf6, 0xe6, 0xff, 0xf2, 0xd8, 0xd5, 0x49, 0xea, 0x38, 0x4a, 0x7d, 0xee, 0x4c, - 0xae, 0x99, 0x29, 0xb8, 0x3a, 0x26, 0x0e, 0xdd, 0xc2, 0xe2, 0xc7, 0x62, 0x1e, 0x3c, 0x6b, 0x25, - 0xc3, 0x25, 0xf2, 0x38, 0x4a, 0x39, 0x35, 0x96, 0x53, 0xd1, 0xfe, 0x3a, 0x03, 0xfe, 0x1a, 0xd0, - 0xd6, 0xba, 0x68, 0x4a, 0xe0, 0xbd, 0xf3, 0xf7, 0x3f, 0x7f, 0x7f, 0x9e, 0x4d, 0xc2, 0x47, 0x62, - 0xff, 0x5c, 0xe8, 0x4e, 0x32, 0xe2, 0xeb, 0xd1, 0xca, 0xbe, 0x89, 0x9d, 0x46, 0x2b, 0x1a, 0x67, - 0xf0, 0xd2, 0x7b, 0xb2, 0xe4, 0xec, 0x66, 0x4e, 0xbb, 0x36, 0xc2, 0xf2, 0x76, 0x3f, 0xcc, 0xba, - 0x3d, 0xd9, 0xda, 0xfb, 0x30, 0xc0, 0xc2, 0x56, 0x59, 0x36, 0x74, 0x27, 0x7c, 0xf1, 0x2f, 0x00, - 0x00, 0xff, 0xff, 0x41, 0x8b, 0x88, 0x87, 0x28, 0x02, 0x00, 0x00, -} - -// Reference imports to suppress errors if they are not otherwise used. -var _ context.Context -var _ grpc.ClientConn - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion4 - -// NFTServiceClient is the client API for NFTService service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. -type NFTServiceClient interface { - MintInvoiceUnpaidNFT(ctx context.Context, in *NFTMintInvoiceUnpaidRequest, opts ...grpc.CallOption) (*NFTMintResponse, error) -} - -type nFTServiceClient struct { - cc *grpc.ClientConn -} - -func NewNFTServiceClient(cc *grpc.ClientConn) NFTServiceClient { - return &nFTServiceClient{cc} -} - -func (c *nFTServiceClient) MintInvoiceUnpaidNFT(ctx context.Context, in *NFTMintInvoiceUnpaidRequest, opts ...grpc.CallOption) (*NFTMintResponse, error) { - out := new(NFTMintResponse) - err := c.cc.Invoke(ctx, "/nft.NFTService/MintInvoiceUnpaidNFT", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// NFTServiceServer is the server API for NFTService service. -type NFTServiceServer interface { - MintInvoiceUnpaidNFT(context.Context, *NFTMintInvoiceUnpaidRequest) (*NFTMintResponse, error) -} - -func RegisterNFTServiceServer(s *grpc.Server, srv NFTServiceServer) { - s.RegisterService(&_NFTService_serviceDesc, srv) -} - -func _NFTService_MintInvoiceUnpaidNFT_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(NFTMintInvoiceUnpaidRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(NFTServiceServer).MintInvoiceUnpaidNFT(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/nft.NFTService/MintInvoiceUnpaidNFT", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(NFTServiceServer).MintInvoiceUnpaidNFT(ctx, req.(*NFTMintInvoiceUnpaidRequest)) - } - return interceptor(ctx, in, info, handler) -} - -var _NFTService_serviceDesc = grpc.ServiceDesc{ - ServiceName: "nft.NFTService", - HandlerType: (*NFTServiceServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "MintInvoiceUnpaidNFT", - Handler: _NFTService_MintInvoiceUnpaidNFT_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "nft/service.proto", -} diff --git a/protobufs/gen/go/nft/service.pb.gw.go b/protobufs/gen/go/nft/service.pb.gw.go deleted file mode 100644 index 898c632a7..000000000 --- a/protobufs/gen/go/nft/service.pb.gw.go +++ /dev/null @@ -1,138 +0,0 @@ -// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. -// source: nft/service.proto - -/* -Package nftpb is a reverse proxy. - -It translates gRPC into RESTful JSON APIs. -*/ -package nftpb - -import ( - "io" - "net/http" - - "github.com/golang/protobuf/proto" - "github.com/grpc-ecosystem/grpc-gateway/runtime" - "github.com/grpc-ecosystem/grpc-gateway/utilities" - "golang.org/x/net/context" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/grpclog" - "google.golang.org/grpc/status" -) - -var _ codes.Code -var _ io.Reader -var _ status.Status -var _ = runtime.String -var _ = utilities.NewDoubleArray - -func request_NFTService_MintInvoiceUnpaidNFT_0(ctx context.Context, marshaler runtime.Marshaler, client NFTServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq NFTMintInvoiceUnpaidRequest - var metadata runtime.ServerMetadata - - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["document_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "document_id") - } - - protoReq.DocumentId, err = runtime.String(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "document_id", err) - } - - msg, err := client.MintInvoiceUnpaidNFT(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -// RegisterNFTServiceHandlerFromEndpoint is same as RegisterNFTServiceHandler but -// automatically dials to "endpoint" and closes the connection when "ctx" gets done. -func RegisterNFTServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { - conn, err := grpc.Dial(endpoint, opts...) - if err != nil { - return err - } - defer func() { - if err != nil { - if cerr := conn.Close(); cerr != nil { - grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) - } - return - } - go func() { - <-ctx.Done() - if cerr := conn.Close(); cerr != nil { - grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) - } - }() - }() - - return RegisterNFTServiceHandler(ctx, mux, conn) -} - -// RegisterNFTServiceHandler registers the http handlers for service NFTService to "mux". -// The handlers forward requests to the grpc endpoint over "conn". -func RegisterNFTServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { - return RegisterNFTServiceHandlerClient(ctx, mux, NewNFTServiceClient(conn)) -} - -// RegisterNFTServiceHandlerClient registers the http handlers for service NFTService -// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "NFTServiceClient". -// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "NFTServiceClient" -// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in -// "NFTServiceClient" to call the correct interceptors. -func RegisterNFTServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client NFTServiceClient) error { - - mux.Handle("POST", pattern_NFTService_MintInvoiceUnpaidNFT_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - if cn, ok := w.(http.CloseNotifier); ok { - go func(done <-chan struct{}, closed <-chan bool) { - select { - case <-done: - case <-closed: - cancel() - } - }(ctx.Done(), cn.CloseNotify()) - } - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_NFTService_MintInvoiceUnpaidNFT_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_NFTService_MintInvoiceUnpaidNFT_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - return nil -} - -var ( - pattern_NFTService_MintInvoiceUnpaidNFT_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3, 2, 4}, []string{"v1", "invoices", "document_id", "mint", "unpaid"}, "")) -) - -var ( - forward_NFTService_MintInvoiceUnpaidNFT_0 = runtime.ForwardResponseMessage -) diff --git a/protobufs/gen/go/notification/service.pb.go b/protobufs/gen/go/notification/service.pb.go deleted file mode 100644 index 23e599e8a..000000000 --- a/protobufs/gen/go/notification/service.pb.go +++ /dev/null @@ -1,121 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// source: notification/service.proto - -package notificationpb - -import ( - context "context" - fmt "fmt" - math "math" - - notification "github.com/centrifuge/centrifuge-protobufs/gen/go/notification" - proto "github.com/golang/protobuf/proto" - empty "github.com/golang/protobuf/ptypes/empty" - _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/options" - _ "google.golang.org/genproto/googleapis/api/annotations" - grpc "google.golang.org/grpc" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package - -func init() { proto.RegisterFile("notification/service.proto", fileDescriptor_fa081ff6329b33e2) } - -var fileDescriptor_fa081ff6329b33e2 = []byte{ - // 240 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0xca, 0xcb, 0x2f, 0xc9, - 0x4c, 0xcb, 0x4c, 0x4e, 0x2c, 0xc9, 0xcc, 0xcf, 0xd3, 0x2f, 0x4e, 0x2d, 0x2a, 0xcb, 0x4c, 0x4e, - 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x41, 0x96, 0x93, 0x92, 0x49, 0xcf, 0xcf, 0x4f, - 0xcf, 0x49, 0xd5, 0x4f, 0x2c, 0xc8, 0xd4, 0x4f, 0xcc, 0xcb, 0xcb, 0x2f, 0x01, 0x0b, 0x17, 0x43, - 0xd4, 0x4a, 0x49, 0x43, 0x65, 0xc1, 0xbc, 0xa4, 0xd2, 0x34, 0xfd, 0xd4, 0xdc, 0x82, 0x92, 0x4a, - 0xa8, 0xa4, 0x3c, 0x8a, 0x25, 0xc8, 0x1c, 0xa8, 0x02, 0x1d, 0x30, 0x95, 0xac, 0x9b, 0x9e, 0x9a, - 0xa7, 0x5b, 0x5c, 0x9e, 0x98, 0x9e, 0x9e, 0x5a, 0xa4, 0x9f, 0x5f, 0x00, 0x36, 0x1f, 0xd3, 0x2e, - 0xa3, 0x7e, 0x46, 0x2e, 0x09, 0x3f, 0x24, 0x43, 0x5c, 0x4a, 0x73, 0x73, 0x2b, 0x83, 0x21, 0x4e, - 0x17, 0x2a, 0xe6, 0x62, 0x03, 0xcb, 0x55, 0x0a, 0x89, 0xe9, 0x41, 0xdc, 0xa4, 0x07, 0x73, 0x93, - 0x9e, 0x2b, 0xc8, 0x4d, 0x52, 0x8a, 0x7a, 0x28, 0x2e, 0x40, 0x36, 0xc9, 0x37, 0xb5, 0xb8, 0x38, - 0x31, 0x3d, 0x55, 0x49, 0x6f, 0x92, 0xa3, 0xac, 0x94, 0x34, 0xd8, 0x5c, 0x05, 0x64, 0xc5, 0x0a, - 0xa9, 0x79, 0x29, 0x05, 0xf9, 0x99, 0x79, 0x25, 0x4d, 0x97, 0x9f, 0x4c, 0x66, 0xe2, 0x10, 0x62, - 0xd3, 0x4f, 0x01, 0xa9, 0x71, 0x32, 0xe2, 0x12, 0x48, 0xce, 0xcf, 0x45, 0x31, 0xd7, 0x89, 0x07, - 0xea, 0xa2, 0x00, 0x90, 0xed, 0x01, 0x8c, 0x51, 0x7c, 0xc8, 0xb2, 0x05, 0x49, 0x49, 0x6c, 0x60, - 0x67, 0x19, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0x03, 0xa0, 0xe2, 0xe4, 0x82, 0x01, 0x00, 0x00, -} - -// Reference imports to suppress errors if they are not otherwise used. -var _ context.Context -var _ grpc.ClientConn - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion4 - -// NotificationDummyServiceClient is the client API for NotificationDummyService service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. -type NotificationDummyServiceClient interface { - Notify(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*notification.NotificationMessage, error) -} - -type notificationDummyServiceClient struct { - cc *grpc.ClientConn -} - -func NewNotificationDummyServiceClient(cc *grpc.ClientConn) NotificationDummyServiceClient { - return ¬ificationDummyServiceClient{cc} -} - -func (c *notificationDummyServiceClient) Notify(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*notification.NotificationMessage, error) { - out := new(notification.NotificationMessage) - err := c.cc.Invoke(ctx, "/notification.NotificationDummyService/Notify", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// NotificationDummyServiceServer is the server API for NotificationDummyService service. -type NotificationDummyServiceServer interface { - Notify(context.Context, *empty.Empty) (*notification.NotificationMessage, error) -} - -func RegisterNotificationDummyServiceServer(s *grpc.Server, srv NotificationDummyServiceServer) { - s.RegisterService(&_NotificationDummyService_serviceDesc, srv) -} - -func _NotificationDummyService_Notify_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(empty.Empty) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(NotificationDummyServiceServer).Notify(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/notification.NotificationDummyService/Notify", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(NotificationDummyServiceServer).Notify(ctx, req.(*empty.Empty)) - } - return interceptor(ctx, in, info, handler) -} - -var _NotificationDummyService_serviceDesc = grpc.ServiceDesc{ - ServiceName: "notification.NotificationDummyService", - HandlerType: (*NotificationDummyServiceServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "Notify", - Handler: _NotificationDummyService_Notify_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "notification/service.proto", -} diff --git a/protobufs/gen/go/notification/service.pb.gw.go b/protobufs/gen/go/notification/service.pb.gw.go deleted file mode 100644 index 3b7560ae2..000000000 --- a/protobufs/gen/go/notification/service.pb.gw.go +++ /dev/null @@ -1,117 +0,0 @@ -// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. -// source: notification/service.proto - -/* -Package notificationpb is a reverse proxy. - -It translates gRPC into RESTful JSON APIs. -*/ -package notificationpb - -import ( - "io" - "net/http" - - "github.com/golang/protobuf/proto" - "github.com/golang/protobuf/ptypes/empty" - "github.com/grpc-ecosystem/grpc-gateway/runtime" - "github.com/grpc-ecosystem/grpc-gateway/utilities" - "golang.org/x/net/context" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/grpclog" - "google.golang.org/grpc/status" -) - -var _ codes.Code -var _ io.Reader -var _ status.Status -var _ = runtime.String -var _ = utilities.NewDoubleArray - -func request_NotificationDummyService_Notify_0(ctx context.Context, marshaler runtime.Marshaler, client NotificationDummyServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq empty.Empty - var metadata runtime.ServerMetadata - - msg, err := client.Notify(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -// RegisterNotificationDummyServiceHandlerFromEndpoint is same as RegisterNotificationDummyServiceHandler but -// automatically dials to "endpoint" and closes the connection when "ctx" gets done. -func RegisterNotificationDummyServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { - conn, err := grpc.Dial(endpoint, opts...) - if err != nil { - return err - } - defer func() { - if err != nil { - if cerr := conn.Close(); cerr != nil { - grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) - } - return - } - go func() { - <-ctx.Done() - if cerr := conn.Close(); cerr != nil { - grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) - } - }() - }() - - return RegisterNotificationDummyServiceHandler(ctx, mux, conn) -} - -// RegisterNotificationDummyServiceHandler registers the http handlers for service NotificationDummyService to "mux". -// The handlers forward requests to the grpc endpoint over "conn". -func RegisterNotificationDummyServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { - return RegisterNotificationDummyServiceHandlerClient(ctx, mux, NewNotificationDummyServiceClient(conn)) -} - -// RegisterNotificationDummyServiceHandlerClient registers the http handlers for service NotificationDummyService -// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "NotificationDummyServiceClient". -// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "NotificationDummyServiceClient" -// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in -// "NotificationDummyServiceClient" to call the correct interceptors. -func RegisterNotificationDummyServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client NotificationDummyServiceClient) error { - - mux.Handle("GET", pattern_NotificationDummyService_Notify_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - if cn, ok := w.(http.CloseNotifier); ok { - go func(done <-chan struct{}, closed <-chan bool) { - select { - case <-done: - case <-closed: - cancel() - } - }(ctx.Done(), cn.CloseNotify()) - } - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_NotificationDummyService_Notify_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_NotificationDummyService_Notify_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - return nil -} - -var ( - pattern_NotificationDummyService_Notify_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0}, []string{"dummy"}, "")) -) - -var ( - forward_NotificationDummyService_Notify_0 = runtime.ForwardResponseMessage -) diff --git a/protobufs/gen/go/protocol/protocol.pb.go b/protobufs/gen/go/protocol/protocol.pb.go deleted file mode 100644 index 1768baf45..000000000 --- a/protobufs/gen/go/protocol/protocol.pb.go +++ /dev/null @@ -1,79 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// source: protocol/protocol.proto - -package protocolpb - -import ( - fmt "fmt" - math "math" - - proto "github.com/golang/protobuf/proto" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package - -type P2PEnvelope struct { - // serialized protobuf for the actual message - Body []byte `protobuf:"bytes,2,opt,name=body,proto3" json:"body,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *P2PEnvelope) Reset() { *m = P2PEnvelope{} } -func (m *P2PEnvelope) String() string { return proto.CompactTextString(m) } -func (*P2PEnvelope) ProtoMessage() {} -func (*P2PEnvelope) Descriptor() ([]byte, []int) { - return fileDescriptor_87968d26f3046c60, []int{0} -} - -func (m *P2PEnvelope) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_P2PEnvelope.Unmarshal(m, b) -} -func (m *P2PEnvelope) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_P2PEnvelope.Marshal(b, m, deterministic) -} -func (m *P2PEnvelope) XXX_Merge(src proto.Message) { - xxx_messageInfo_P2PEnvelope.Merge(m, src) -} -func (m *P2PEnvelope) XXX_Size() int { - return xxx_messageInfo_P2PEnvelope.Size(m) -} -func (m *P2PEnvelope) XXX_DiscardUnknown() { - xxx_messageInfo_P2PEnvelope.DiscardUnknown(m) -} - -var xxx_messageInfo_P2PEnvelope proto.InternalMessageInfo - -func (m *P2PEnvelope) GetBody() []byte { - if m != nil { - return m.Body - } - return nil -} - -func init() { - proto.RegisterType((*P2PEnvelope)(nil), "protocol.P2PEnvelope") -} - -func init() { proto.RegisterFile("protocol/protocol.proto", fileDescriptor_87968d26f3046c60) } - -var fileDescriptor_87968d26f3046c60 = []byte{ - // 104 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x2f, 0x28, 0xca, 0x2f, - 0xc9, 0x4f, 0xce, 0xcf, 0xd1, 0x87, 0x31, 0xf4, 0xc0, 0x0c, 0x21, 0x0e, 0x18, 0x5f, 0x49, 0x91, - 0x8b, 0x3b, 0xc0, 0x28, 0xc0, 0x35, 0xaf, 0x2c, 0x35, 0x27, 0xbf, 0x20, 0x55, 0x48, 0x88, 0x8b, - 0x25, 0x29, 0x3f, 0xa5, 0x52, 0x82, 0x49, 0x81, 0x51, 0x83, 0x27, 0x08, 0xcc, 0x76, 0xd2, 0xe6, - 0xe2, 0x49, 0xce, 0xcf, 0xd5, 0x83, 0x69, 0x71, 0xe2, 0x0d, 0x80, 0xb2, 0xc0, 0x74, 0x00, 0x63, - 0x14, 0x17, 0x4c, 0xaa, 0x20, 0x29, 0x89, 0x0d, 0xcc, 0x36, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, - 0xc7, 0xdc, 0x5f, 0x90, 0x7b, 0x00, 0x00, 0x00, -} diff --git a/protobufs/gen/go/purchaseorder/service.pb.go b/protobufs/gen/go/purchaseorder/service.pb.go deleted file mode 100644 index 88f67c345..000000000 --- a/protobufs/gen/go/purchaseorder/service.pb.go +++ /dev/null @@ -1,1192 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// source: purchaseorder/service.proto - -package popb - -import ( - context "context" - fmt "fmt" - math "math" - - document "github.com/centrifuge/go-centrifuge/protobufs/gen/go/document" - proto "github.com/golang/protobuf/proto" - timestamp "github.com/golang/protobuf/ptypes/timestamp" - _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/options" - _ "google.golang.org/genproto/googleapis/api/annotations" - grpc "google.golang.org/grpc" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package - -type GetRequest struct { - DocumentId string `protobuf:"bytes,1,opt,name=document_id,json=documentId,proto3" json:"document_id,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *GetRequest) Reset() { *m = GetRequest{} } -func (m *GetRequest) String() string { return proto.CompactTextString(m) } -func (*GetRequest) ProtoMessage() {} -func (*GetRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_0cac5437ed905264, []int{0} -} - -func (m *GetRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_GetRequest.Unmarshal(m, b) -} -func (m *GetRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_GetRequest.Marshal(b, m, deterministic) -} -func (m *GetRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetRequest.Merge(m, src) -} -func (m *GetRequest) XXX_Size() int { - return xxx_messageInfo_GetRequest.Size(m) -} -func (m *GetRequest) XXX_DiscardUnknown() { - xxx_messageInfo_GetRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_GetRequest proto.InternalMessageInfo - -func (m *GetRequest) GetDocumentId() string { - if m != nil { - return m.DocumentId - } - return "" -} - -type GetVersionRequest struct { - DocumentId string `protobuf:"bytes,1,opt,name=document_id,json=documentId,proto3" json:"document_id,omitempty"` - VersionId string `protobuf:"bytes,2,opt,name=version_id,json=versionId,proto3" json:"version_id,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *GetVersionRequest) Reset() { *m = GetVersionRequest{} } -func (m *GetVersionRequest) String() string { return proto.CompactTextString(m) } -func (*GetVersionRequest) ProtoMessage() {} -func (*GetVersionRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_0cac5437ed905264, []int{1} -} - -func (m *GetVersionRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_GetVersionRequest.Unmarshal(m, b) -} -func (m *GetVersionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_GetVersionRequest.Marshal(b, m, deterministic) -} -func (m *GetVersionRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetVersionRequest.Merge(m, src) -} -func (m *GetVersionRequest) XXX_Size() int { - return xxx_messageInfo_GetVersionRequest.Size(m) -} -func (m *GetVersionRequest) XXX_DiscardUnknown() { - xxx_messageInfo_GetVersionRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_GetVersionRequest proto.InternalMessageInfo - -func (m *GetVersionRequest) GetDocumentId() string { - if m != nil { - return m.DocumentId - } - return "" -} - -func (m *GetVersionRequest) GetVersionId() string { - if m != nil { - return m.VersionId - } - return "" -} - -type PurchaseOrderCreatePayload struct { - ReadAccess []string `protobuf:"bytes,1,rep,name=read_access,json=readAccess,proto3" json:"read_access,omitempty"` - WriteAccess []string `protobuf:"bytes,2,rep,name=write_access,json=writeAccess,proto3" json:"write_access,omitempty"` - Data *PurchaseOrderData `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` - // custom attributes - Attributes map[string]*document.Attribute `protobuf:"bytes,4,rep,name=attributes,proto3" json:"attributes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *PurchaseOrderCreatePayload) Reset() { *m = PurchaseOrderCreatePayload{} } -func (m *PurchaseOrderCreatePayload) String() string { return proto.CompactTextString(m) } -func (*PurchaseOrderCreatePayload) ProtoMessage() {} -func (*PurchaseOrderCreatePayload) Descriptor() ([]byte, []int) { - return fileDescriptor_0cac5437ed905264, []int{2} -} - -func (m *PurchaseOrderCreatePayload) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_PurchaseOrderCreatePayload.Unmarshal(m, b) -} -func (m *PurchaseOrderCreatePayload) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_PurchaseOrderCreatePayload.Marshal(b, m, deterministic) -} -func (m *PurchaseOrderCreatePayload) XXX_Merge(src proto.Message) { - xxx_messageInfo_PurchaseOrderCreatePayload.Merge(m, src) -} -func (m *PurchaseOrderCreatePayload) XXX_Size() int { - return xxx_messageInfo_PurchaseOrderCreatePayload.Size(m) -} -func (m *PurchaseOrderCreatePayload) XXX_DiscardUnknown() { - xxx_messageInfo_PurchaseOrderCreatePayload.DiscardUnknown(m) -} - -var xxx_messageInfo_PurchaseOrderCreatePayload proto.InternalMessageInfo - -func (m *PurchaseOrderCreatePayload) GetReadAccess() []string { - if m != nil { - return m.ReadAccess - } - return nil -} - -func (m *PurchaseOrderCreatePayload) GetWriteAccess() []string { - if m != nil { - return m.WriteAccess - } - return nil -} - -func (m *PurchaseOrderCreatePayload) GetData() *PurchaseOrderData { - if m != nil { - return m.Data - } - return nil -} - -func (m *PurchaseOrderCreatePayload) GetAttributes() map[string]*document.Attribute { - if m != nil { - return m.Attributes - } - return nil -} - -type PurchaseOrderUpdatePayload struct { - DocumentId string `protobuf:"bytes,1,opt,name=document_id,json=documentId,proto3" json:"document_id,omitempty"` - ReadAccess []string `protobuf:"bytes,2,rep,name=read_access,json=readAccess,proto3" json:"read_access,omitempty"` - WriteAccess []string `protobuf:"bytes,3,rep,name=write_access,json=writeAccess,proto3" json:"write_access,omitempty"` - Data *PurchaseOrderData `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"` - // custom attributes - Attributes map[string]*document.Attribute `protobuf:"bytes,5,rep,name=attributes,proto3" json:"attributes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *PurchaseOrderUpdatePayload) Reset() { *m = PurchaseOrderUpdatePayload{} } -func (m *PurchaseOrderUpdatePayload) String() string { return proto.CompactTextString(m) } -func (*PurchaseOrderUpdatePayload) ProtoMessage() {} -func (*PurchaseOrderUpdatePayload) Descriptor() ([]byte, []int) { - return fileDescriptor_0cac5437ed905264, []int{3} -} - -func (m *PurchaseOrderUpdatePayload) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_PurchaseOrderUpdatePayload.Unmarshal(m, b) -} -func (m *PurchaseOrderUpdatePayload) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_PurchaseOrderUpdatePayload.Marshal(b, m, deterministic) -} -func (m *PurchaseOrderUpdatePayload) XXX_Merge(src proto.Message) { - xxx_messageInfo_PurchaseOrderUpdatePayload.Merge(m, src) -} -func (m *PurchaseOrderUpdatePayload) XXX_Size() int { - return xxx_messageInfo_PurchaseOrderUpdatePayload.Size(m) -} -func (m *PurchaseOrderUpdatePayload) XXX_DiscardUnknown() { - xxx_messageInfo_PurchaseOrderUpdatePayload.DiscardUnknown(m) -} - -var xxx_messageInfo_PurchaseOrderUpdatePayload proto.InternalMessageInfo - -func (m *PurchaseOrderUpdatePayload) GetDocumentId() string { - if m != nil { - return m.DocumentId - } - return "" -} - -func (m *PurchaseOrderUpdatePayload) GetReadAccess() []string { - if m != nil { - return m.ReadAccess - } - return nil -} - -func (m *PurchaseOrderUpdatePayload) GetWriteAccess() []string { - if m != nil { - return m.WriteAccess - } - return nil -} - -func (m *PurchaseOrderUpdatePayload) GetData() *PurchaseOrderData { - if m != nil { - return m.Data - } - return nil -} - -func (m *PurchaseOrderUpdatePayload) GetAttributes() map[string]*document.Attribute { - if m != nil { - return m.Attributes - } - return nil -} - -type PurchaseOrderResponse struct { - Header *document.ResponseHeader `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"` - Data *PurchaseOrderData `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` - // custom attributes - Attributes map[string]*document.Attribute `protobuf:"bytes,3,rep,name=attributes,proto3" json:"attributes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *PurchaseOrderResponse) Reset() { *m = PurchaseOrderResponse{} } -func (m *PurchaseOrderResponse) String() string { return proto.CompactTextString(m) } -func (*PurchaseOrderResponse) ProtoMessage() {} -func (*PurchaseOrderResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_0cac5437ed905264, []int{4} -} - -func (m *PurchaseOrderResponse) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_PurchaseOrderResponse.Unmarshal(m, b) -} -func (m *PurchaseOrderResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_PurchaseOrderResponse.Marshal(b, m, deterministic) -} -func (m *PurchaseOrderResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_PurchaseOrderResponse.Merge(m, src) -} -func (m *PurchaseOrderResponse) XXX_Size() int { - return xxx_messageInfo_PurchaseOrderResponse.Size(m) -} -func (m *PurchaseOrderResponse) XXX_DiscardUnknown() { - xxx_messageInfo_PurchaseOrderResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_PurchaseOrderResponse proto.InternalMessageInfo - -func (m *PurchaseOrderResponse) GetHeader() *document.ResponseHeader { - if m != nil { - return m.Header - } - return nil -} - -func (m *PurchaseOrderResponse) GetData() *PurchaseOrderData { - if m != nil { - return m.Data - } - return nil -} - -func (m *PurchaseOrderResponse) GetAttributes() map[string]*document.Attribute { - if m != nil { - return m.Attributes - } - return nil -} - -type PurchaseOrderData struct { - Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` - // purchase order number or reference number - Number string `protobuf:"bytes,2,opt,name=number,proto3" json:"number,omitempty"` - SenderOrderId string `protobuf:"bytes,3,opt,name=sender_order_id,json=senderOrderId,proto3" json:"sender_order_id,omitempty"` - RecipientOrderId string `protobuf:"bytes,4,opt,name=recipient_order_id,json=recipientOrderId,proto3" json:"recipient_order_id,omitempty"` - RequisitionId string `protobuf:"bytes,5,opt,name=requisition_id,json=requisitionId,proto3" json:"requisition_id,omitempty"` - RequesterName string `protobuf:"bytes,6,opt,name=requester_name,json=requesterName,proto3" json:"requester_name,omitempty"` - RequesterEmail string `protobuf:"bytes,7,opt,name=requester_email,json=requesterEmail,proto3" json:"requester_email,omitempty"` - ShipToCompanyName string `protobuf:"bytes,8,opt,name=ship_to_company_name,json=shipToCompanyName,proto3" json:"ship_to_company_name,omitempty"` - ShipToContactPersonName string `protobuf:"bytes,9,opt,name=ship_to_contact_person_name,json=shipToContactPersonName,proto3" json:"ship_to_contact_person_name,omitempty"` - ShipToStreet1 string `protobuf:"bytes,10,opt,name=ship_to_street1,json=shipToStreet1,proto3" json:"ship_to_street1,omitempty"` - ShipToStreet2 string `protobuf:"bytes,11,opt,name=ship_to_street2,json=shipToStreet2,proto3" json:"ship_to_street2,omitempty"` - ShipToCity string `protobuf:"bytes,12,opt,name=ship_to_city,json=shipToCity,proto3" json:"ship_to_city,omitempty"` - ShipToZipcode string `protobuf:"bytes,13,opt,name=ship_to_zipcode,json=shipToZipcode,proto3" json:"ship_to_zipcode,omitempty"` - ShipToState string `protobuf:"bytes,14,opt,name=ship_to_state,json=shipToState,proto3" json:"ship_to_state,omitempty"` - ShipToCountry string `protobuf:"bytes,15,opt,name=ship_to_country,json=shipToCountry,proto3" json:"ship_to_country,omitempty"` - PaymentTerms string `protobuf:"bytes,16,opt,name=payment_terms,json=paymentTerms,proto3" json:"payment_terms,omitempty"` - Currency string `protobuf:"bytes,17,opt,name=currency,proto3" json:"currency,omitempty"` - TotalAmount string `protobuf:"bytes,18,opt,name=total_amount,json=totalAmount,proto3" json:"total_amount,omitempty"` - // centrifuge ID of the recipient - Recipient string `protobuf:"bytes,19,opt,name=recipient,proto3" json:"recipient,omitempty"` - // centrifuge ID of the sender - Sender string `protobuf:"bytes,20,opt,name=sender,proto3" json:"sender,omitempty"` - Comment string `protobuf:"bytes,21,opt,name=comment,proto3" json:"comment,omitempty"` - DateSent *timestamp.Timestamp `protobuf:"bytes,22,opt,name=date_sent,json=dateSent,proto3" json:"date_sent,omitempty"` - DateConfirmed *timestamp.Timestamp `protobuf:"bytes,23,opt,name=date_confirmed,json=dateConfirmed,proto3" json:"date_confirmed,omitempty"` - DateUpdated *timestamp.Timestamp `protobuf:"bytes,24,opt,name=date_updated,json=dateUpdated,proto3" json:"date_updated,omitempty"` - DateCreated *timestamp.Timestamp `protobuf:"bytes,25,opt,name=date_created,json=dateCreated,proto3" json:"date_created,omitempty"` - Attachments []*document.BinaryAttachment `protobuf:"bytes,26,rep,name=attachments,proto3" json:"attachments,omitempty"` - LineItems []*LineItem `protobuf:"bytes,27,rep,name=line_items,json=lineItems,proto3" json:"line_items,omitempty"` - PaymentDetails []*document.PaymentDetails `protobuf:"bytes,28,rep,name=payment_details,json=paymentDetails,proto3" json:"payment_details,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *PurchaseOrderData) Reset() { *m = PurchaseOrderData{} } -func (m *PurchaseOrderData) String() string { return proto.CompactTextString(m) } -func (*PurchaseOrderData) ProtoMessage() {} -func (*PurchaseOrderData) Descriptor() ([]byte, []int) { - return fileDescriptor_0cac5437ed905264, []int{5} -} - -func (m *PurchaseOrderData) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_PurchaseOrderData.Unmarshal(m, b) -} -func (m *PurchaseOrderData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_PurchaseOrderData.Marshal(b, m, deterministic) -} -func (m *PurchaseOrderData) XXX_Merge(src proto.Message) { - xxx_messageInfo_PurchaseOrderData.Merge(m, src) -} -func (m *PurchaseOrderData) XXX_Size() int { - return xxx_messageInfo_PurchaseOrderData.Size(m) -} -func (m *PurchaseOrderData) XXX_DiscardUnknown() { - xxx_messageInfo_PurchaseOrderData.DiscardUnknown(m) -} - -var xxx_messageInfo_PurchaseOrderData proto.InternalMessageInfo - -func (m *PurchaseOrderData) GetStatus() string { - if m != nil { - return m.Status - } - return "" -} - -func (m *PurchaseOrderData) GetNumber() string { - if m != nil { - return m.Number - } - return "" -} - -func (m *PurchaseOrderData) GetSenderOrderId() string { - if m != nil { - return m.SenderOrderId - } - return "" -} - -func (m *PurchaseOrderData) GetRecipientOrderId() string { - if m != nil { - return m.RecipientOrderId - } - return "" -} - -func (m *PurchaseOrderData) GetRequisitionId() string { - if m != nil { - return m.RequisitionId - } - return "" -} - -func (m *PurchaseOrderData) GetRequesterName() string { - if m != nil { - return m.RequesterName - } - return "" -} - -func (m *PurchaseOrderData) GetRequesterEmail() string { - if m != nil { - return m.RequesterEmail - } - return "" -} - -func (m *PurchaseOrderData) GetShipToCompanyName() string { - if m != nil { - return m.ShipToCompanyName - } - return "" -} - -func (m *PurchaseOrderData) GetShipToContactPersonName() string { - if m != nil { - return m.ShipToContactPersonName - } - return "" -} - -func (m *PurchaseOrderData) GetShipToStreet1() string { - if m != nil { - return m.ShipToStreet1 - } - return "" -} - -func (m *PurchaseOrderData) GetShipToStreet2() string { - if m != nil { - return m.ShipToStreet2 - } - return "" -} - -func (m *PurchaseOrderData) GetShipToCity() string { - if m != nil { - return m.ShipToCity - } - return "" -} - -func (m *PurchaseOrderData) GetShipToZipcode() string { - if m != nil { - return m.ShipToZipcode - } - return "" -} - -func (m *PurchaseOrderData) GetShipToState() string { - if m != nil { - return m.ShipToState - } - return "" -} - -func (m *PurchaseOrderData) GetShipToCountry() string { - if m != nil { - return m.ShipToCountry - } - return "" -} - -func (m *PurchaseOrderData) GetPaymentTerms() string { - if m != nil { - return m.PaymentTerms - } - return "" -} - -func (m *PurchaseOrderData) GetCurrency() string { - if m != nil { - return m.Currency - } - return "" -} - -func (m *PurchaseOrderData) GetTotalAmount() string { - if m != nil { - return m.TotalAmount - } - return "" -} - -func (m *PurchaseOrderData) GetRecipient() string { - if m != nil { - return m.Recipient - } - return "" -} - -func (m *PurchaseOrderData) GetSender() string { - if m != nil { - return m.Sender - } - return "" -} - -func (m *PurchaseOrderData) GetComment() string { - if m != nil { - return m.Comment - } - return "" -} - -func (m *PurchaseOrderData) GetDateSent() *timestamp.Timestamp { - if m != nil { - return m.DateSent - } - return nil -} - -func (m *PurchaseOrderData) GetDateConfirmed() *timestamp.Timestamp { - if m != nil { - return m.DateConfirmed - } - return nil -} - -func (m *PurchaseOrderData) GetDateUpdated() *timestamp.Timestamp { - if m != nil { - return m.DateUpdated - } - return nil -} - -func (m *PurchaseOrderData) GetDateCreated() *timestamp.Timestamp { - if m != nil { - return m.DateCreated - } - return nil -} - -func (m *PurchaseOrderData) GetAttachments() []*document.BinaryAttachment { - if m != nil { - return m.Attachments - } - return nil -} - -func (m *PurchaseOrderData) GetLineItems() []*LineItem { - if m != nil { - return m.LineItems - } - return nil -} - -func (m *PurchaseOrderData) GetPaymentDetails() []*document.PaymentDetails { - if m != nil { - return m.PaymentDetails - } - return nil -} - -type LineItem struct { - Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` - ItemNumber string `protobuf:"bytes,2,opt,name=item_number,json=itemNumber,proto3" json:"item_number,omitempty"` - Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` - AmountInvoiced string `protobuf:"bytes,4,opt,name=amount_invoiced,json=amountInvoiced,proto3" json:"amount_invoiced,omitempty"` - AmountTotal string `protobuf:"bytes,5,opt,name=amount_total,json=amountTotal,proto3" json:"amount_total,omitempty"` - RequisitionNumber string `protobuf:"bytes,6,opt,name=requisition_number,json=requisitionNumber,proto3" json:"requisition_number,omitempty"` - RequisitionItem string `protobuf:"bytes,7,opt,name=requisition_item,json=requisitionItem,proto3" json:"requisition_item,omitempty"` - PartNo string `protobuf:"bytes,8,opt,name=part_no,json=partNo,proto3" json:"part_no,omitempty"` - PricePerUnit string `protobuf:"bytes,9,opt,name=price_per_unit,json=pricePerUnit,proto3" json:"price_per_unit,omitempty"` - UnitOfMeasure string `protobuf:"bytes,10,opt,name=unit_of_measure,json=unitOfMeasure,proto3" json:"unit_of_measure,omitempty"` - Quantity string `protobuf:"bytes,11,opt,name=quantity,proto3" json:"quantity,omitempty"` - ReceivedQuantity string `protobuf:"bytes,12,opt,name=received_quantity,json=receivedQuantity,proto3" json:"received_quantity,omitempty"` - DateUpdated *timestamp.Timestamp `protobuf:"bytes,13,opt,name=date_updated,json=dateUpdated,proto3" json:"date_updated,omitempty"` - DateCreated *timestamp.Timestamp `protobuf:"bytes,14,opt,name=date_created,json=dateCreated,proto3" json:"date_created,omitempty"` - RevisionNumber int64 `protobuf:"varint,15,opt,name=revision_number,json=revisionNumber,proto3" json:"revision_number,omitempty"` - Activities []*LineItemActivity `protobuf:"bytes,16,rep,name=activities,proto3" json:"activities,omitempty"` - TaxItems []*TaxItem `protobuf:"bytes,17,rep,name=tax_items,json=taxItems,proto3" json:"tax_items,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *LineItem) Reset() { *m = LineItem{} } -func (m *LineItem) String() string { return proto.CompactTextString(m) } -func (*LineItem) ProtoMessage() {} -func (*LineItem) Descriptor() ([]byte, []int) { - return fileDescriptor_0cac5437ed905264, []int{6} -} - -func (m *LineItem) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_LineItem.Unmarshal(m, b) -} -func (m *LineItem) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_LineItem.Marshal(b, m, deterministic) -} -func (m *LineItem) XXX_Merge(src proto.Message) { - xxx_messageInfo_LineItem.Merge(m, src) -} -func (m *LineItem) XXX_Size() int { - return xxx_messageInfo_LineItem.Size(m) -} -func (m *LineItem) XXX_DiscardUnknown() { - xxx_messageInfo_LineItem.DiscardUnknown(m) -} - -var xxx_messageInfo_LineItem proto.InternalMessageInfo - -func (m *LineItem) GetStatus() string { - if m != nil { - return m.Status - } - return "" -} - -func (m *LineItem) GetItemNumber() string { - if m != nil { - return m.ItemNumber - } - return "" -} - -func (m *LineItem) GetDescription() string { - if m != nil { - return m.Description - } - return "" -} - -func (m *LineItem) GetAmountInvoiced() string { - if m != nil { - return m.AmountInvoiced - } - return "" -} - -func (m *LineItem) GetAmountTotal() string { - if m != nil { - return m.AmountTotal - } - return "" -} - -func (m *LineItem) GetRequisitionNumber() string { - if m != nil { - return m.RequisitionNumber - } - return "" -} - -func (m *LineItem) GetRequisitionItem() string { - if m != nil { - return m.RequisitionItem - } - return "" -} - -func (m *LineItem) GetPartNo() string { - if m != nil { - return m.PartNo - } - return "" -} - -func (m *LineItem) GetPricePerUnit() string { - if m != nil { - return m.PricePerUnit - } - return "" -} - -func (m *LineItem) GetUnitOfMeasure() string { - if m != nil { - return m.UnitOfMeasure - } - return "" -} - -func (m *LineItem) GetQuantity() string { - if m != nil { - return m.Quantity - } - return "" -} - -func (m *LineItem) GetReceivedQuantity() string { - if m != nil { - return m.ReceivedQuantity - } - return "" -} - -func (m *LineItem) GetDateUpdated() *timestamp.Timestamp { - if m != nil { - return m.DateUpdated - } - return nil -} - -func (m *LineItem) GetDateCreated() *timestamp.Timestamp { - if m != nil { - return m.DateCreated - } - return nil -} - -func (m *LineItem) GetRevisionNumber() int64 { - if m != nil { - return m.RevisionNumber - } - return 0 -} - -func (m *LineItem) GetActivities() []*LineItemActivity { - if m != nil { - return m.Activities - } - return nil -} - -func (m *LineItem) GetTaxItems() []*TaxItem { - if m != nil { - return m.TaxItems - } - return nil -} - -type TaxItem struct { - ItemNumber string `protobuf:"bytes,1,opt,name=item_number,json=itemNumber,proto3" json:"item_number,omitempty"` - PurchaseOrderItemNumber string `protobuf:"bytes,2,opt,name=purchase_order_item_number,json=purchaseOrderItemNumber,proto3" json:"purchase_order_item_number,omitempty"` - TaxAmount string `protobuf:"bytes,3,opt,name=tax_amount,json=taxAmount,proto3" json:"tax_amount,omitempty"` - TaxRate string `protobuf:"bytes,4,opt,name=tax_rate,json=taxRate,proto3" json:"tax_rate,omitempty"` - TaxCode string `protobuf:"bytes,5,opt,name=tax_code,json=taxCode,proto3" json:"tax_code,omitempty"` - TaxBaseAmount string `protobuf:"bytes,6,opt,name=tax_base_amount,json=taxBaseAmount,proto3" json:"tax_base_amount,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *TaxItem) Reset() { *m = TaxItem{} } -func (m *TaxItem) String() string { return proto.CompactTextString(m) } -func (*TaxItem) ProtoMessage() {} -func (*TaxItem) Descriptor() ([]byte, []int) { - return fileDescriptor_0cac5437ed905264, []int{7} -} - -func (m *TaxItem) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_TaxItem.Unmarshal(m, b) -} -func (m *TaxItem) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_TaxItem.Marshal(b, m, deterministic) -} -func (m *TaxItem) XXX_Merge(src proto.Message) { - xxx_messageInfo_TaxItem.Merge(m, src) -} -func (m *TaxItem) XXX_Size() int { - return xxx_messageInfo_TaxItem.Size(m) -} -func (m *TaxItem) XXX_DiscardUnknown() { - xxx_messageInfo_TaxItem.DiscardUnknown(m) -} - -var xxx_messageInfo_TaxItem proto.InternalMessageInfo - -func (m *TaxItem) GetItemNumber() string { - if m != nil { - return m.ItemNumber - } - return "" -} - -func (m *TaxItem) GetPurchaseOrderItemNumber() string { - if m != nil { - return m.PurchaseOrderItemNumber - } - return "" -} - -func (m *TaxItem) GetTaxAmount() string { - if m != nil { - return m.TaxAmount - } - return "" -} - -func (m *TaxItem) GetTaxRate() string { - if m != nil { - return m.TaxRate - } - return "" -} - -func (m *TaxItem) GetTaxCode() string { - if m != nil { - return m.TaxCode - } - return "" -} - -func (m *TaxItem) GetTaxBaseAmount() string { - if m != nil { - return m.TaxBaseAmount - } - return "" -} - -type LineItemActivity struct { - ItemNumber string `protobuf:"bytes,1,opt,name=item_number,json=itemNumber,proto3" json:"item_number,omitempty"` - //delivered, returned, credited, invoiced, paid, ... - Status string `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` - Quantity string `protobuf:"bytes,3,opt,name=quantity,proto3" json:"quantity,omitempty"` - Amount string `protobuf:"bytes,4,opt,name=amount,proto3" json:"amount,omitempty"` - //depending on status delivery note, invoice, ... - ReferenceDocumentId string `protobuf:"bytes,5,opt,name=reference_document_id,json=referenceDocumentId,proto3" json:"reference_document_id,omitempty"` -//line item from the reference document - ReferenceDocumentItem string `protobuf:"bytes,6,opt,name=reference_document_item,json=referenceDocumentItem,proto3" json:"reference_document_item,omitempty"` - Date *timestamp.Timestamp `protobuf:"bytes,7,opt,name=date,proto3" json:"date,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *LineItemActivity) Reset() { *m = LineItemActivity{} } -func (m *LineItemActivity) String() string { return proto.CompactTextString(m) } -func (*LineItemActivity) ProtoMessage() {} -func (*LineItemActivity) Descriptor() ([]byte, []int) { - return fileDescriptor_0cac5437ed905264, []int{8} -} - -func (m *LineItemActivity) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_LineItemActivity.Unmarshal(m, b) -} -func (m *LineItemActivity) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_LineItemActivity.Marshal(b, m, deterministic) -} -func (m *LineItemActivity) XXX_Merge(src proto.Message) { - xxx_messageInfo_LineItemActivity.Merge(m, src) -} -func (m *LineItemActivity) XXX_Size() int { - return xxx_messageInfo_LineItemActivity.Size(m) -} -func (m *LineItemActivity) XXX_DiscardUnknown() { - xxx_messageInfo_LineItemActivity.DiscardUnknown(m) -} - -var xxx_messageInfo_LineItemActivity proto.InternalMessageInfo - -func (m *LineItemActivity) GetItemNumber() string { - if m != nil { - return m.ItemNumber - } - return "" -} - -func (m *LineItemActivity) GetStatus() string { - if m != nil { - return m.Status - } - return "" -} - -func (m *LineItemActivity) GetQuantity() string { - if m != nil { - return m.Quantity - } - return "" -} - -func (m *LineItemActivity) GetAmount() string { - if m != nil { - return m.Amount - } - return "" -} - -func (m *LineItemActivity) GetReferenceDocumentId() string { - if m != nil { - return m.ReferenceDocumentId - } - return "" -} - -func (m *LineItemActivity) GetReferenceDocumentItem() string { - if m != nil { - return m.ReferenceDocumentItem - } - return "" -} - -func (m *LineItemActivity) GetDate() *timestamp.Timestamp { - if m != nil { - return m.Date - } - return nil -} - -func init() { - proto.RegisterType((*GetRequest)(nil), "po.GetRequest") - proto.RegisterType((*GetVersionRequest)(nil), "po.GetVersionRequest") - proto.RegisterType((*PurchaseOrderCreatePayload)(nil), "po.PurchaseOrderCreatePayload") - proto.RegisterMapType((map[string]*document.Attribute)(nil), "po.PurchaseOrderCreatePayload.AttributesEntry") - proto.RegisterType((*PurchaseOrderUpdatePayload)(nil), "po.PurchaseOrderUpdatePayload") - proto.RegisterMapType((map[string]*document.Attribute)(nil), "po.PurchaseOrderUpdatePayload.AttributesEntry") - proto.RegisterType((*PurchaseOrderResponse)(nil), "po.PurchaseOrderResponse") - proto.RegisterMapType((map[string]*document.Attribute)(nil), "po.PurchaseOrderResponse.AttributesEntry") - proto.RegisterType((*PurchaseOrderData)(nil), "po.PurchaseOrderData") - proto.RegisterType((*LineItem)(nil), "po.LineItem") - proto.RegisterType((*TaxItem)(nil), "po.TaxItem") - proto.RegisterType((*LineItemActivity)(nil), "po.LineItemActivity") -} - -func init() { proto.RegisterFile("purchaseorder/service.proto", fileDescriptor_0cac5437ed905264) } - -var fileDescriptor_0cac5437ed905264 = []byte{ - // 1588 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0xdd, 0x6e, 0x1b, 0x41, - 0x15, 0x96, 0xed, 0xc4, 0x89, 0x8f, 0x1d, 0x27, 0x99, 0xfc, 0x6d, 0xdd, 0x3f, 0xd7, 0x85, 0x92, - 0xb4, 0x8d, 0x4d, 0x0d, 0xa2, 0xd0, 0x96, 0x0b, 0x27, 0xad, 0x82, 0x25, 0x48, 0x83, 0x93, 0x22, - 0xd1, 0x9b, 0xd5, 0x64, 0xf7, 0x24, 0x19, 0xe1, 0xfd, 0xe9, 0xcc, 0xd8, 0x8d, 0xa9, 0x2a, 0x21, - 0x6e, 0x91, 0xb8, 0x68, 0x2f, 0x79, 0x07, 0x78, 0x06, 0x78, 0x02, 0x24, 0x5e, 0xa1, 0x37, 0xbc, - 0x05, 0x9a, 0x9f, 0xb5, 0xd7, 0x71, 0x12, 0xb7, 0x42, 0xea, 0x95, 0x3d, 0xe7, 0x7c, 0xe7, 0xec, - 0xd9, 0xb3, 0xdf, 0x7c, 0x67, 0x06, 0x6e, 0xc6, 0x3d, 0xee, 0x9d, 0x51, 0x81, 0x11, 0xf7, 0x91, - 0x37, 0x04, 0xf2, 0x3e, 0xf3, 0xb0, 0x1e, 0xf3, 0x48, 0x46, 0x24, 0x1b, 0x47, 0x95, 0x75, 0x3f, - 0xf2, 0x7a, 0x01, 0x86, 0x72, 0xdc, 0x57, 0xb9, 0x75, 0x1a, 0x45, 0xa7, 0x5d, 0x6c, 0xd0, 0x98, - 0x35, 0x68, 0x18, 0x46, 0x92, 0x4a, 0x16, 0x85, 0xc2, 0x7a, 0xef, 0x5a, 0xaf, 0x5e, 0x1d, 0xf7, - 0x4e, 0x1a, 0x92, 0x05, 0x28, 0x24, 0x0d, 0x62, 0x0b, 0x78, 0xac, 0x7f, 0xbc, 0xed, 0x53, 0x0c, - 0xb7, 0xc5, 0x7b, 0x7a, 0x7a, 0x8a, 0xbc, 0x11, 0xc5, 0x3a, 0xc5, 0x64, 0xba, 0xda, 0x36, 0xc0, - 0x1e, 0xca, 0x0e, 0xbe, 0xeb, 0xa1, 0x90, 0xe4, 0x2e, 0x14, 0x93, 0xa2, 0x5c, 0xe6, 0x3b, 0x99, - 0x6a, 0x66, 0xb3, 0xd0, 0x81, 0xc4, 0xd4, 0xf6, 0x6b, 0x87, 0xb0, 0xbc, 0x87, 0xf2, 0x77, 0xc8, - 0x05, 0x8b, 0xc2, 0xaf, 0x8d, 0x22, 0xb7, 0x01, 0xfa, 0x26, 0x44, 0xf9, 0xb3, 0xda, 0x5f, 0xb0, - 0x96, 0xb6, 0x5f, 0xfb, 0x47, 0x16, 0x2a, 0x07, 0xb6, 0x59, 0xaf, 0x55, 0xb3, 0x76, 0x39, 0x52, - 0x89, 0x07, 0x74, 0xd0, 0x8d, 0xa8, 0xaf, 0xd2, 0x73, 0xa4, 0xbe, 0x4b, 0x3d, 0x0f, 0x85, 0x70, - 0x32, 0xd5, 0x9c, 0x4a, 0xaf, 0x4c, 0x2d, 0x6d, 0x21, 0xf7, 0xa0, 0xf4, 0x9e, 0x33, 0x89, 0x09, - 0x22, 0xab, 0x11, 0x45, 0x6d, 0xb3, 0x90, 0x2d, 0x98, 0xf1, 0xa9, 0xa4, 0x4e, 0xae, 0x9a, 0xd9, - 0x2c, 0x36, 0xd7, 0xea, 0x71, 0x54, 0x1f, 0x7b, 0xe2, 0x4b, 0x2a, 0x69, 0x47, 0x43, 0xc8, 0x3e, - 0x00, 0x95, 0x92, 0xb3, 0xe3, 0x9e, 0x44, 0xe1, 0xcc, 0x54, 0x73, 0x9b, 0xc5, 0x66, 0x7d, 0x22, - 0x60, 0xac, 0xc4, 0x7a, 0x6b, 0x18, 0xf0, 0x2a, 0x94, 0x7c, 0xd0, 0x49, 0x65, 0xa8, 0x74, 0x60, - 0xf1, 0x82, 0x9b, 0x2c, 0x41, 0xee, 0x0f, 0x38, 0xb0, 0x8d, 0x52, 0x7f, 0xc9, 0x16, 0xcc, 0xf6, - 0x69, 0xb7, 0x87, 0xba, 0x39, 0xc5, 0xe6, 0x4a, 0x3d, 0xe9, 0xde, 0x28, 0x75, 0xc7, 0x20, 0x9e, - 0x65, 0x7f, 0x9e, 0xa9, 0xfd, 0xfb, 0x62, 0xc7, 0xde, 0xc4, 0xfe, 0x78, 0xc7, 0xae, 0xff, 0x20, - 0x17, 0x5a, 0x9a, 0x9d, 0xda, 0xd2, 0xdc, 0xd5, 0x2d, 0x9d, 0xf9, 0xd6, 0x96, 0xce, 0x5e, 0xd1, - 0xd2, 0xb1, 0x77, 0xf8, 0xee, 0x2d, 0xfd, 0x9c, 0x85, 0xb5, 0xb1, 0x72, 0x3a, 0x28, 0xe2, 0x28, - 0x14, 0x48, 0x7e, 0x0c, 0xf9, 0x33, 0xa4, 0x3e, 0x72, 0x9d, 0xbd, 0xd8, 0x74, 0x46, 0x99, 0x12, - 0xcc, 0xaf, 0xb4, 0xbf, 0x63, 0x71, 0xc3, 0xd6, 0x64, 0xa7, 0xb7, 0xa6, 0x3d, 0xd6, 0x9a, 0x9c, - 0x6e, 0xcd, 0xd6, 0x44, 0x40, 0xf2, 0x9c, 0xef, 0xde, 0x95, 0x7f, 0x16, 0x60, 0x79, 0xa2, 0x74, - 0xb2, 0x0e, 0x79, 0x21, 0xa9, 0xec, 0x09, 0x9b, 0xd9, 0xae, 0x94, 0x3d, 0xec, 0x05, 0xc7, 0xc8, - 0xed, 0x1e, 0xb7, 0x2b, 0xf2, 0x00, 0x16, 0x05, 0x86, 0x3e, 0x72, 0x57, 0x6b, 0xa1, 0xe2, 0x64, - 0x4e, 0x03, 0x16, 0x8c, 0x59, 0x67, 0x6e, 0xfb, 0xe4, 0x31, 0x10, 0x8e, 0x1e, 0x8b, 0x99, 0x22, - 0xee, 0x10, 0x3a, 0xa3, 0xa1, 0x4b, 0x43, 0x4f, 0x82, 0xfe, 0x21, 0x94, 0x39, 0xbe, 0xeb, 0x31, - 0xc1, 0xa4, 0x55, 0x96, 0x59, 0x93, 0x34, 0x65, 0x1d, 0xc1, 0x50, 0x48, 0xe4, 0x6e, 0x48, 0x03, - 0x74, 0xf2, 0x23, 0x98, 0xb6, 0xee, 0xd3, 0x00, 0xc9, 0x8f, 0x60, 0x71, 0x04, 0xc3, 0x80, 0xb2, - 0xae, 0x33, 0xa7, 0x71, 0xa3, 0xe8, 0x57, 0xca, 0x4a, 0x1a, 0xb0, 0x2a, 0xce, 0x58, 0xec, 0xca, - 0xc8, 0xf5, 0xa2, 0x20, 0xa6, 0xe1, 0xc0, 0x64, 0x9d, 0xd7, 0xe8, 0x65, 0xe5, 0x3b, 0x8a, 0x76, - 0x8d, 0x47, 0x67, 0x7e, 0x01, 0x37, 0x47, 0x01, 0xa1, 0xa4, 0x9e, 0x74, 0x63, 0xe4, 0x22, 0x0a, - 0x4d, 0x5c, 0x41, 0xc7, 0x6d, 0x24, 0x71, 0x1a, 0x70, 0xa0, 0xfd, 0x3a, 0x5a, 0xf5, 0xce, 0x46, - 0x0b, 0xc9, 0x11, 0xe5, 0x13, 0x07, 0x6c, 0xef, 0x74, 0xc4, 0xa1, 0x31, 0x4e, 0xe2, 0x9a, 0x4e, - 0x71, 0x12, 0xd7, 0x24, 0x55, 0x28, 0x0d, 0xab, 0x61, 0x72, 0xe0, 0x94, 0x8c, 0x38, 0xd8, 0xc7, - 0x33, 0x39, 0x48, 0x67, 0xfa, 0x23, 0x8b, 0xbd, 0xc8, 0x47, 0x67, 0x21, 0x9d, 0xe9, 0xad, 0x31, - 0x92, 0x1a, 0x2c, 0x8c, 0x9e, 0x48, 0x25, 0x3a, 0x65, 0x8d, 0x2a, 0x26, 0xcf, 0xa3, 0x72, 0xac, - 0x7a, 0x2f, 0xea, 0x29, 0x4e, 0x3a, 0x8b, 0xe9, 0x5c, 0xbb, 0xc6, 0x48, 0xee, 0xc3, 0x42, 0x4c, - 0x07, 0x5a, 0xb0, 0x24, 0xf2, 0x40, 0x38, 0x4b, 0x1a, 0x55, 0xb2, 0xc6, 0x23, 0x65, 0x23, 0x15, - 0x98, 0xf7, 0x7a, 0x9c, 0x63, 0xe8, 0x0d, 0x9c, 0x65, 0xed, 0x1f, 0xae, 0x95, 0x60, 0xc9, 0x48, - 0xd2, 0xae, 0x4b, 0x03, 0x95, 0xd2, 0x21, 0xa6, 0x16, 0x6d, 0x6b, 0x69, 0x13, 0xb9, 0x05, 0x85, - 0x21, 0x87, 0x9c, 0x15, 0x33, 0x84, 0x86, 0x06, 0xcd, 0x69, 0x4d, 0x46, 0x67, 0xd5, 0x72, 0x5a, - 0xaf, 0x88, 0x03, 0x73, 0x5e, 0x14, 0xa8, 0x22, 0x9c, 0x35, 0xed, 0x48, 0x96, 0xe4, 0x29, 0x14, - 0x94, 0x60, 0xb9, 0x42, 0xf9, 0xd6, 0xf5, 0x76, 0xaa, 0xd4, 0xcd, 0x74, 0xae, 0x27, 0xd3, 0xb9, - 0x7e, 0x94, 0x4c, 0xe7, 0xce, 0xbc, 0x02, 0x1f, 0xaa, 0xc0, 0x16, 0x94, 0x75, 0xa0, 0x17, 0x85, - 0x27, 0x8c, 0x07, 0xe8, 0x3b, 0x1b, 0x53, 0xa3, 0x17, 0x54, 0xc4, 0x6e, 0x12, 0x40, 0x7e, 0x09, - 0x25, 0x9d, 0xa2, 0xa7, 0x35, 0xd3, 0x77, 0x9c, 0xa9, 0x09, 0x8a, 0x0a, 0x68, 0x24, 0x76, 0x14, - 0xee, 0xe9, 0x29, 0xe6, 0x3b, 0x37, 0xbe, 0x2e, 0xdc, 0x0c, 0x3d, 0x9f, 0xbc, 0x80, 0x22, 0x95, - 0x92, 0x7a, 0x67, 0xaa, 0x0f, 0xc2, 0xa9, 0x68, 0xd5, 0xaa, 0x8c, 0xa4, 0x64, 0x87, 0x85, 0x94, - 0x0f, 0x5a, 0x43, 0x48, 0x27, 0x0d, 0x27, 0x8f, 0x00, 0xba, 0x2c, 0x44, 0x97, 0x49, 0x0c, 0x84, - 0x73, 0x53, 0x07, 0x97, 0x94, 0xe4, 0xfd, 0x9a, 0x85, 0xd8, 0x96, 0x18, 0x74, 0x0a, 0x5d, 0xfb, - 0x4f, 0x90, 0x16, 0x2c, 0x26, 0xc4, 0xf0, 0x51, 0x52, 0xd6, 0x15, 0xce, 0x2d, 0x1d, 0x91, 0x52, - 0xe1, 0x03, 0x03, 0x78, 0x69, 0xfc, 0x9d, 0x72, 0x3c, 0xb6, 0xae, 0xfd, 0x6b, 0x16, 0xe6, 0x93, - 0xd4, 0x57, 0x4a, 0xd7, 0x5d, 0x28, 0xaa, 0x7a, 0xdc, 0x31, 0xfd, 0x02, 0x65, 0xda, 0x37, 0x1a, - 0x56, 0x85, 0xa2, 0x8f, 0xc2, 0xe3, 0x4c, 0x1f, 0xa5, 0xac, 0x7e, 0xa5, 0x4d, 0x4a, 0x41, 0x0c, - 0xf9, 0x5c, 0x16, 0xf6, 0x23, 0xe6, 0x61, 0x22, 0x5d, 0x65, 0x63, 0x6e, 0x5b, 0xab, 0xe2, 0xaa, - 0x05, 0x6a, 0x7a, 0x5a, 0xd9, 0x2a, 0x1a, 0xdb, 0x91, 0x32, 0x91, 0x6d, 0xa5, 0x84, 0x23, 0x6d, - 0xb3, 0x55, 0x19, 0xe1, 0x5a, 0x4e, 0x79, 0x6c, 0x71, 0x5b, 0xb0, 0x34, 0x26, 0x85, 0x12, 0x03, - 0xab, 0x5e, 0x8b, 0x69, 0x31, 0x54, 0x0d, 0xd8, 0x80, 0xb9, 0x98, 0x72, 0xe9, 0x86, 0x91, 0x55, - 0xac, 0xbc, 0x5a, 0xee, 0x47, 0xe4, 0x07, 0x50, 0x8e, 0x39, 0xf3, 0x50, 0x89, 0x93, 0xdb, 0x0b, - 0x99, 0xb4, 0xca, 0x54, 0xd2, 0xd6, 0x03, 0xe4, 0x6f, 0x42, 0x26, 0xd5, 0x86, 0x56, 0x3e, 0x37, - 0x3a, 0x71, 0x03, 0xa4, 0xa2, 0xc7, 0x31, 0x91, 0x23, 0x65, 0x7e, 0x7d, 0xf2, 0x1b, 0x63, 0x54, - 0x7b, 0xf5, 0x5d, 0x8f, 0x86, 0x52, 0x49, 0x8c, 0xd1, 0xa1, 0xe1, 0x9a, 0x3c, 0x82, 0x65, 0x8e, - 0x1e, 0xb2, 0x3e, 0xfa, 0xee, 0x10, 0x54, 0x1a, 0xaa, 0xbc, 0x76, 0xfc, 0x36, 0x01, 0x5f, 0x64, - 0xfa, 0xc2, 0xff, 0xc7, 0xf4, 0xf2, 0xb7, 0x31, 0x5d, 0x4f, 0x85, 0x3e, 0x13, 0xa9, 0x8f, 0xa0, - 0xf4, 0x2b, 0xa7, 0xa6, 0x82, 0x31, 0xdb, 0x2f, 0xf0, 0x53, 0x00, 0xea, 0x49, 0xd6, 0x67, 0x92, - 0xa1, 0x52, 0x2f, 0x45, 0xd1, 0xd5, 0x34, 0xa9, 0x5b, 0xc6, 0xab, 0x46, 0xf6, 0x10, 0x47, 0x36, - 0xa1, 0x20, 0xe9, 0xb9, 0xdd, 0x09, 0xcb, 0x3a, 0xa8, 0xa8, 0x82, 0x8e, 0xe8, 0xb9, 0xde, 0x08, - 0xf3, 0xd2, 0xfc, 0x11, 0xb5, 0x2f, 0x19, 0x98, 0xb3, 0xd6, 0x8b, 0x5c, 0xcd, 0x4c, 0x70, 0xf5, - 0x39, 0x54, 0x92, 0xcb, 0x47, 0x32, 0x46, 0x27, 0xb8, 0xbd, 0x11, 0xa7, 0xc7, 0x7a, 0x7b, 0x14, - 0x7c, 0x1b, 0x40, 0xd5, 0x64, 0x75, 0xd4, 0xf0, 0x5c, 0x55, 0x69, 0x55, 0xf4, 0x06, 0xa8, 0xa2, - 0x5c, 0xae, 0x04, 0xdf, 0xd0, 0x7b, 0x4e, 0xd2, 0xf3, 0x8e, 0x12, 0x7b, 0xeb, 0xd2, 0x13, 0x63, - 0x76, 0xe8, 0xda, 0x55, 0xb3, 0xe2, 0x01, 0x2c, 0x2a, 0xd7, 0xb1, 0xaa, 0xc8, 0x66, 0xb6, 0x53, - 0x58, 0xd2, 0xf3, 0x1d, 0x2a, 0xd0, 0x64, 0xaf, 0xfd, 0x2d, 0x0b, 0x4b, 0x17, 0x3b, 0x36, 0xfd, - 0x7d, 0x47, 0x9b, 0x3a, 0x3b, 0xb6, 0xa9, 0xd3, 0x24, 0xcc, 0x5d, 0x20, 0xe1, 0x3a, 0xe4, 0x6d, - 0x21, 0xe6, 0x2d, 0xec, 0x8a, 0x34, 0x61, 0x8d, 0xe3, 0x09, 0xaa, 0xa9, 0x82, 0x6e, 0xfa, 0x14, - 0x6d, 0xde, 0x68, 0x65, 0xe8, 0x7c, 0x39, 0x3a, 0x4e, 0xff, 0x0c, 0x36, 0x2e, 0x8b, 0x51, 0xbb, - 0xd0, 0xbc, 0xe5, 0xda, 0x64, 0x94, 0xfa, 0x90, 0x75, 0x7d, 0x4e, 0x44, 0xbd, 0x55, 0xaf, 0x27, - 0xa5, 0xc6, 0x35, 0xff, 0x3b, 0x03, 0xab, 0x63, 0xa7, 0xb1, 0x43, 0x73, 0x71, 0x24, 0x7f, 0xca, - 0x40, 0xde, 0x50, 0x96, 0xdc, 0xb9, 0xfe, 0xaa, 0x52, 0xb9, 0x71, 0xe5, 0xe1, 0xb2, 0xf6, 0xfc, - 0x53, 0xab, 0x52, 0x71, 0x0c, 0x5c, 0x54, 0x69, 0x35, 0x61, 0x48, 0x55, 0x73, 0xe8, 0xcf, 0xff, - 0xf9, 0xf2, 0x39, 0xeb, 0xd4, 0x56, 0x1a, 0xfd, 0x27, 0x8d, 0x71, 0x76, 0x89, 0x67, 0x99, 0x87, - 0xe4, 0xaf, 0x19, 0xc8, 0x9b, 0x4d, 0x77, 0x49, 0x09, 0x63, 0x47, 0xfb, 0xeb, 0x4a, 0x68, 0xeb, - 0x12, 0x0c, 0xfc, 0x8a, 0x12, 0x1e, 0x54, 0xee, 0x5d, 0x52, 0x42, 0xe3, 0x43, 0xea, 0x5b, 0x7d, - 0x54, 0x05, 0xfd, 0x3d, 0xa3, 0xaf, 0xb6, 0xf6, 0xae, 0x4a, 0xf4, 0x29, 0x7c, 0xe2, 0xee, 0x7a, - 0x5d, 0x2d, 0xe1, 0xa7, 0xd6, 0xe3, 0xca, 0xc3, 0x3d, 0x94, 0x55, 0x5a, 0x15, 0x31, 0x7a, 0xec, - 0x84, 0x79, 0x55, 0x7b, 0x6b, 0xad, 0x46, 0x27, 0x97, 0x57, 0xf7, 0x0b, 0xf2, 0x74, 0x6a, 0x75, - 0x0d, 0x9b, 0x44, 0x34, 0x3e, 0x8c, 0xae, 0xc5, 0x1f, 0xc9, 0x5f, 0x32, 0x90, 0xdb, 0x43, 0x49, - 0xca, 0xb6, 0xd2, 0xaf, 0x28, 0xf1, 0xf7, 0x9f, 0x5a, 0xdb, 0x95, 0x47, 0xaa, 0x44, 0x79, 0x86, - 0x55, 0x73, 0x18, 0x92, 0x53, 0x6b, 0xbc, 0x4f, 0xa6, 0x77, 0x70, 0xe7, 0x0e, 0xe4, 0xbd, 0x28, - 0xa8, 0xc7, 0xd1, 0x4e, 0xc9, 0xb2, 0xec, 0x40, 0xd1, 0xf2, 0x20, 0xf3, 0x76, 0x26, 0x8e, 0xe2, - 0xe3, 0xe3, 0xbc, 0x66, 0xe9, 0x4f, 0xfe, 0x17, 0x00, 0x00, 0xff, 0xff, 0xed, 0xd7, 0x10, 0xb2, - 0xe7, 0x10, 0x00, 0x00, -} - -// Reference imports to suppress errors if they are not otherwise used. -var _ context.Context -var _ grpc.ClientConn - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion4 - -// PurchaseOrderServiceClient is the client API for PurchaseOrderService service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. -type PurchaseOrderServiceClient interface { - Create(ctx context.Context, in *PurchaseOrderCreatePayload, opts ...grpc.CallOption) (*PurchaseOrderResponse, error) - Update(ctx context.Context, in *PurchaseOrderUpdatePayload, opts ...grpc.CallOption) (*PurchaseOrderResponse, error) - GetVersion(ctx context.Context, in *GetVersionRequest, opts ...grpc.CallOption) (*PurchaseOrderResponse, error) - Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*PurchaseOrderResponse, error) -} - -type purchaseOrderServiceClient struct { - cc *grpc.ClientConn -} - -func NewPurchaseOrderServiceClient(cc *grpc.ClientConn) PurchaseOrderServiceClient { - return &purchaseOrderServiceClient{cc} -} - -func (c *purchaseOrderServiceClient) Create(ctx context.Context, in *PurchaseOrderCreatePayload, opts ...grpc.CallOption) (*PurchaseOrderResponse, error) { - out := new(PurchaseOrderResponse) - err := c.cc.Invoke(ctx, "/po.PurchaseOrderService/Create", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *purchaseOrderServiceClient) Update(ctx context.Context, in *PurchaseOrderUpdatePayload, opts ...grpc.CallOption) (*PurchaseOrderResponse, error) { - out := new(PurchaseOrderResponse) - err := c.cc.Invoke(ctx, "/po.PurchaseOrderService/Update", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *purchaseOrderServiceClient) GetVersion(ctx context.Context, in *GetVersionRequest, opts ...grpc.CallOption) (*PurchaseOrderResponse, error) { - out := new(PurchaseOrderResponse) - err := c.cc.Invoke(ctx, "/po.PurchaseOrderService/GetVersion", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *purchaseOrderServiceClient) Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*PurchaseOrderResponse, error) { - out := new(PurchaseOrderResponse) - err := c.cc.Invoke(ctx, "/po.PurchaseOrderService/Get", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// PurchaseOrderServiceServer is the server API for PurchaseOrderService service. -type PurchaseOrderServiceServer interface { - Create(context.Context, *PurchaseOrderCreatePayload) (*PurchaseOrderResponse, error) - Update(context.Context, *PurchaseOrderUpdatePayload) (*PurchaseOrderResponse, error) - GetVersion(context.Context, *GetVersionRequest) (*PurchaseOrderResponse, error) - Get(context.Context, *GetRequest) (*PurchaseOrderResponse, error) -} - -func RegisterPurchaseOrderServiceServer(s *grpc.Server, srv PurchaseOrderServiceServer) { - s.RegisterService(&_PurchaseOrderService_serviceDesc, srv) -} - -func _PurchaseOrderService_Create_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(PurchaseOrderCreatePayload) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(PurchaseOrderServiceServer).Create(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/po.PurchaseOrderService/Create", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(PurchaseOrderServiceServer).Create(ctx, req.(*PurchaseOrderCreatePayload)) - } - return interceptor(ctx, in, info, handler) -} - -func _PurchaseOrderService_Update_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(PurchaseOrderUpdatePayload) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(PurchaseOrderServiceServer).Update(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/po.PurchaseOrderService/Update", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(PurchaseOrderServiceServer).Update(ctx, req.(*PurchaseOrderUpdatePayload)) - } - return interceptor(ctx, in, info, handler) -} - -func _PurchaseOrderService_GetVersion_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetVersionRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(PurchaseOrderServiceServer).GetVersion(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/po.PurchaseOrderService/GetVersion", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(PurchaseOrderServiceServer).GetVersion(ctx, req.(*GetVersionRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _PurchaseOrderService_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(PurchaseOrderServiceServer).Get(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/po.PurchaseOrderService/Get", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(PurchaseOrderServiceServer).Get(ctx, req.(*GetRequest)) - } - return interceptor(ctx, in, info, handler) -} - -var _PurchaseOrderService_serviceDesc = grpc.ServiceDesc{ - ServiceName: "po.PurchaseOrderService", - HandlerType: (*PurchaseOrderServiceServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "Create", - Handler: _PurchaseOrderService_Create_Handler, - }, - { - MethodName: "Update", - Handler: _PurchaseOrderService_Update_Handler, - }, - { - MethodName: "GetVersion", - Handler: _PurchaseOrderService_GetVersion_Handler, - }, - { - MethodName: "Get", - Handler: _PurchaseOrderService_Get_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "purchaseorder/service.proto", -} diff --git a/protobufs/gen/go/purchaseorder/service.pb.gw.go b/protobufs/gen/go/purchaseorder/service.pb.gw.go deleted file mode 100644 index 7a9441276..000000000 --- a/protobufs/gen/go/purchaseorder/service.pb.gw.go +++ /dev/null @@ -1,315 +0,0 @@ -// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. -// source: purchaseorder/service.proto - -/* -Package popb is a reverse proxy. - -It translates gRPC into RESTful JSON APIs. -*/ -package popb - -import ( - "io" - "net/http" - - "github.com/golang/protobuf/proto" - "github.com/grpc-ecosystem/grpc-gateway/runtime" - "github.com/grpc-ecosystem/grpc-gateway/utilities" - "golang.org/x/net/context" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/grpclog" - "google.golang.org/grpc/status" -) - -var _ codes.Code -var _ io.Reader -var _ status.Status -var _ = runtime.String -var _ = utilities.NewDoubleArray - -func request_PurchaseOrderService_Create_0(ctx context.Context, marshaler runtime.Marshaler, client PurchaseOrderServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq PurchaseOrderCreatePayload - var metadata runtime.ServerMetadata - - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - msg, err := client.Create(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func request_PurchaseOrderService_Update_0(ctx context.Context, marshaler runtime.Marshaler, client PurchaseOrderServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq PurchaseOrderUpdatePayload - var metadata runtime.ServerMetadata - - if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF { - return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) - } - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["document_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "document_id") - } - - protoReq.DocumentId, err = runtime.String(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "document_id", err) - } - - msg, err := client.Update(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func request_PurchaseOrderService_GetVersion_0(ctx context.Context, marshaler runtime.Marshaler, client PurchaseOrderServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq GetVersionRequest - var metadata runtime.ServerMetadata - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["document_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "document_id") - } - - protoReq.DocumentId, err = runtime.String(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "document_id", err) - } - - val, ok = pathParams["version_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "version_id") - } - - protoReq.VersionId, err = runtime.String(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "version_id", err) - } - - msg, err := client.GetVersion(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -func request_PurchaseOrderService_Get_0(ctx context.Context, marshaler runtime.Marshaler, client PurchaseOrderServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { - var protoReq GetRequest - var metadata runtime.ServerMetadata - - var ( - val string - ok bool - err error - _ = err - ) - - val, ok = pathParams["document_id"] - if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "document_id") - } - - protoReq.DocumentId, err = runtime.String(val) - - if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "document_id", err) - } - - msg, err := client.Get(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) - return msg, metadata, err - -} - -// RegisterPurchaseOrderServiceHandlerFromEndpoint is same as RegisterPurchaseOrderServiceHandler but -// automatically dials to "endpoint" and closes the connection when "ctx" gets done. -func RegisterPurchaseOrderServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { - conn, err := grpc.Dial(endpoint, opts...) - if err != nil { - return err - } - defer func() { - if err != nil { - if cerr := conn.Close(); cerr != nil { - grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) - } - return - } - go func() { - <-ctx.Done() - if cerr := conn.Close(); cerr != nil { - grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) - } - }() - }() - - return RegisterPurchaseOrderServiceHandler(ctx, mux, conn) -} - -// RegisterPurchaseOrderServiceHandler registers the http handlers for service PurchaseOrderService to "mux". -// The handlers forward requests to the grpc endpoint over "conn". -func RegisterPurchaseOrderServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { - return RegisterPurchaseOrderServiceHandlerClient(ctx, mux, NewPurchaseOrderServiceClient(conn)) -} - -// RegisterPurchaseOrderServiceHandlerClient registers the http handlers for service PurchaseOrderService -// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "PurchaseOrderServiceClient". -// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "PurchaseOrderServiceClient" -// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in -// "PurchaseOrderServiceClient" to call the correct interceptors. -func RegisterPurchaseOrderServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client PurchaseOrderServiceClient) error { - - mux.Handle("POST", pattern_PurchaseOrderService_Create_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - if cn, ok := w.(http.CloseNotifier); ok { - go func(done <-chan struct{}, closed <-chan bool) { - select { - case <-done: - case <-closed: - cancel() - } - }(ctx.Done(), cn.CloseNotify()) - } - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_PurchaseOrderService_Create_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_PurchaseOrderService_Create_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("PUT", pattern_PurchaseOrderService_Update_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - if cn, ok := w.(http.CloseNotifier); ok { - go func(done <-chan struct{}, closed <-chan bool) { - select { - case <-done: - case <-closed: - cancel() - } - }(ctx.Done(), cn.CloseNotify()) - } - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_PurchaseOrderService_Update_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_PurchaseOrderService_Update_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("GET", pattern_PurchaseOrderService_GetVersion_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - if cn, ok := w.(http.CloseNotifier); ok { - go func(done <-chan struct{}, closed <-chan bool) { - select { - case <-done: - case <-closed: - cancel() - } - }(ctx.Done(), cn.CloseNotify()) - } - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_PurchaseOrderService_GetVersion_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_PurchaseOrderService_GetVersion_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - mux.Handle("GET", pattern_PurchaseOrderService_Get_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { - ctx, cancel := context.WithCancel(req.Context()) - defer cancel() - if cn, ok := w.(http.CloseNotifier); ok { - go func(done <-chan struct{}, closed <-chan bool) { - select { - case <-done: - case <-closed: - cancel() - } - }(ctx.Done(), cn.CloseNotify()) - } - inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) - rctx, err := runtime.AnnotateContext(ctx, mux, req) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - resp, md, err := request_PurchaseOrderService_Get_0(rctx, inboundMarshaler, client, req, pathParams) - ctx = runtime.NewServerMetadataContext(ctx, md) - if err != nil { - runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) - return - } - - forward_PurchaseOrderService_Get_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) - - }) - - return nil -} - -var ( - pattern_PurchaseOrderService_Create_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "purchase_orders"}, "")) - - pattern_PurchaseOrderService_Update_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "purchase_orders", "document_id"}, "")) - - pattern_PurchaseOrderService_GetVersion_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"v1", "purchase_orders", "document_id", "versions", "version_id"}, "")) - - pattern_PurchaseOrderService_Get_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "purchase_orders", "document_id"}, "")) -) - -var ( - forward_PurchaseOrderService_Create_0 = runtime.ForwardResponseMessage - - forward_PurchaseOrderService_Update_0 = runtime.ForwardResponseMessage - - forward_PurchaseOrderService_GetVersion_0 = runtime.ForwardResponseMessage - - forward_PurchaseOrderService_Get_0 = runtime.ForwardResponseMessage -) diff --git a/protobufs/gen/swagger.json b/protobufs/gen/swagger.json deleted file mode 100644 index 22f8b89ca..000000000 --- a/protobufs/gen/swagger.json +++ /dev/null @@ -1 +0,0 @@ -{"swagger":"2.0","info":{"version":"0.0.5","title":"Centrifuge OS Node API","description":"\n","contact":{"name":"Centrifuge","url":"https://github.com/centrifuge/go-centrifuge","email":"hello@centrifuge.io"}},"host":"localhost:8082","basePath":"","schemes":["http"],"consumes":["application/json"],"produces":["application/json"],"tags":[],"definitions":{"accountAccountData":{"type":"object","properties":{"eth_account":{"$ref":"#/definitions/accountEthereumAccount"},"eth_default_account_name":{"type":"string"},"receive_event_notification_endpoint":{"type":"string"},"identity_id":{"type":"string"},"signing_key_pair":{"$ref":"#/definitions/accountKeyPair"},"p2p_key_pair":{"$ref":"#/definitions/accountKeyPair"}}},"accountEthereumAccount":{"type":"object","properties":{"address":{"type":"string"},"key":{"type":"string"},"password":{"type":"string"}}},"accountGetAllAccountResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/definitions/accountAccountData"}}}},"accountKeyPair":{"type":"object","properties":{"pub":{"type":"string"},"pvt":{"type":"string"}}},"accountUpdateAccountRequest":{"type":"object","properties":{"account_id":{"type":"string"},"data":{"$ref":"#/definitions/accountAccountData"}}},"byteutils.HexBytes":{"type":"array","items":{}},"coreapi.Attribute":{"type":"object","properties":{"type":{"type":"string","enum":["integer","decimal","string","bytes","timestamp"]},"value":{"type":"string"}}},"coreapi.AttributeMap":{"type":"object","additionalProperties":{"type":"object","properties":{"type":{"type":"string","enum":["integer","decimal","string","bytes","timestamp"]},"value":{"type":"string"}}}},"coreapi.CreateDocumentRequest":{"type":"object","properties":{"attributes":{"type":"object","$ref":"#/definitions/coreapi.AttributeMap"},"data":{"type":"object"},"read_access":{"type":"array","items":{"type":"string"}},"scheme":{"type":"string","enum":["generic","invoice","purchase_order","entity"]},"write_access":{"type":"array","items":{"type":"string"}}}},"coreapi.DocumentResponse":{"type":"object","properties":{"attributes":{"type":"object","$ref":"#/definitions/coreapi.AttributeMap"},"data":{"type":"object"},"header":{"type":"object","$ref":"#/definitions/coreapi.ResponseHeader"},"scheme":{"type":"string","enum":["generic","invoice","purchase_order","entity"]}}},"coreapi.MintNFTRequest":{"type":"object","properties":{"deposit_address":{"type":"string"},"document_id":{"type":"string"},"grant_nft_access":{"type":"boolean"},"proof_fields":{"type":"array","items":{"type":"string"}},"submit_nft_owner_access_proof":{"type":"boolean"},"submit_token_proof":{"type":"boolean"}}},"coreapi.MintNFTResponse":{"type":"object","properties":{"deposit_address":{"type":"string"},"document_id":{"type":"string"},"header":{"type":"object","$ref":"#/definitions/coreapi.NFTResponseHeader"},"registry_address":{"type":"string"},"token_id":{"type":"string"}}},"coreapi.NFT":{"type":"object","properties":{"owner":{"type":"string"},"registry":{"type":"string"},"token_id":{"type":"string"},"token_index":{"type":"string"}}},"coreapi.NFTOwnerResponse":{"type":"object","properties":{"owner":{"type":"string"},"registry_address":{"type":"string"},"token_id":{"type":"string"}}},"coreapi.NFTResponseHeader":{"type":"object","properties":{"job_id":{"type":"string"}}},"coreapi.Proof":{"type":"object","properties":{"hash":{"type":"string"},"property":{"type":"string"},"salt":{"type":"string"},"sorted_hashes":{"type":"array","items":{"type":"string"}},"value":{"type":"string"}}},"coreapi.ProofResponseHeader":{"type":"object","properties":{"document_id":{"type":"string"},"state":{"type":"string"},"version_id":{"type":"string"}}},"coreapi.ProofsRequest":{"type":"object","properties":{"fields":{"type":"array","items":{"type":"string"}}}},"coreapi.ProofsResponse":{"type":"object","properties":{"field_proofs":{"type":"array","items":{"$ref":"#/definitions/coreapi.Proof"}},"header":{"type":"object","$ref":"#/definitions/coreapi.ProofResponseHeader"}}},"coreapi.ResponseHeader":{"type":"object","properties":{"author":{"type":"string"},"created_at":{"type":"string"},"document_id":{"type":"string"},"job_id":{"type":"string"},"nfts":{"type":"array","items":{"$ref":"#/definitions/coreapi.NFT"}},"read_access":{"type":"array","items":{"type":"string"}},"version_id":{"type":"string"},"write_access":{"type":"array","items":{"type":"string"}}}},"coreapi.SignRequest":{"type":"object","properties":{"payload":{"type":"string"}}},"coreapi.SignResponse":{"type":"object","properties":{"payload":{"type":"string"},"public_key":{"type":"string"},"signature":{"type":"string"},"signer_id":{"type":"string"}}},"coreapi.TransferNFTRequest":{"type":"object","properties":{"to":{"type":"string"}}},"coreapi.TransferNFTResponse":{"type":"object","properties":{"header":{"type":"object","$ref":"#/definitions/coreapi.NFTResponseHeader"},"registry_address":{"type":"string"},"to":{"type":"string"},"token_id":{"type":"string"}}},"health.Pong":{"type":"object","properties":{"network":{"type":"string"},"version":{"type":"string"}}},"httputils.HTTPError":{"type":"object","properties":{"message":{"type":"string"}}},"jobs.StatusResponse":{"type":"object","properties":{"job_id":{"type":"string"},"last_updated":{"type":"string"},"message":{"type":"string"},"status":{"type":"string"}}},"transferdetails.TransferDetailData":{"type":"object","properties":{"amount":{"type":"string"},"currency":{"type":"string"},"data":{"type":"string"},"recipient_id":{"type":"string"},"scheduled_date":{"type":"string"},"sender_id":{"type":"string"},"settlement_date":{"type":"string"},"settlement_reference":{"type":"string"},"status":{"type":"string"},"transfer_id":{"type":"string"},"transfer_type":{"type":"string"}}},"userapi.CreateTransferDetailRequest":{"type":"object","properties":{"data":{"type":"object","$ref":"#/definitions/transferdetails.TransferDetailData"},"document_id":{"type":"string"}}},"userapi.TransferDetailListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/definitions/transferdetails.TransferDetailData"}},"header":{"type":"object","$ref":"#/definitions/coreapi.ResponseHeader"}}},"userapi.TransferDetailResponse":{"type":"object","properties":{"data":{"type":"object","$ref":"#/definitions/transferdetails.TransferDetailData"},"header":{"type":"object","$ref":"#/definitions/coreapi.ResponseHeader"}}},"userapi.UpdateTransferDetailRequest":{"type":"object","properties":{"data":{"type":"object","$ref":"#/definitions/transferdetails.TransferDetailData"},"document_id":{"type":"string"},"transfer_id":{"type":"string"}}},"documentAttribute":{"type":"object","properties":{"key":{"type":"string","title":"this is the sha256 hash of the label of the attribute, is not allowed to be updated by the client"},"type":{"type":"string"},"value":{"type":"string"}},"title":"Attribute represents a custom attribute"},"documentNFT":{"type":"object","properties":{"registry":{"type":"string"},"owner":{"type":"string","title":"read owner from Ethereum and empty when used in POST/PUT"},"token_id":{"type":"string"},"token_index":{"type":"string","title":"index of the token in the registry"}}},"documentResponseHeader":{"type":"object","properties":{"document_id":{"type":"string"},"version_id":{"type":"string"},"author":{"type":"string"},"created_at":{"type":"string"},"read_access":{"type":"array","items":{"type":"string"}},"write_access":{"type":"array","items":{"type":"string"}},"job_id":{"type":"string"},"nfts":{"type":"array","items":{"$ref":"#/definitions/documentNFT"}}}},"entityAddress":{"type":"object","properties":{"is_main":{"type":"boolean","format":"boolean"},"is_remit_to":{"type":"boolean","format":"boolean"},"is_ship_to":{"type":"boolean","format":"boolean"},"is_pay_to":{"type":"boolean","format":"boolean"},"label":{"type":"string"},"zip":{"type":"string"},"state":{"type":"string"},"country":{"type":"string"},"address_line1":{"type":"string"},"address_line2":{"type":"string"},"contact_person":{"type":"string"}}},"entityBankPaymentMethod":{"type":"object","properties":{"identifier":{"type":"string","format":"byte"},"address":{"$ref":"#/definitions/entityAddress"},"holder_name":{"type":"string"},"bank_key":{"type":"string"},"bank_account_number":{"type":"string"},"supported_currency":{"type":"string"}}},"entityContact":{"type":"object","properties":{"name":{"type":"string"},"title":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"},"fax":{"type":"string"}}},"entityCryptoPaymentMethod":{"type":"object","properties":{"identifier":{"type":"string","format":"byte"},"to":{"type":"string"},"chain_uri":{"type":"string"},"supported_currency":{"type":"string"}}},"entityEntityCreatePayload":{"type":"object","properties":{"read_access":{"type":"array","items":{"type":"string"}},"write_access":{"type":"array","items":{"type":"string"}},"data":{"$ref":"#/definitions/entityEntityData"},"attributes":{"type":"object","additionalProperties":{"$ref":"#/definitions/documentAttribute"},"title":"custom attributes"}}},"entityEntityData":{"type":"object","properties":{"identity":{"type":"string"},"legal_name":{"type":"string"},"addresses":{"type":"array","items":{"$ref":"#/definitions/entityAddress"},"title":"address"},"payment_details":{"type":"array","items":{"$ref":"#/definitions/entityPaymentDetail"},"title":"tax information"},"contacts":{"type":"array","items":{"$ref":"#/definitions/entityContact"},"title":"Entity contact list"}},"title":"EntityData is the default entity schema"},"entityEntityDataResponse":{"type":"object","properties":{"entity":{"$ref":"#/definitions/entityEntityData"},"relationships":{"type":"array","items":{"$ref":"#/definitions/entityRelationship"}}},"title":"Entity Relationships"},"entityEntityResponse":{"type":"object","properties":{"header":{"$ref":"#/definitions/documentResponseHeader"},"data":{"$ref":"#/definitions/entityEntityDataResponse"},"attributes":{"type":"object","additionalProperties":{"$ref":"#/definitions/documentAttribute"},"title":"custom attributes"}}},"entityEntityUpdatePayload":{"type":"object","properties":{"document_id":{"type":"string"},"read_access":{"type":"array","items":{"type":"string"}},"write_access":{"type":"array","items":{"type":"string"}},"data":{"$ref":"#/definitions/entityEntityData"},"attributes":{"type":"object","additionalProperties":{"$ref":"#/definitions/documentAttribute"},"title":"custom attributes"}}},"entityOtherPayment":{"type":"object","properties":{"identifier":{"type":"string","format":"byte"},"type":{"type":"string"},"pay_to":{"type":"string"},"supported_currency":{"type":"string"}}},"entityPaymentDetail":{"type":"object","properties":{"predefined":{"type":"boolean","format":"boolean","title":"fields for bank accounts and ethereum wallets"},"bank_payment_method":{"$ref":"#/definitions/entityBankPaymentMethod"},"crypto_payment_method":{"$ref":"#/definitions/entityCryptoPaymentMethod"},"other_method":{"$ref":"#/definitions/entityOtherPayment"}}},"entityRelationship":{"type":"object","properties":{"identity":{"type":"string"},"active":{"type":"boolean","format":"boolean"}}},"entityRelationshipData":{"type":"object","properties":{"owner_identity":{"type":"string","title":"DID of relationship owner"},"target_identity":{"type":"string","title":"DID of target identity"},"entity_identifier":{"type":"string","title":"identifier of Entity whose data can be accessed via this relationship"}}},"entityRelationshipPayload":{"type":"object","properties":{"document_id":{"type":"string","title":"entity identifier"},"target_identity":{"type":"string"}}},"entityRelationshipResponse":{"type":"object","properties":{"header":{"$ref":"#/definitions/documentResponseHeader"},"relationship":{"type":"array","items":{"$ref":"#/definitions/entityRelationshipData"}}}},"funFundingCreatePayload":{"type":"object","properties":{"document_id":{"type":"string"},"data":{"$ref":"#/definitions/funFundingData"}}},"funFundingData":{"type":"object","properties":{"agreement_id":{"type":"string"},"amount":{"type":"string"},"apr":{"type":"string"},"days":{"type":"string"},"fee":{"type":"string"},"repayment_due_date":{"type":"string"},"repayment_occurred_date":{"type":"string"},"repayment_amount":{"type":"string"},"currency":{"type":"string"},"nft_address":{"type":"string"},"payment_details_id":{"type":"string"},"funder_id":{"type":"string"},"borrower_id":{"type":"string"}},"title":"FundingData is the default funding extension schema"},"funFundingListResponse":{"type":"object","properties":{"header":{"$ref":"#/definitions/documentResponseHeader"},"data":{"type":"array","items":{"$ref":"#/definitions/funFundingResponseData"}}}},"funFundingResponse":{"type":"object","properties":{"header":{"$ref":"#/definitions/documentResponseHeader"},"data":{"$ref":"#/definitions/funFundingResponseData"}}},"funFundingResponseData":{"type":"object","properties":{"funding":{"$ref":"#/definitions/funFundingData"},"signatures":{"type":"array","items":{"$ref":"#/definitions/funFundingSignature"}}}},"funFundingSignature":{"type":"object","properties":{"valid":{"type":"string"},"outdated_signature":{"type":"string"},"identity":{"type":"string"},"signed_version":{"type":"string"}}},"funFundingUpdatePayload":{"type":"object","properties":{"document_id":{"type":"string"},"agreement_id":{"type":"string"},"data":{"$ref":"#/definitions/funFundingData"}}},"funRequest":{"type":"object","properties":{"document_id":{"type":"string"},"agreement_id":{"type":"string"}}},"documentBinaryAttachment":{"type":"object","properties":{"name":{"type":"string"},"file_type":{"type":"string","title":"mime type of attached file"},"size":{"type":"string","format":"uint64","title":"in bytes"},"data":{"type":"string"},"checksum":{"type":"string","title":"the md5 checksum of the original file for easier verification - optional"}}},"documentPaymentDetails":{"type":"object","properties":{"id":{"type":"string","title":"identifying this payment. could be a sequential number, could be a transaction hash of the crypto payment"},"date_executed":{"type":"string","format":"date-time"},"payee":{"type":"string","title":"centrifuge id of payee"},"payer":{"type":"string","title":"centrifuge id of payer"},"amount":{"type":"string"},"currency":{"type":"string"},"reference":{"type":"string","title":"payment reference (e.g. reference field on bank transfer)"},"bank_name":{"type":"string"},"bank_address":{"type":"string"},"bank_country":{"type":"string"},"bank_account_number":{"type":"string"},"bank_account_currency":{"type":"string"},"bank_account_holder_name":{"type":"string"},"bank_key":{"type":"string"},"crypto_chain_uri":{"type":"string","title":"the ID of the chain to use in URI format. e.g. \"ethereum://42/\""},"crypto_transaction_id":{"type":"string","title":"the transaction in which the payment happened"},"crypto_from":{"type":"string","title":"from address"},"crypto_to":{"type":"string","title":"to address"}}},"invInvoiceCreatePayload":{"type":"object","properties":{"read_access":{"type":"array","items":{"type":"string"}},"write_access":{"type":"array","items":{"type":"string"}},"data":{"$ref":"#/definitions/invInvoiceData"},"attributes":{"type":"object","additionalProperties":{"$ref":"#/definitions/documentAttribute"},"title":"custom attributes"}}},"invInvoiceData":{"type":"object","properties":{"number":{"type":"string","title":"invoice number or reference number"},"status":{"type":"string","title":"invoice status"},"sender_invoice_id":{"type":"string"},"recipient_invoice_id":{"type":"string"},"sender_company_name":{"type":"string"},"sender_contact_person_name":{"type":"string"},"sender_street1":{"type":"string","title":"street and address details of the sender company"},"sender_street2":{"type":"string"},"sender_city":{"type":"string"},"sender_zipcode":{"type":"string"},"sender_state":{"type":"string"},"sender_country":{"type":"string","title":"country ISO code of the sender of this invoice"},"bill_to_company_name":{"type":"string"},"bill_to_contact_person_name":{"type":"string"},"bill_to_street1":{"type":"string"},"bill_to_street2":{"type":"string"},"bill_to_city":{"type":"string"},"bill_to_zipcode":{"type":"string"},"bill_to_state":{"type":"string"},"bill_to_country":{"type":"string"},"bill_to_vat_number":{"type":"string"},"bill_to_local_tax_id":{"type":"string"},"remit_to_company_name":{"type":"string"},"remit_to_contact_person_name":{"type":"string"},"remit_to_street1":{"type":"string"},"remit_to_street2":{"type":"string"},"remit_to_city":{"type":"string"},"remit_to_zipcode":{"type":"string"},"remit_to_state":{"type":"string"},"remit_to_country":{"type":"string"},"remit_to_vat_number":{"type":"string"},"remit_to_local_tax_id":{"type":"string"},"remit_to_tax_country":{"type":"string"},"ship_to_company_name":{"type":"string"},"ship_to_contact_person_name":{"type":"string"},"ship_to_street1":{"type":"string"},"ship_to_street2":{"type":"string"},"ship_to_city":{"type":"string"},"ship_to_zipcode":{"type":"string"},"ship_to_state":{"type":"string"},"ship_to_country":{"type":"string"},"currency":{"type":"string","title":"ISO currency code"},"gross_amount":{"type":"string","title":"invoice amount including tax"},"net_amount":{"type":"string","title":"invoice amount excluding tax"},"tax_amount":{"type":"string"},"tax_rate":{"type":"string"},"tax_on_line_level":{"type":"boolean","format":"boolean"},"recipient":{"type":"string","title":"centrifuge ID of the recipient"},"sender":{"type":"string","title":"centrifuge ID of the sender"},"payee":{"type":"string","title":"centrifuge ID of the payee"},"comment":{"type":"string"},"shipping_terms":{"type":"string"},"requester_email":{"type":"string"},"requester_name":{"type":"string"},"delivery_number":{"type":"string","title":"number of the delivery note"},"is_credit_note":{"type":"boolean","format":"boolean"},"credit_note_invoice_number":{"type":"string"},"credit_for_invoice_date":{"type":"string","format":"date-time"},"date_due":{"type":"string","format":"date-time"},"date_paid":{"type":"string","format":"date-time"},"date_updated":{"type":"string","format":"date-time"},"date_created":{"type":"string","format":"date-time"},"attachments":{"type":"array","items":{"$ref":"#/definitions/documentBinaryAttachment"}},"line_items":{"type":"array","items":{"$ref":"#/definitions/invLineItem"}},"payment_details":{"type":"array","items":{"$ref":"#/definitions/documentPaymentDetails"}},"tax_items":{"type":"array","items":{"$ref":"#/definitions/invTaxItem"}}},"title":"InvoiceData is the default invoice schema"},"invInvoiceResponse":{"type":"object","properties":{"header":{"$ref":"#/definitions/documentResponseHeader"},"data":{"$ref":"#/definitions/invInvoiceData"},"attributes":{"type":"object","additionalProperties":{"$ref":"#/definitions/documentAttribute"},"title":"custom attributes"}}},"invInvoiceUpdatePayload":{"type":"object","properties":{"document_id":{"type":"string"},"read_access":{"type":"array","items":{"type":"string"}},"write_access":{"type":"array","items":{"type":"string"}},"data":{"$ref":"#/definitions/invInvoiceData"},"attributes":{"type":"object","additionalProperties":{"$ref":"#/definitions/documentAttribute"},"title":"custom attributes"}}},"invLineItem":{"type":"object","properties":{"item_number":{"type":"string"},"description":{"type":"string"},"sender_part_no":{"type":"string"},"price_per_unit":{"type":"string"},"quantity":{"type":"string"},"unit_of_measure":{"type":"string"},"net_weight":{"type":"string"},"tax_amount":{"type":"string"},"tax_rate":{"type":"string"},"tax_code":{"type":"string"},"total_amount":{"type":"string","title":"the total amount of the line item"},"purchase_order_number":{"type":"string"},"purchase_order_item_number":{"type":"string"},"delivery_note_number":{"type":"string"}}},"invTaxItem":{"type":"object","properties":{"item_number":{"type":"string"},"invoice_item_number":{"type":"string"},"tax_amount":{"type":"string"},"tax_rate":{"type":"string"},"tax_code":{"type":"string"},"tax_base_amount":{"type":"string"}}},"nftNFTMintInvoiceUnpaidRequest":{"type":"object","properties":{"document_id":{"type":"string","title":"Invoice Document identifier"},"deposit_address":{"type":"string","title":"Deposit address for NFT Token created"}}},"nftNFTMintResponse":{"type":"object","properties":{"header":{"$ref":"#/definitions/nftResponseHeader"}}},"nftResponseHeader":{"type":"object","properties":{"job_id":{"type":"string"}}},"notificationNotificationMessage":{"type":"object","properties":{"event_type":{"type":"integer","format":"int64"},"recorded":{"type":"string","format":"date-time"},"document_type":{"type":"string"},"status":{"type":"string","title":"status - Failure or Success"},"message":{"type":"string","title":"message associated to status, if error, error message enclosed"},"document_id":{"type":"string","title":"document_id"},"account_id":{"type":"string","title":"account_id is the account associated to webhook"},"from_id":{"type":"string","title":"from_id if provided, original trigger of the event"},"to_id":{"type":"string","title":"to_id if provided, final destination of the event"}},"title":"NotificationMessage wraps a single document to be notified to upstream services"},"poLineItem":{"type":"object","properties":{"status":{"type":"string"},"item_number":{"type":"string"},"description":{"type":"string"},"amount_invoiced":{"type":"string"},"amount_total":{"type":"string"},"requisition_number":{"type":"string"},"requisition_item":{"type":"string"},"part_no":{"type":"string"},"price_per_unit":{"type":"string"},"unit_of_measure":{"type":"string"},"quantity":{"type":"string"},"received_quantity":{"type":"string"},"date_updated":{"type":"string","format":"date-time"},"date_created":{"type":"string","format":"date-time"},"revision_number":{"type":"string","format":"int64"},"activities":{"type":"array","items":{"$ref":"#/definitions/poLineItemActivity"}},"tax_items":{"type":"array","items":{"$ref":"#/definitions/poTaxItem"}}}},"poLineItemActivity":{"type":"object","properties":{"item_number":{"type":"string"},"status":{"type":"string","description":"delivered, returned, credited, invoiced, paid, ..."},"quantity":{"type":"string"},"amount":{"type":"string"},"reference_document_id":{"type":"string","description":"depending on status delivery note, invoice, ..."},"reference_document_item":{"type":"string","title":"line item from the reference document"},"date":{"type":"string","format":"date-time"}}},"poPurchaseOrderCreatePayload":{"type":"object","properties":{"read_access":{"type":"array","items":{"type":"string"}},"write_access":{"type":"array","items":{"type":"string"}},"data":{"$ref":"#/definitions/poPurchaseOrderData"},"attributes":{"type":"object","additionalProperties":{"$ref":"#/definitions/documentAttribute"},"title":"custom attributes"}}},"poPurchaseOrderData":{"type":"object","properties":{"status":{"type":"string"},"number":{"type":"string","title":"purchase order number or reference number"},"sender_order_id":{"type":"string"},"recipient_order_id":{"type":"string"},"requisition_id":{"type":"string"},"requester_name":{"type":"string"},"requester_email":{"type":"string"},"ship_to_company_name":{"type":"string"},"ship_to_contact_person_name":{"type":"string"},"ship_to_street1":{"type":"string"},"ship_to_street2":{"type":"string"},"ship_to_city":{"type":"string"},"ship_to_zipcode":{"type":"string"},"ship_to_state":{"type":"string"},"ship_to_country":{"type":"string"},"payment_terms":{"type":"string"},"currency":{"type":"string"},"total_amount":{"type":"string"},"recipient":{"type":"string","title":"centrifuge ID of the recipient"},"sender":{"type":"string","title":"centrifuge ID of the sender"},"comment":{"type":"string"},"date_sent":{"type":"string","format":"date-time"},"date_confirmed":{"type":"string","format":"date-time"},"date_updated":{"type":"string","format":"date-time"},"date_created":{"type":"string","format":"date-time"},"attachments":{"type":"array","items":{"$ref":"#/definitions/documentBinaryAttachment"}},"line_items":{"type":"array","items":{"$ref":"#/definitions/poLineItem"}},"payment_details":{"type":"array","items":{"$ref":"#/definitions/documentPaymentDetails"}}}},"poPurchaseOrderResponse":{"type":"object","properties":{"header":{"$ref":"#/definitions/documentResponseHeader"},"data":{"$ref":"#/definitions/poPurchaseOrderData"},"attributes":{"type":"object","additionalProperties":{"$ref":"#/definitions/documentAttribute"},"title":"custom attributes"}}},"poPurchaseOrderUpdatePayload":{"type":"object","properties":{"document_id":{"type":"string"},"read_access":{"type":"array","items":{"type":"string"}},"write_access":{"type":"array","items":{"type":"string"}},"data":{"$ref":"#/definitions/poPurchaseOrderData"},"attributes":{"type":"object","additionalProperties":{"$ref":"#/definitions/documentAttribute"},"title":"custom attributes"}}},"poTaxItem":{"type":"object","properties":{"item_number":{"type":"string"},"purchase_order_item_number":{"type":"string"},"tax_amount":{"type":"string"},"tax_rate":{"type":"string"},"tax_code":{"type":"string"},"tax_base_amount":{"type":"string"}}}},"paths":{"/v1/accounts":{"get":{"description":"Get All Accounts","operationId":"GetAllAccounts","responses":{"200":{"description":"","schema":{"$ref":"#/definitions/accountGetAllAccountResponse"}}},"tags":["AccountService"],"parameters":[{"name":"authorization","in":"header","description":"Hex encoded centrifuge ID of the account for the intended API action","required":true,"type":"string"}]},"post":{"description":"Creates an Account","operationId":"CreateAccount","responses":{"200":{"description":"","schema":{"$ref":"#/definitions/accountAccountData"}}},"parameters":[{"name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/accountAccountData"}},{"name":"authorization","in":"header","description":"Hex encoded centrifuge ID of the account for the intended API action","required":true,"type":"string"}],"tags":["AccountService"]}},"/v1/accounts/generate":{"post":{"description":"Generates an Account taking defaults based on the main account","operationId":"GenerateAccount","responses":{"200":{"description":"","schema":{"$ref":"#/definitions/accountAccountData"}}},"tags":["AccountService"],"parameters":[{"name":"authorization","in":"header","description":"Hex encoded centrifuge ID of the account for the intended API action","required":true,"type":"string"}]}},"/v1/accounts/{account_id}":{"get":{"description":"Get Account","operationId":"GetAccount","responses":{"200":{"description":"","schema":{"$ref":"#/definitions/accountAccountData"}}},"parameters":[{"name":"account_id","in":"path","required":true,"type":"string"},{"name":"authorization","in":"header","description":"Hex encoded centrifuge ID of the account for the intended API action","required":true,"type":"string"}],"tags":["AccountService"]},"put":{"description":"Updates an Account","operationId":"UpdateAccount","responses":{"200":{"description":"","schema":{"$ref":"#/definitions/accountAccountData"}}},"parameters":[{"name":"account_id","in":"path","required":true,"type":"string"},{"name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/accountUpdateAccountRequest"}},{"name":"authorization","in":"header","description":"Hex encoded centrifuge ID of the account for the intended API action","required":true,"type":"string"}],"tags":["AccountService"]}},"/ping":{"get":{"description":"returns node version and network","produces":["application/json"],"tags":["Health"],"summary":"Health check for Node","operationId":"ping","responses":{"200":{"description":"OK","schema":{"type":"object","$ref":"#/definitions/health.Pong"}}}}},"/v1/accounts/{account_id}/sign":{"post":{"description":"Signs and returns the signature of the Payload.","produces":["application/json"],"tags":["Accounts"],"summary":"Signs and returns the signature of the Payload.","operationId":"account_sign","parameters":[{"type":"string","description":"Account ID","name":"account_id","in":"path","required":true},{"description":"Sign request","name":"body","in":"body","required":true,"schema":{"type":"object","$ref":"#/definitions/coreapi.SignRequest"}},{"name":"authorization","in":"header","description":"Hex encoded centrifuge ID of the account for the intended API action","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"object","$ref":"#/definitions/coreapi.SignResponse"}},"400":{"description":"Bad Request","schema":{"type":"object","$ref":"#/definitions/httputils.HTTPError"}},"500":{"description":"Internal Server Error","schema":{"type":"object","$ref":"#/definitions/httputils.HTTPError"}}}}},"/v1/documents":{"post":{"description":"Creates a new document and anchors it.","consumes":["application/json"],"produces":["application/json"],"tags":["Documents"],"summary":"Creates a new document and anchors it.","operationId":"create_document","parameters":[{"type":"string","description":"Hex encoded centrifuge ID of the account for the intended API action","name":"authorization","in":"header","required":true},{"description":"Document Create request","name":"body","in":"body","required":true,"schema":{"type":"object","$ref":"#/definitions/coreapi.CreateDocumentRequest"}}],"responses":{"201":{"description":"Created","schema":{"type":"object","$ref":"#/definitions/coreapi.DocumentResponse"}},"400":{"description":"Bad Request","schema":{"type":"object","$ref":"#/definitions/httputils.HTTPError"}},"403":{"description":"Forbidden","schema":{"type":"object","$ref":"#/definitions/httputils.HTTPError"}},"500":{"description":"Internal Server Error","schema":{"type":"object","$ref":"#/definitions/httputils.HTTPError"}}}}},"/v1/documents/{document_id}":{"get":{"description":"Returns the latest version of the document.","produces":["application/json"],"tags":["Documents"],"summary":"Returns the latest version of the document.","operationId":"get_document","parameters":[{"type":"string","description":"Hex encoded centrifuge ID of the account for the intended API action","name":"authorization","in":"header","required":true},{"type":"string","description":"Document Identifier","name":"document_id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"type":"object","$ref":"#/definitions/coreapi.DocumentResponse"}},"400":{"description":"Bad Request","schema":{"type":"object","$ref":"#/definitions/httputils.HTTPError"}},"404":{"description":"Not Found","schema":{"type":"object","$ref":"#/definitions/httputils.HTTPError"}},"500":{"description":"Internal Server Error","schema":{"type":"object","$ref":"#/definitions/httputils.HTTPError"}}}},"put":{"description":"Updates an existing document and anchors it.","consumes":["application/json"],"produces":["application/json"],"tags":["Documents"],"summary":"Updates an existing document and anchors it.","operationId":"update_document","parameters":[{"type":"string","description":"Hex encoded centrifuge ID of the account for the intended API action","name":"authorization","in":"header","required":true},{"type":"string","description":"Document Identifier","name":"document_id","in":"path","required":true},{"description":"Document Update request","name":"body","in":"body","required":true,"schema":{"type":"object","$ref":"#/definitions/coreapi.CreateDocumentRequest"}}],"responses":{"201":{"description":"Created","schema":{"type":"object","$ref":"#/definitions/coreapi.DocumentResponse"}},"400":{"description":"Bad Request","schema":{"type":"object","$ref":"#/definitions/httputils.HTTPError"}},"403":{"description":"Forbidden","schema":{"type":"object","$ref":"#/definitions/httputils.HTTPError"}},"500":{"description":"Internal Server Error","schema":{"type":"object","$ref":"#/definitions/httputils.HTTPError"}}}}},"/v1/documents/{document_id}/proofs":{"post":{"description":"Generates proofs for the fields from latest version of the document.","produces":["application/json"],"tags":["Documents"],"summary":"Generates proofs for the fields from latest version of the document.","operationId":"generate_document_proofs","parameters":[{"type":"string","description":"Hex encoded centrifuge ID of the account for the intended API action","name":"authorization","in":"header","required":true},{"type":"string","description":"Document Identifier","name":"document_id","in":"path","required":true},{"description":"Document proof request","name":"body","in":"body","required":true,"schema":{"type":"object","$ref":"#/definitions/coreapi.ProofsRequest"}}],"responses":{"200":{"description":"OK","schema":{"type":"object","$ref":"#/definitions/coreapi.ProofsResponse"}},"400":{"description":"Bad Request","schema":{"type":"object","$ref":"#/definitions/httputils.HTTPError"}},"500":{"description":"Internal Server Error","schema":{"type":"object","$ref":"#/definitions/httputils.HTTPError"}}}}},"/v1/documents/{document_id}/transfer_details":{"get":{"description":"Returns a list of the latest versions of all transfer details on the document.","produces":["application/json"],"tags":["Documents"],"summary":"Returns a list of the latest versions of all transfer details on the document.","operationId":"get_transfer","parameters":[{"type":"string","description":"Hex encoded centrifuge ID of the account for the intended API action","name":"authorization","in":"header","required":true},{"type":"string","description":"Document Identifier","name":"document_id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"type":"object","$ref":"#/definitions/userapi.TransferDetailListResponse"}},"400":{"description":"Bad Request","schema":{"type":"object","$ref":"#/definitions/httputils.HTTPError"}},"404":{"description":"Not Found","schema":{"type":"object","$ref":"#/definitions/httputils.HTTPError"}},"500":{"description":"Internal Server Error","schema":{"type":"object","$ref":"#/definitions/httputils.HTTPError"}}}},"post":{"description":"Creates a new transfer detail extension on a document and anchors it.","consumes":["application/json"],"produces":["application/json"],"tags":["TransferDetail"],"summary":"Creates a new transfer detail extension on a document and anchors it.","operationId":"create_transfer_detail","parameters":[{"type":"string","description":"Hex encoded centrifuge ID of the account for the intended API action","name":"authorization","in":"header","required":true},{"description":"Transfer Detail Create Request","name":"body","in":"body","required":true,"schema":{"type":"object","$ref":"#/definitions/userapi.CreateTransferDetailRequest"}},{"type":"string","description":"Document Identifier","name":"document_id","in":"path","required":true}],"responses":{"201":{"description":"Created","schema":{"type":"object","$ref":"#/definitions/userapi.TransferDetailResponse"}},"400":{"description":"Bad Request","schema":{"type":"object","$ref":"#/definitions/httputils.HTTPError"}},"403":{"description":"Forbidden","schema":{"type":"object","$ref":"#/definitions/httputils.HTTPError"}},"500":{"description":"Internal Server Error","schema":{"type":"object","$ref":"#/definitions/httputils.HTTPError"}}}}},"/v1/documents/{document_id}/transfer_details/{transfer_id}":{"get":{"description":"Returns the latest version of the transfer detail.","produces":["application/json"],"tags":["Documents"],"summary":"Returns the latest version of the transfer detail.","operationId":"get_transfer","parameters":[{"type":"string","description":"Hex encoded centrifuge ID of the account for the intended API action","name":"authorization","in":"header","required":true},{"type":"string","description":"Document Identifier","name":"document_id","in":"path","required":true},{"type":"string","description":"Transfer Detail Identifier","name":"transfer_id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"type":"object","$ref":"#/definitions/userapi.TransferDetailResponse"}},"400":{"description":"Bad Request","schema":{"type":"object","$ref":"#/definitions/httputils.HTTPError"}},"404":{"description":"Not Found","schema":{"type":"object","$ref":"#/definitions/httputils.HTTPError"}},"500":{"description":"Internal Server Error","schema":{"type":"object","$ref":"#/definitions/httputils.HTTPError"}}}},"put":{"description":"Updates a new transfer detail extension on a document and anchors it.","consumes":["application/json"],"produces":["application/json"],"tags":["TransferDetail"],"summary":"Updates a new transfer detail extension on a document and anchors it.","operationId":"update_transfer_detail","parameters":[{"type":"string","description":"Hex encoded centrifuge ID of the account for the intended API action","name":"authorization","in":"header","required":true},{"description":"Transfer Detail Update Request","name":"body","in":"body","required":true,"schema":{"type":"object","$ref":"#/definitions/userapi.UpdateTransferDetailRequest"}},{"type":"string","description":"Document Identifier","name":"document_id","in":"path","required":true},{"type":"string","description":"Transfer Detail Identifier","name":"transfer_id","in":"path","required":true}],"responses":{"201":{"description":"Created","schema":{"type":"object","$ref":"#/definitions/userapi.TransferDetailResponse"}},"400":{"description":"Bad Request","schema":{"type":"object","$ref":"#/definitions/httputils.HTTPError"}},"403":{"description":"Forbidden","schema":{"type":"object","$ref":"#/definitions/httputils.HTTPError"}},"500":{"description":"Internal Server Error","schema":{"type":"object","$ref":"#/definitions/httputils.HTTPError"}}}}},"/v1/documents/{document_id}/versions/{version_id}":{"get":{"description":"Returns the specific version of the document.","produces":["application/json"],"tags":["Documents"],"summary":"Returns the specific version of the document.","operationId":"get_document_version","parameters":[{"type":"string","description":"Hex encoded centrifuge ID of the account for the intended API action","name":"authorization","in":"header","required":true},{"type":"string","description":"Document Identifier","name":"document_id","in":"path","required":true},{"type":"string","description":"Document Version Identifier","name":"version_id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"type":"object","$ref":"#/definitions/coreapi.DocumentResponse"}},"400":{"description":"Bad Request","schema":{"type":"object","$ref":"#/definitions/httputils.HTTPError"}},"404":{"description":"Not Found","schema":{"type":"object","$ref":"#/definitions/httputils.HTTPError"}},"500":{"description":"Internal Server Error","schema":{"type":"object","$ref":"#/definitions/httputils.HTTPError"}}}}},"/v1/documents/{document_id}/versions/{version_id}/proofs":{"post":{"description":"Generates proofs for the fields from a specific document version.","produces":["application/json"],"tags":["Documents"],"summary":"Generates proofs for the fields from a specific document version.","operationId":"generate_document_version_proofs","parameters":[{"type":"string","description":"Hex encoded centrifuge ID of the account for the intended API action","name":"authorization","in":"header","required":true},{"type":"string","description":"Document Identifier","name":"document_id","in":"path","required":true},{"type":"string","description":"Document Version Identifier","name":"version_id","in":"path","required":true},{"description":"Document proof request","name":"body","in":"body","required":true,"schema":{"type":"object","$ref":"#/definitions/coreapi.ProofsRequest"}}],"responses":{"200":{"description":"OK","schema":{"type":"object","$ref":"#/definitions/coreapi.ProofsResponse"}},"400":{"description":"Bad Request","schema":{"type":"object","$ref":"#/definitions/httputils.HTTPError"}},"500":{"description":"Internal Server Error","schema":{"type":"object","$ref":"#/definitions/httputils.HTTPError"}}}}},"/v1/jobs/{job_id}":{"get":{"description":"Returns the status of a given Job.","produces":["application/json"],"tags":["Jobs"],"summary":"Returns the status of a given Job.","operationId":"get_job_status","parameters":[{"type":"string","description":"Hex encoded centrifuge ID of the account for the intended API action","name":"authorization","in":"header","required":true},{"type":"string","description":"Job ID","name":"job_id","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"type":"object","$ref":"#/definitions/jobs.StatusResponse"}},"400":{"description":"Bad Request","schema":{"type":"object","$ref":"#/definitions/httputils.HTTPError"}},"404":{"description":"Not Found","schema":{"type":"object","$ref":"#/definitions/httputils.HTTPError"}}}}},"/v1/nfts/registries/{registry_address}/mint":{"post":{"description":"Mints an NFT against a document.","consumes":["application/json"],"produces":["application/json"],"tags":["NFT"],"summary":"Mints an NFT against a document.","operationId":"mint_nft","parameters":[{"type":"string","description":"Hex encoded centrifuge ID of the account for the intended API action","name":"authorization","in":"header","required":true},{"type":"string","description":"NFT registry address in hex","name":"registry_address","in":"path","required":true},{"description":"Mint NFT request","name":"body","in":"body","required":true,"schema":{"type":"object","$ref":"#/definitions/coreapi.MintNFTRequest"}}],"responses":{"201":{"description":"Created","schema":{"type":"object","$ref":"#/definitions/coreapi.MintNFTResponse"}},"400":{"description":"Bad Request","schema":{"type":"object","$ref":"#/definitions/httputils.HTTPError"}},"500":{"description":"Internal Server Error","schema":{"type":"object","$ref":"#/definitions/httputils.HTTPError"}}}}},"/v1/nfts/registries/{registry_address}/tokens/{token_id}/owner":{"get":{"description":"Returns the Owner of the given NFT.","produces":["application/json"],"tags":["NFT"],"summary":"Returns the Owner of the given NFT.","operationId":"owner_of_nft","parameters":[{"type":"string","description":"Hex encoded centrifuge ID of the account for the intended API action","name":"authorization","in":"header","required":true},{"type":"string","description":"NFT token ID in hex","name":"token_id","in":"path","required":true},{"type":"string","description":"Registry address in hex","name":"registry_address","in":"path","required":true}],"responses":{"200":{"description":"OK","schema":{"type":"object","$ref":"#/definitions/coreapi.NFTOwnerResponse"}},"400":{"description":"Bad Request","schema":{"type":"object","$ref":"#/definitions/httputils.HTTPError"}}}}},"/v1/nfts/registries/{registry_address}/tokens/{token_id}/transfer":{"post":{"description":"Transfers given NFT to provide address.","consumes":["application/json"],"produces":["application/json"],"tags":["NFT"],"summary":"Transfers given NFT to provide address.","operationId":"transfer_nft","parameters":[{"type":"string","description":"Hex encoded centrifuge ID of the account for the intended API action","name":"authorization","in":"header","required":true},{"type":"string","description":"NFT registry address in hex","name":"registry_address","in":"path","required":true},{"type":"string","description":"NFT token ID in hex","name":"token_id","in":"path","required":true},{"description":"Mint NFT request","name":"body","in":"body","required":true,"schema":{"type":"object","$ref":"#/definitions/coreapi.TransferNFTRequest"}}],"responses":{"200":{"description":"OK","schema":{"type":"object","$ref":"#/definitions/coreapi.TransferNFTResponse"}},"400":{"description":"Bad Request","schema":{"type":"object","$ref":"#/definitions/httputils.HTTPError"}},"500":{"description":"Internal Server Error","schema":{"type":"object","$ref":"#/definitions/httputils.HTTPError"}}}}},"/v1/entities":{"post":{"description":"Creates an entity","operationId":"Create","responses":{"200":{"description":"","schema":{"$ref":"#/definitions/entityEntityResponse"}}},"parameters":[{"name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/entityEntityCreatePayload"}},{"name":"authorization","in":"header","description":"Hex encoded centrifuge ID of the account for the intended API action","required":true,"type":"string"}],"tags":["EntityService"]}},"/v1/entities/{document_id}":{"get":{"description":"Get the current entity","operationId":"Get","responses":{"200":{"description":"","schema":{"$ref":"#/definitions/entityEntityResponse"}}},"parameters":[{"name":"document_id","in":"path","required":true,"type":"string"},{"name":"authorization","in":"header","description":"Hex encoded centrifuge ID of the account for the intended API action","required":true,"type":"string"}],"tags":["EntityService"]},"put":{"description":"Updates an entity","operationId":"Update","responses":{"200":{"description":"","schema":{"$ref":"#/definitions/entityEntityResponse"}}},"parameters":[{"name":"document_id","in":"path","required":true,"type":"string"},{"name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/entityEntityUpdatePayload"}},{"name":"authorization","in":"header","description":"Hex encoded centrifuge ID of the account for the intended API action","required":true,"type":"string"}],"tags":["EntityService"]}},"/v1/entities/{document_id}/revoke":{"post":{"summary":"Entity Relation Revoke","description":"revoke an entity document share","operationId":"Revoke","responses":{"200":{"description":"","schema":{"$ref":"#/definitions/entityRelationshipResponse"}}},"parameters":[{"name":"document_id","description":"entity identifier","in":"path","required":true,"type":"string"},{"name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/entityRelationshipPayload"}},{"name":"authorization","in":"header","description":"Hex encoded centrifuge ID of the account for the intended API action","required":true,"type":"string"}],"tags":["EntityService"]}},"/v1/entities/{document_id}/share":{"post":{"summary":"Entity Relation Share","description":"Share the entity document with others","operationId":"Share","responses":{"200":{"description":"","schema":{"$ref":"#/definitions/entityRelationshipResponse"}}},"parameters":[{"name":"document_id","description":"entity identifier","in":"path","required":true,"type":"string"},{"name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/entityRelationshipPayload"}},{"name":"authorization","in":"header","description":"Hex encoded centrifuge ID of the account for the intended API action","required":true,"type":"string"}],"tags":["EntityService"]}},"/v1/relationships/{relationship_id}/entity":{"get":{"summary":"Entity Relation Get","description":"Get entity from business partner","operationId":"GetEntityByRelationship","responses":{"200":{"description":"","schema":{"$ref":"#/definitions/entityEntityResponse"}}},"parameters":[{"name":"relationship_id","in":"path","required":true,"type":"string"},{"name":"authorization","in":"header","description":"Hex encoded centrifuge ID of the account for the intended API action","required":true,"type":"string"}],"tags":["EntityService"]}},"/v1/documents/{document_id}/funding_agreements":{"get":{"description":"Get all funding agreements of a latest document","operationId":"GetList","responses":{"200":{"description":"","schema":{"$ref":"#/definitions/funFundingListResponse"}}},"parameters":[{"name":"document_id","in":"path","required":true,"type":"string"},{"name":"authorization","in":"header","description":"Hex encoded centrifuge ID of the account for the intended API action","required":true,"type":"string"}],"tags":["FundingService"]},"post":{"description":"Adds a funding to a document","operationId":"Create","responses":{"200":{"description":"","schema":{"$ref":"#/definitions/funFundingResponse"}}},"parameters":[{"name":"document_id","in":"path","required":true,"type":"string"},{"name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/funFundingCreatePayload"}},{"name":"authorization","in":"header","description":"Hex encoded centrifuge ID of the account for the intended API action","required":true,"type":"string"}],"tags":["FundingService"]}},"/v1/documents/{document_id}/funding_agreements/{agreement_id}":{"get":{"description":"Get a funding agreement of a latest document","operationId":"Get","responses":{"200":{"description":"","schema":{"$ref":"#/definitions/funFundingResponse"}}},"parameters":[{"name":"document_id","in":"path","required":true,"type":"string"},{"name":"agreement_id","in":"path","required":true,"type":"string"},{"name":"authorization","in":"header","description":"Hex encoded centrifuge ID of the account for the intended API action","required":true,"type":"string"}],"tags":["FundingService"]},"put":{"description":"Updates a funding agreement in a document","operationId":"Update","responses":{"200":{"description":"","schema":{"$ref":"#/definitions/funFundingResponse"}}},"parameters":[{"name":"document_id","in":"path","required":true,"type":"string"},{"name":"agreement_id","in":"path","required":true,"type":"string"},{"name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/funFundingUpdatePayload"}},{"name":"authorization","in":"header","description":"Hex encoded centrifuge ID of the account for the intended API action","required":true,"type":"string"}],"tags":["FundingService"]}},"/v1/documents/{document_id}/funding_agreements/{agreement_id}/sign":{"post":{"description":"Signs funding agreement in a document","operationId":"Sign","responses":{"200":{"description":"","schema":{"$ref":"#/definitions/funFundingResponse"}}},"parameters":[{"name":"document_id","in":"path","required":true,"type":"string"},{"name":"agreement_id","in":"path","required":true,"type":"string"},{"name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/funRequest"}},{"name":"authorization","in":"header","description":"Hex encoded centrifuge ID of the account for the intended API action","required":true,"type":"string"}],"tags":["FundingService"]}},"/v1/documents/{document_id}/versions/{version_id}/funding_agreements":{"get":{"description":"Get all funding agreements of a document version","operationId":"GetListVersion","responses":{"200":{"description":"","schema":{"$ref":"#/definitions/funFundingListResponse"}}},"parameters":[{"name":"document_id","in":"path","required":true,"type":"string"},{"name":"version_id","in":"path","required":true,"type":"string"},{"name":"authorization","in":"header","description":"Hex encoded centrifuge ID of the account for the intended API action","required":true,"type":"string"}],"tags":["FundingService"]}},"/v1/documents/{document_id}/versions/{version_id}/funding_agreements/{agreement_id}":{"get":{"description":"Get a funding agreement of a document version","operationId":"GetVersion","responses":{"200":{"description":"","schema":{"$ref":"#/definitions/funFundingResponse"}}},"parameters":[{"name":"document_id","in":"path","required":true,"type":"string"},{"name":"version_id","in":"path","required":true,"type":"string"},{"name":"agreement_id","in":"path","required":true,"type":"string"},{"name":"authorization","in":"header","description":"Hex encoded centrifuge ID of the account for the intended API action","required":true,"type":"string"}],"tags":["FundingService"]}},"/v1/invoices":{"post":{"description":"Creates an invoice","operationId":"Create","responses":{"200":{"description":"","schema":{"$ref":"#/definitions/invInvoiceResponse"}}},"parameters":[{"name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/invInvoiceCreatePayload"}},{"name":"authorization","in":"header","description":"Hex encoded centrifuge ID of the account for the intended API action","required":true,"type":"string"}],"tags":["InvoiceService"]}},"/v1/invoices/{document_id}":{"get":{"description":"Get the current invoice","operationId":"Get","responses":{"200":{"description":"","schema":{"$ref":"#/definitions/invInvoiceResponse"}}},"parameters":[{"name":"document_id","in":"path","required":true,"type":"string"},{"name":"authorization","in":"header","description":"Hex encoded centrifuge ID of the account for the intended API action","required":true,"type":"string"}],"tags":["InvoiceService"]},"put":{"description":"Updates an invoice","operationId":"Update","responses":{"200":{"description":"","schema":{"$ref":"#/definitions/invInvoiceResponse"}}},"parameters":[{"name":"document_id","in":"path","required":true,"type":"string"},{"name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/invInvoiceUpdatePayload"}},{"name":"authorization","in":"header","description":"Hex encoded centrifuge ID of the account for the intended API action","required":true,"type":"string"}],"tags":["InvoiceService"]}},"/v1/invoices/{document_id}/versions/{version_id}":{"get":{"description":"Get a specific version of an invoice","operationId":"GetVersion","responses":{"200":{"description":"","schema":{"$ref":"#/definitions/invInvoiceResponse"}}},"parameters":[{"name":"document_id","in":"path","required":true,"type":"string"},{"name":"version_id","in":"path","required":true,"type":"string"},{"name":"authorization","in":"header","description":"Hex encoded centrifuge ID of the account for the intended API action","required":true,"type":"string"}],"tags":["InvoiceService"]}},"/v1/invoices/{document_id}/mint/unpaid":{"post":{"description":"Mints an NFT out of an Unpaid Centrifuge Invoice","operationId":"MintInvoiceUnpaidNFT","responses":{"200":{"description":"","schema":{"$ref":"#/definitions/nftNFTMintResponse"}}},"parameters":[{"name":"document_id","description":"Invoice Document identifier","in":"path","required":true,"type":"string"},{"name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/nftNFTMintInvoiceUnpaidRequest"}},{"name":"authorization","in":"header","description":"Hex encoded centrifuge ID of the account for the intended API action","required":true,"type":"string"}],"tags":["NFTService"]}},"/dummy":{"get":{"description":"Dummy notification endpoint","operationId":"Notify","responses":{"200":{"description":"","schema":{"$ref":"#/definitions/notificationNotificationMessage"}}},"tags":["NotificationDummyService"],"parameters":[{"name":"authorization","in":"header","description":"Hex encoded centrifuge ID of the account for the intended API action","required":true,"type":"string"}]}},"/v1/purchase_orders":{"post":{"description":"Creates a purchase order","operationId":"Create","responses":{"200":{"description":"","schema":{"$ref":"#/definitions/poPurchaseOrderResponse"}}},"parameters":[{"name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/poPurchaseOrderCreatePayload"}},{"name":"authorization","in":"header","description":"Hex encoded centrifuge ID of the account for the intended API action","required":true,"type":"string"}],"tags":["PurchaseOrderService"]}},"/v1/purchase_orders/{document_id}":{"get":{"description":"Get the current version of a purchase order","operationId":"Get","responses":{"200":{"description":"","schema":{"$ref":"#/definitions/poPurchaseOrderResponse"}}},"parameters":[{"name":"document_id","in":"path","required":true,"type":"string"},{"name":"authorization","in":"header","description":"Hex encoded centrifuge ID of the account for the intended API action","required":true,"type":"string"}],"tags":["PurchaseOrderService"]},"put":{"description":"Updates a purchase order","operationId":"Update","responses":{"200":{"description":"","schema":{"$ref":"#/definitions/poPurchaseOrderResponse"}}},"parameters":[{"name":"document_id","in":"path","required":true,"type":"string"},{"name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/poPurchaseOrderUpdatePayload"}},{"name":"authorization","in":"header","description":"Hex encoded centrifuge ID of the account for the intended API action","required":true,"type":"string"}],"tags":["PurchaseOrderService"]}},"/v1/purchase_orders/{document_id}/versions/{version_id}":{"get":{"description":"Get a specific version of a purchase order","operationId":"GetVersion","responses":{"200":{"description":"","schema":{"$ref":"#/definitions/poPurchaseOrderResponse"}}},"parameters":[{"name":"document_id","in":"path","required":true,"type":"string"},{"name":"version_id","in":"path","required":true,"type":"string"},{"name":"authorization","in":"header","description":"Hex encoded centrifuge ID of the account for the intended API action","required":true,"type":"string"}],"tags":["PurchaseOrderService"]}}}} \ No newline at end of file diff --git a/protobufs/gen/swagger/account/service.swagger.json b/protobufs/gen/swagger/account/service.swagger.json deleted file mode 100644 index 28200b315..000000000 --- a/protobufs/gen/swagger/account/service.swagger.json +++ /dev/null @@ -1,206 +0,0 @@ -{ - "swagger": "2.0", - "info": { - "title": "account/service.proto", - "version": "version not set" - }, - "schemes": [ - "http", - "https" - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "paths": { - "/v1/accounts": { - "get": { - "description": "Get All Accounts", - "operationId": "GetAllAccounts", - "responses": { - "200": { - "description": "", - "schema": { - "$ref": "#/definitions/accountGetAllAccountResponse" - } - } - }, - "tags": [ - "AccountService" - ] - }, - "post": { - "description": "Creates an Account", - "operationId": "CreateAccount", - "responses": { - "200": { - "description": "", - "schema": { - "$ref": "#/definitions/accountAccountData" - } - } - }, - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/accountAccountData" - } - } - ], - "tags": [ - "AccountService" - ] - } - }, - "/v1/accounts/generate": { - "post": { - "description": "Generates an Account taking defaults based on the main account", - "operationId": "GenerateAccount", - "responses": { - "200": { - "description": "", - "schema": { - "$ref": "#/definitions/accountAccountData" - } - } - }, - "tags": [ - "AccountService" - ] - } - }, - "/v1/accounts/{account_id}": { - "get": { - "description": "Get Account", - "operationId": "GetAccount", - "responses": { - "200": { - "description": "", - "schema": { - "$ref": "#/definitions/accountAccountData" - } - } - }, - "parameters": [ - { - "name": "account_id", - "in": "path", - "required": true, - "type": "string" - } - ], - "tags": [ - "AccountService" - ] - }, - "put": { - "description": "Updates an Account", - "operationId": "UpdateAccount", - "responses": { - "200": { - "description": "", - "schema": { - "$ref": "#/definitions/accountAccountData" - } - } - }, - "parameters": [ - { - "name": "account_id", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/accountUpdateAccountRequest" - } - } - ], - "tags": [ - "AccountService" - ] - } - } - }, - "definitions": { - "accountAccountData": { - "type": "object", - "properties": { - "eth_account": { - "$ref": "#/definitions/accountEthereumAccount" - }, - "eth_default_account_name": { - "type": "string" - }, - "receive_event_notification_endpoint": { - "type": "string" - }, - "identity_id": { - "type": "string" - }, - "signing_key_pair": { - "$ref": "#/definitions/accountKeyPair" - }, - "p2p_key_pair": { - "$ref": "#/definitions/accountKeyPair" - } - } - }, - "accountEthereumAccount": { - "type": "object", - "properties": { - "address": { - "type": "string" - }, - "key": { - "type": "string" - }, - "password": { - "type": "string" - } - } - }, - "accountGetAllAccountResponse": { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/accountAccountData" - } - } - } - }, - "accountKeyPair": { - "type": "object", - "properties": { - "pub": { - "type": "string" - }, - "pvt": { - "type": "string" - } - } - }, - "accountUpdateAccountRequest": { - "type": "object", - "properties": { - "account_id": { - "type": "string" - }, - "data": { - "$ref": "#/definitions/accountAccountData" - } - } - } - } -} diff --git a/protobufs/gen/swagger/api/api.swagger.json b/protobufs/gen/swagger/api/api.swagger.json deleted file mode 100644 index 75f1c3ce7..000000000 --- a/protobufs/gen/swagger/api/api.swagger.json +++ /dev/null @@ -1,1474 +0,0 @@ -{ - "schemes": [ - "http" - ], - "swagger": "2.0", - "info": { - "description": "Centrifuge OS Node API", - "title": "Centrifuge OS Node API", - "contact": { - "name": "Centrifuge", - "url": "https://github.com/centrifuge/go-centrifuge", - "email": "hello@centrifuge.io" - }, - "license": { - "name": "MIT" - }, - "version": "0.0.5" - }, - "host": "localhost:8082", - "basePath": "/", - "paths": { - "/ping": { - "get": { - "description": "returns node version and network", - "produces": [ - "application/json" - ], - "tags": [ - "Health" - ], - "summary": "Health check for Node", - "operationId": "ping", - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object", - "$ref": "#/definitions/health.Pong" - } - } - } - } - }, - "/v1/accounts/{account_id}/sign": { - "post": { - "description": "Signs and returns the signature of the Payload.", - "produces": [ - "application/json" - ], - "tags": [ - "Accounts" - ], - "summary": "Signs and returns the signature of the Payload.", - "operationId": "account_sign", - "parameters": [ - { - "type": "string", - "description": "Account ID", - "name": "account_id", - "in": "path", - "required": true - }, - { - "description": "Sign request", - "name": "body", - "in": "body", - "required": true, - "schema": { - "type": "object", - "$ref": "#/definitions/coreapi.SignRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object", - "$ref": "#/definitions/coreapi.SignResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "type": "object", - "$ref": "#/definitions/httputils.HTTPError" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "type": "object", - "$ref": "#/definitions/httputils.HTTPError" - } - } - } - } - }, - "/v1/documents": { - "post": { - "description": "Creates a new document and anchors it.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Documents" - ], - "summary": "Creates a new document and anchors it.", - "operationId": "create_document", - "parameters": [ - { - "type": "string", - "description": "Hex encoded centrifuge ID of the account for the intended API action", - "name": "authorization", - "in": "header", - "required": true - }, - { - "description": "Document Create request", - "name": "body", - "in": "body", - "required": true, - "schema": { - "type": "object", - "$ref": "#/definitions/coreapi.CreateDocumentRequest" - } - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "type": "object", - "$ref": "#/definitions/coreapi.DocumentResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "type": "object", - "$ref": "#/definitions/httputils.HTTPError" - } - }, - "403": { - "description": "Forbidden", - "schema": { - "type": "object", - "$ref": "#/definitions/httputils.HTTPError" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "type": "object", - "$ref": "#/definitions/httputils.HTTPError" - } - } - } - } - }, - "/v1/documents/{document_id}": { - "get": { - "description": "Returns the latest version of the document.", - "produces": [ - "application/json" - ], - "tags": [ - "Documents" - ], - "summary": "Returns the latest version of the document.", - "operationId": "get_document", - "parameters": [ - { - "type": "string", - "description": "Hex encoded centrifuge ID of the account for the intended API action", - "name": "authorization", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Document Identifier", - "name": "document_id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object", - "$ref": "#/definitions/coreapi.DocumentResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "type": "object", - "$ref": "#/definitions/httputils.HTTPError" - } - }, - "404": { - "description": "Not Found", - "schema": { - "type": "object", - "$ref": "#/definitions/httputils.HTTPError" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "type": "object", - "$ref": "#/definitions/httputils.HTTPError" - } - } - } - }, - "put": { - "description": "Updates an existing document and anchors it.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Documents" - ], - "summary": "Updates an existing document and anchors it.", - "operationId": "update_document", - "parameters": [ - { - "type": "string", - "description": "Hex encoded centrifuge ID of the account for the intended API action", - "name": "authorization", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Document Identifier", - "name": "document_id", - "in": "path", - "required": true - }, - { - "description": "Document Update request", - "name": "body", - "in": "body", - "required": true, - "schema": { - "type": "object", - "$ref": "#/definitions/coreapi.CreateDocumentRequest" - } - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "type": "object", - "$ref": "#/definitions/coreapi.DocumentResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "type": "object", - "$ref": "#/definitions/httputils.HTTPError" - } - }, - "403": { - "description": "Forbidden", - "schema": { - "type": "object", - "$ref": "#/definitions/httputils.HTTPError" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "type": "object", - "$ref": "#/definitions/httputils.HTTPError" - } - } - } - } - }, - "/v1/documents/{document_id}/proofs": { - "post": { - "description": "Generates proofs for the fields from latest version of the document.", - "produces": [ - "application/json" - ], - "tags": [ - "Documents" - ], - "summary": "Generates proofs for the fields from latest version of the document.", - "operationId": "generate_document_proofs", - "parameters": [ - { - "type": "string", - "description": "Hex encoded centrifuge ID of the account for the intended API action", - "name": "authorization", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Document Identifier", - "name": "document_id", - "in": "path", - "required": true - }, - { - "description": "Document proof request", - "name": "body", - "in": "body", - "required": true, - "schema": { - "type": "object", - "$ref": "#/definitions/coreapi.ProofsRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object", - "$ref": "#/definitions/coreapi.ProofsResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "type": "object", - "$ref": "#/definitions/httputils.HTTPError" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "type": "object", - "$ref": "#/definitions/httputils.HTTPError" - } - } - } - } - }, - "/v1/documents/{document_id}/transfer_details": { - "get": { - "description": "Returns a list of the latest versions of all transfer details on the document.", - "produces": [ - "application/json" - ], - "tags": [ - "Documents" - ], - "summary": "Returns a list of the latest versions of all transfer details on the document.", - "operationId": "get_transfer", - "parameters": [ - { - "type": "string", - "description": "Hex encoded centrifuge ID of the account for the intended API action", - "name": "authorization", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Document Identifier", - "name": "document_id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object", - "$ref": "#/definitions/userapi.TransferDetailListResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "type": "object", - "$ref": "#/definitions/httputils.HTTPError" - } - }, - "404": { - "description": "Not Found", - "schema": { - "type": "object", - "$ref": "#/definitions/httputils.HTTPError" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "type": "object", - "$ref": "#/definitions/httputils.HTTPError" - } - } - } - }, - "post": { - "description": "Creates a new transfer detail extension on a document and anchors it.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "TransferDetail" - ], - "summary": "Creates a new transfer detail extension on a document and anchors it.", - "operationId": "create_transfer_detail", - "parameters": [ - { - "type": "string", - "description": "Hex encoded centrifuge ID of the account for the intended API action", - "name": "authorization", - "in": "header", - "required": true - }, - { - "description": "Transfer Detail Create Request", - "name": "body", - "in": "body", - "required": true, - "schema": { - "type": "object", - "$ref": "#/definitions/userapi.CreateTransferDetailRequest" - } - }, - { - "type": "string", - "description": "Document Identifier", - "name": "document_id", - "in": "path", - "required": true - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "type": "object", - "$ref": "#/definitions/userapi.TransferDetailResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "type": "object", - "$ref": "#/definitions/httputils.HTTPError" - } - }, - "403": { - "description": "Forbidden", - "schema": { - "type": "object", - "$ref": "#/definitions/httputils.HTTPError" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "type": "object", - "$ref": "#/definitions/httputils.HTTPError" - } - } - } - } - }, - "/v1/documents/{document_id}/transfer_details/{transfer_id}": { - "get": { - "description": "Returns the latest version of the transfer detail.", - "produces": [ - "application/json" - ], - "tags": [ - "Documents" - ], - "summary": "Returns the latest version of the transfer detail.", - "operationId": "get_transfer", - "parameters": [ - { - "type": "string", - "description": "Hex encoded centrifuge ID of the account for the intended API action", - "name": "authorization", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Document Identifier", - "name": "document_id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Transfer Detail Identifier", - "name": "transfer_id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object", - "$ref": "#/definitions/userapi.TransferDetailResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "type": "object", - "$ref": "#/definitions/httputils.HTTPError" - } - }, - "404": { - "description": "Not Found", - "schema": { - "type": "object", - "$ref": "#/definitions/httputils.HTTPError" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "type": "object", - "$ref": "#/definitions/httputils.HTTPError" - } - } - } - }, - "put": { - "description": "Updates a new transfer detail extension on a document and anchors it.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "TransferDetail" - ], - "summary": "Updates a new transfer detail extension on a document and anchors it.", - "operationId": "update_transfer_detail", - "parameters": [ - { - "type": "string", - "description": "Hex encoded centrifuge ID of the account for the intended API action", - "name": "authorization", - "in": "header", - "required": true - }, - { - "description": "Transfer Detail Update Request", - "name": "body", - "in": "body", - "required": true, - "schema": { - "type": "object", - "$ref": "#/definitions/userapi.UpdateTransferDetailRequest" - } - }, - { - "type": "string", - "description": "Document Identifier", - "name": "document_id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Transfer Detail Identifier", - "name": "transfer_id", - "in": "path", - "required": true - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "type": "object", - "$ref": "#/definitions/userapi.TransferDetailResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "type": "object", - "$ref": "#/definitions/httputils.HTTPError" - } - }, - "403": { - "description": "Forbidden", - "schema": { - "type": "object", - "$ref": "#/definitions/httputils.HTTPError" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "type": "object", - "$ref": "#/definitions/httputils.HTTPError" - } - } - } - } - }, - "/v1/documents/{document_id}/versions/{version_id}": { - "get": { - "description": "Returns the specific version of the document.", - "produces": [ - "application/json" - ], - "tags": [ - "Documents" - ], - "summary": "Returns the specific version of the document.", - "operationId": "get_document_version", - "parameters": [ - { - "type": "string", - "description": "Hex encoded centrifuge ID of the account for the intended API action", - "name": "authorization", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Document Identifier", - "name": "document_id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Document Version Identifier", - "name": "version_id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object", - "$ref": "#/definitions/coreapi.DocumentResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "type": "object", - "$ref": "#/definitions/httputils.HTTPError" - } - }, - "404": { - "description": "Not Found", - "schema": { - "type": "object", - "$ref": "#/definitions/httputils.HTTPError" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "type": "object", - "$ref": "#/definitions/httputils.HTTPError" - } - } - } - } - }, - "/v1/documents/{document_id}/versions/{version_id}/proofs": { - "post": { - "description": "Generates proofs for the fields from a specific document version.", - "produces": [ - "application/json" - ], - "tags": [ - "Documents" - ], - "summary": "Generates proofs for the fields from a specific document version.", - "operationId": "generate_document_version_proofs", - "parameters": [ - { - "type": "string", - "description": "Hex encoded centrifuge ID of the account for the intended API action", - "name": "authorization", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Document Identifier", - "name": "document_id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Document Version Identifier", - "name": "version_id", - "in": "path", - "required": true - }, - { - "description": "Document proof request", - "name": "body", - "in": "body", - "required": true, - "schema": { - "type": "object", - "$ref": "#/definitions/coreapi.ProofsRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object", - "$ref": "#/definitions/coreapi.ProofsResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "type": "object", - "$ref": "#/definitions/httputils.HTTPError" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "type": "object", - "$ref": "#/definitions/httputils.HTTPError" - } - } - } - } - }, - "/v1/jobs/{job_id}": { - "get": { - "description": "Returns the status of a given Job.", - "produces": [ - "application/json" - ], - "tags": [ - "Jobs" - ], - "summary": "Returns the status of a given Job.", - "operationId": "get_job_status", - "parameters": [ - { - "type": "string", - "description": "Hex encoded centrifuge ID of the account for the intended API action", - "name": "authorization", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "Job ID", - "name": "job_id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object", - "$ref": "#/definitions/jobs.StatusResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "type": "object", - "$ref": "#/definitions/httputils.HTTPError" - } - }, - "404": { - "description": "Not Found", - "schema": { - "type": "object", - "$ref": "#/definitions/httputils.HTTPError" - } - } - } - } - }, - "/v1/nfts/registries/{registry_address}/mint": { - "post": { - "description": "Mints an NFT against a document.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "NFT" - ], - "summary": "Mints an NFT against a document.", - "operationId": "mint_nft", - "parameters": [ - { - "type": "string", - "description": "Hex encoded centrifuge ID of the account for the intended API action", - "name": "authorization", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "NFT registry address in hex", - "name": "registry_address", - "in": "path", - "required": true - }, - { - "description": "Mint NFT request", - "name": "body", - "in": "body", - "required": true, - "schema": { - "type": "object", - "$ref": "#/definitions/coreapi.MintNFTRequest" - } - } - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "type": "object", - "$ref": "#/definitions/coreapi.MintNFTResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "type": "object", - "$ref": "#/definitions/httputils.HTTPError" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "type": "object", - "$ref": "#/definitions/httputils.HTTPError" - } - } - } - } - }, - "/v1/nfts/registries/{registry_address}/tokens/{token_id}/owner": { - "get": { - "description": "Returns the Owner of the given NFT.", - "produces": [ - "application/json" - ], - "tags": [ - "NFT" - ], - "summary": "Returns the Owner of the given NFT.", - "operationId": "owner_of_nft", - "parameters": [ - { - "type": "string", - "description": "Hex encoded centrifuge ID of the account for the intended API action", - "name": "authorization", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "NFT token ID in hex", - "name": "token_id", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "Registry address in hex", - "name": "registry_address", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object", - "$ref": "#/definitions/coreapi.NFTOwnerResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "type": "object", - "$ref": "#/definitions/httputils.HTTPError" - } - } - } - } - }, - "/v1/nfts/registries/{registry_address}/tokens/{token_id}/transfer": { - "post": { - "description": "Transfers given NFT to provide address.", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "NFT" - ], - "summary": "Transfers given NFT to provide address.", - "operationId": "transfer_nft", - "parameters": [ - { - "type": "string", - "description": "Hex encoded centrifuge ID of the account for the intended API action", - "name": "authorization", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "NFT registry address in hex", - "name": "registry_address", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "NFT token ID in hex", - "name": "token_id", - "in": "path", - "required": true - }, - { - "description": "Mint NFT request", - "name": "body", - "in": "body", - "required": true, - "schema": { - "type": "object", - "$ref": "#/definitions/coreapi.TransferNFTRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "type": "object", - "$ref": "#/definitions/coreapi.TransferNFTResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "type": "object", - "$ref": "#/definitions/httputils.HTTPError" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "type": "object", - "$ref": "#/definitions/httputils.HTTPError" - } - } - } - } - } - }, - "definitions": { - "byteutils.HexBytes": { - "type": "array", - "items": {} - }, - "coreapi.Attribute": { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": [ - "integer", - "decimal", - "string", - "bytes", - "timestamp" - ] - }, - "value": { - "type": "string" - } - } - }, - "coreapi.AttributeMap": { - "type": "object", - "additionalProperties": { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": [ - "integer", - "decimal", - "string", - "bytes", - "timestamp" - ] - }, - "value": { - "type": "string" - } - } - } - }, - "coreapi.CreateDocumentRequest": { - "type": "object", - "properties": { - "attributes": { - "type": "object", - "$ref": "#/definitions/coreapi.AttributeMap" - }, - "data": { - "type": "object" - }, - "read_access": { - "type": "array", - "items": { - "type": "string" - } - }, - "scheme": { - "type": "string", - "enum": [ - "generic", - "invoice", - "purchase_order", - "entity" - ] - }, - "write_access": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "coreapi.DocumentResponse": { - "type": "object", - "properties": { - "attributes": { - "type": "object", - "$ref": "#/definitions/coreapi.AttributeMap" - }, - "data": { - "type": "object" - }, - "header": { - "type": "object", - "$ref": "#/definitions/coreapi.ResponseHeader" - }, - "scheme": { - "type": "string", - "enum": [ - "generic", - "invoice", - "purchase_order", - "entity" - ] - } - } - }, - "coreapi.MintNFTRequest": { - "type": "object", - "properties": { - "deposit_address": { - "type": "string" - }, - "document_id": { - "type": "string" - }, - "grant_nft_access": { - "type": "boolean" - }, - "proof_fields": { - "type": "array", - "items": { - "type": "string" - } - }, - "submit_nft_owner_access_proof": { - "type": "boolean" - }, - "submit_token_proof": { - "type": "boolean" - } - } - }, - "coreapi.MintNFTResponse": { - "type": "object", - "properties": { - "deposit_address": { - "type": "string" - }, - "document_id": { - "type": "string" - }, - "header": { - "type": "object", - "$ref": "#/definitions/coreapi.NFTResponseHeader" - }, - "registry_address": { - "type": "string" - }, - "token_id": { - "type": "string" - } - } - }, - "coreapi.NFT": { - "type": "object", - "properties": { - "owner": { - "type": "string" - }, - "registry": { - "type": "string" - }, - "token_id": { - "type": "string" - }, - "token_index": { - "type": "string" - } - } - }, - "coreapi.NFTOwnerResponse": { - "type": "object", - "properties": { - "owner": { - "type": "string" - }, - "registry_address": { - "type": "string" - }, - "token_id": { - "type": "string" - } - } - }, - "coreapi.NFTResponseHeader": { - "type": "object", - "properties": { - "job_id": { - "type": "string" - } - } - }, - "coreapi.Proof": { - "type": "object", - "properties": { - "hash": { - "type": "string" - }, - "property": { - "type": "string" - }, - "salt": { - "type": "string" - }, - "sorted_hashes": { - "type": "array", - "items": { - "type": "string" - } - }, - "value": { - "type": "string" - } - } - }, - "coreapi.ProofResponseHeader": { - "type": "object", - "properties": { - "document_id": { - "type": "string" - }, - "state": { - "type": "string" - }, - "version_id": { - "type": "string" - } - } - }, - "coreapi.ProofsRequest": { - "type": "object", - "properties": { - "fields": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "coreapi.ProofsResponse": { - "type": "object", - "properties": { - "field_proofs": { - "type": "array", - "items": { - "$ref": "#/definitions/coreapi.Proof" - } - }, - "header": { - "type": "object", - "$ref": "#/definitions/coreapi.ProofResponseHeader" - } - } - }, - "coreapi.ResponseHeader": { - "type": "object", - "properties": { - "author": { - "type": "string" - }, - "created_at": { - "type": "string" - }, - "document_id": { - "type": "string" - }, - "job_id": { - "type": "string" - }, - "nfts": { - "type": "array", - "items": { - "$ref": "#/definitions/coreapi.NFT" - } - }, - "read_access": { - "type": "array", - "items": { - "type": "string" - } - }, - "version_id": { - "type": "string" - }, - "write_access": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "coreapi.SignRequest": { - "type": "object", - "properties": { - "payload": { - "type": "string" - } - } - }, - "coreapi.SignResponse": { - "type": "object", - "properties": { - "payload": { - "type": "string" - }, - "public_key": { - "type": "string" - }, - "signature": { - "type": "string" - }, - "signer_id": { - "type": "string" - } - } - }, - "coreapi.TransferNFTRequest": { - "type": "object", - "properties": { - "to": { - "type": "string" - } - } - }, - "coreapi.TransferNFTResponse": { - "type": "object", - "properties": { - "header": { - "type": "object", - "$ref": "#/definitions/coreapi.NFTResponseHeader" - }, - "registry_address": { - "type": "string" - }, - "to": { - "type": "string" - }, - "token_id": { - "type": "string" - } - } - }, - "health.Pong": { - "type": "object", - "properties": { - "network": { - "type": "string" - }, - "version": { - "type": "string" - } - } - }, - "httputils.HTTPError": { - "type": "object", - "properties": { - "message": { - "type": "string" - } - } - }, - "jobs.StatusResponse": { - "type": "object", - "properties": { - "job_id": { - "type": "string" - }, - "last_updated": { - "type": "string" - }, - "message": { - "type": "string" - }, - "status": { - "type": "string" - } - } - }, - "transferdetails.TransferDetailData": { - "type": "object", - "properties": { - "amount": { - "type": "string" - }, - "currency": { - "type": "string" - }, - "data": { - "type": "string" - }, - "recipient_id": { - "type": "string" - }, - "scheduled_date": { - "type": "string" - }, - "sender_id": { - "type": "string" - }, - "settlement_date": { - "type": "string" - }, - "settlement_reference": { - "type": "string" - }, - "status": { - "type": "string" - }, - "transfer_id": { - "type": "string" - }, - "transfer_type": { - "type": "string" - } - } - }, - "userapi.CreateTransferDetailRequest": { - "type": "object", - "properties": { - "data": { - "type": "object", - "$ref": "#/definitions/transferdetails.TransferDetailData" - }, - "document_id": { - "type": "string" - } - } - }, - "userapi.TransferDetailListResponse": { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/transferdetails.TransferDetailData" - } - }, - "header": { - "type": "object", - "$ref": "#/definitions/coreapi.ResponseHeader" - } - } - }, - "userapi.TransferDetailResponse": { - "type": "object", - "properties": { - "data": { - "type": "object", - "$ref": "#/definitions/transferdetails.TransferDetailData" - }, - "header": { - "type": "object", - "$ref": "#/definitions/coreapi.ResponseHeader" - } - } - }, - "userapi.UpdateTransferDetailRequest": { - "type": "object", - "properties": { - "data": { - "type": "object", - "$ref": "#/definitions/transferdetails.TransferDetailData" - }, - "document_id": { - "type": "string" - }, - "transfer_id": { - "type": "string" - } - } - } - } -} \ No newline at end of file diff --git a/protobufs/gen/swagger/document/service.swagger.json b/protobufs/gen/swagger/document/service.swagger.json deleted file mode 100644 index ae9f66cfb..000000000 --- a/protobufs/gen/swagger/document/service.swagger.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "swagger": "2.0", - "info": { - "title": "document/service.proto", - "version": "version not set" - }, - "schemes": [ - "http", - "https" - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "paths": {}, - "definitions": {} -} diff --git a/protobufs/gen/swagger/entity/service.swagger.json b/protobufs/gen/swagger/entity/service.swagger.json deleted file mode 100644 index ce92509bd..000000000 --- a/protobufs/gen/swagger/entity/service.swagger.json +++ /dev/null @@ -1,592 +0,0 @@ -{ - "swagger": "2.0", - "info": { - "title": "entity/service.proto", - "version": "version not set" - }, - "schemes": [ - "http", - "https" - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "paths": { - "/v1/entities": { - "post": { - "description": "Creates an entity", - "operationId": "Create", - "responses": { - "200": { - "description": "", - "schema": { - "$ref": "#/definitions/entityEntityResponse" - } - } - }, - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/entityEntityCreatePayload" - } - } - ], - "tags": [ - "EntityService" - ] - } - }, - "/v1/entities/{document_id}": { - "get": { - "description": "Get the current entity", - "operationId": "Get", - "responses": { - "200": { - "description": "", - "schema": { - "$ref": "#/definitions/entityEntityResponse" - } - } - }, - "parameters": [ - { - "name": "document_id", - "in": "path", - "required": true, - "type": "string" - } - ], - "tags": [ - "EntityService" - ] - }, - "put": { - "description": "Updates an entity", - "operationId": "Update", - "responses": { - "200": { - "description": "", - "schema": { - "$ref": "#/definitions/entityEntityResponse" - } - } - }, - "parameters": [ - { - "name": "document_id", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/entityEntityUpdatePayload" - } - } - ], - "tags": [ - "EntityService" - ] - } - }, - "/v1/entities/{document_id}/revoke": { - "post": { - "summary": "Entity Relation Revoke", - "description": "revoke an entity document share", - "operationId": "Revoke", - "responses": { - "200": { - "description": "", - "schema": { - "$ref": "#/definitions/entityRelationshipResponse" - } - } - }, - "parameters": [ - { - "name": "document_id", - "description": "entity identifier", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/entityRelationshipPayload" - } - } - ], - "tags": [ - "EntityService" - ] - } - }, - "/v1/entities/{document_id}/share": { - "post": { - "summary": "Entity Relation Share", - "description": "Share the entity document with others", - "operationId": "Share", - "responses": { - "200": { - "description": "", - "schema": { - "$ref": "#/definitions/entityRelationshipResponse" - } - } - }, - "parameters": [ - { - "name": "document_id", - "description": "entity identifier", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/entityRelationshipPayload" - } - } - ], - "tags": [ - "EntityService" - ] - } - }, - "/v1/relationships/{relationship_id}/entity": { - "get": { - "summary": "Entity Relation Get", - "description": "Get entity from business partner", - "operationId": "GetEntityByRelationship", - "responses": { - "200": { - "description": "", - "schema": { - "$ref": "#/definitions/entityEntityResponse" - } - } - }, - "parameters": [ - { - "name": "relationship_id", - "in": "path", - "required": true, - "type": "string" - } - ], - "tags": [ - "EntityService" - ] - } - } - }, - "definitions": { - "documentAttribute": { - "type": "object", - "properties": { - "key": { - "type": "string", - "title": "this is the sha256 hash of the label of the attribute, is not allowed to be updated by the client" - }, - "type": { - "type": "string" - }, - "value": { - "type": "string" - } - }, - "title": "Attribute represents a custom attribute" - }, - "documentNFT": { - "type": "object", - "properties": { - "registry": { - "type": "string" - }, - "owner": { - "type": "string", - "title": "read owner from Ethereum and empty when used in POST/PUT" - }, - "token_id": { - "type": "string" - }, - "token_index": { - "type": "string", - "title": "index of the token in the registry" - } - } - }, - "documentResponseHeader": { - "type": "object", - "properties": { - "document_id": { - "type": "string" - }, - "version_id": { - "type": "string" - }, - "author": { - "type": "string" - }, - "created_at": { - "type": "string" - }, - "read_access": { - "type": "array", - "items": { - "type": "string" - } - }, - "write_access": { - "type": "array", - "items": { - "type": "string" - } - }, - "job_id": { - "type": "string" - }, - "nfts": { - "type": "array", - "items": { - "$ref": "#/definitions/documentNFT" - } - } - } - }, - "entityAddress": { - "type": "object", - "properties": { - "is_main": { - "type": "boolean", - "format": "boolean" - }, - "is_remit_to": { - "type": "boolean", - "format": "boolean" - }, - "is_ship_to": { - "type": "boolean", - "format": "boolean" - }, - "is_pay_to": { - "type": "boolean", - "format": "boolean" - }, - "label": { - "type": "string" - }, - "zip": { - "type": "string" - }, - "state": { - "type": "string" - }, - "country": { - "type": "string" - }, - "address_line1": { - "type": "string" - }, - "address_line2": { - "type": "string" - }, - "contact_person": { - "type": "string" - } - } - }, - "entityBankPaymentMethod": { - "type": "object", - "properties": { - "identifier": { - "type": "string", - "format": "byte" - }, - "address": { - "$ref": "#/definitions/entityAddress" - }, - "holder_name": { - "type": "string" - }, - "bank_key": { - "type": "string" - }, - "bank_account_number": { - "type": "string" - }, - "supported_currency": { - "type": "string" - } - } - }, - "entityContact": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "title": { - "type": "string" - }, - "email": { - "type": "string" - }, - "phone": { - "type": "string" - }, - "fax": { - "type": "string" - } - } - }, - "entityCryptoPaymentMethod": { - "type": "object", - "properties": { - "identifier": { - "type": "string", - "format": "byte" - }, - "to": { - "type": "string" - }, - "chain_uri": { - "type": "string" - }, - "supported_currency": { - "type": "string" - } - } - }, - "entityEntityCreatePayload": { - "type": "object", - "properties": { - "read_access": { - "type": "array", - "items": { - "type": "string" - } - }, - "write_access": { - "type": "array", - "items": { - "type": "string" - } - }, - "data": { - "$ref": "#/definitions/entityEntityData" - }, - "attributes": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/documentAttribute" - }, - "title": "custom attributes" - } - } - }, - "entityEntityData": { - "type": "object", - "properties": { - "identity": { - "type": "string" - }, - "legal_name": { - "type": "string" - }, - "addresses": { - "type": "array", - "items": { - "$ref": "#/definitions/entityAddress" - }, - "title": "address" - }, - "payment_details": { - "type": "array", - "items": { - "$ref": "#/definitions/entityPaymentDetail" - }, - "title": "tax information" - }, - "contacts": { - "type": "array", - "items": { - "$ref": "#/definitions/entityContact" - }, - "title": "Entity contact list" - } - }, - "title": "EntityData is the default entity schema" - }, - "entityEntityDataResponse": { - "type": "object", - "properties": { - "entity": { - "$ref": "#/definitions/entityEntityData" - }, - "relationships": { - "type": "array", - "items": { - "$ref": "#/definitions/entityRelationship" - } - } - }, - "title": "Entity Relationships" - }, - "entityEntityResponse": { - "type": "object", - "properties": { - "header": { - "$ref": "#/definitions/documentResponseHeader" - }, - "data": { - "$ref": "#/definitions/entityEntityDataResponse" - }, - "attributes": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/documentAttribute" - }, - "title": "custom attributes" - } - } - }, - "entityEntityUpdatePayload": { - "type": "object", - "properties": { - "document_id": { - "type": "string" - }, - "read_access": { - "type": "array", - "items": { - "type": "string" - } - }, - "write_access": { - "type": "array", - "items": { - "type": "string" - } - }, - "data": { - "$ref": "#/definitions/entityEntityData" - }, - "attributes": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/documentAttribute" - }, - "title": "custom attributes" - } - } - }, - "entityOtherPayment": { - "type": "object", - "properties": { - "identifier": { - "type": "string", - "format": "byte" - }, - "type": { - "type": "string" - }, - "pay_to": { - "type": "string" - }, - "supported_currency": { - "type": "string" - } - } - }, - "entityPaymentDetail": { - "type": "object", - "properties": { - "predefined": { - "type": "boolean", - "format": "boolean", - "title": "fields for bank accounts and ethereum wallets" - }, - "bank_payment_method": { - "$ref": "#/definitions/entityBankPaymentMethod" - }, - "crypto_payment_method": { - "$ref": "#/definitions/entityCryptoPaymentMethod" - }, - "other_method": { - "$ref": "#/definitions/entityOtherPayment" - } - } - }, - "entityRelationship": { - "type": "object", - "properties": { - "identity": { - "type": "string" - }, - "active": { - "type": "boolean", - "format": "boolean" - } - } - }, - "entityRelationshipData": { - "type": "object", - "properties": { - "owner_identity": { - "type": "string", - "title": "DID of relationship owner" - }, - "target_identity": { - "type": "string", - "title": "DID of target identity" - }, - "entity_identifier": { - "type": "string", - "title": "identifier of Entity whose data can be accessed via this relationship" - } - } - }, - "entityRelationshipPayload": { - "type": "object", - "properties": { - "document_id": { - "type": "string", - "title": "entity identifier" - }, - "target_identity": { - "type": "string" - } - } - }, - "entityRelationshipResponse": { - "type": "object", - "properties": { - "header": { - "$ref": "#/definitions/documentResponseHeader" - }, - "relationship": { - "type": "array", - "items": { - "$ref": "#/definitions/entityRelationshipData" - } - } - } - } - } -} diff --git a/protobufs/gen/swagger/funding/funding.swagger.json b/protobufs/gen/swagger/funding/funding.swagger.json deleted file mode 100644 index 73718cf68..000000000 --- a/protobufs/gen/swagger/funding/funding.swagger.json +++ /dev/null @@ -1,446 +0,0 @@ -{ - "swagger": "2.0", - "info": { - "title": "funding/funding.proto", - "version": "version not set" - }, - "schemes": [ - "http", - "https" - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "paths": { - "/v1/documents/{document_id}/funding_agreements": { - "get": { - "description": "Get all funding agreements of a latest document", - "operationId": "GetList", - "responses": { - "200": { - "description": "", - "schema": { - "$ref": "#/definitions/funFundingListResponse" - } - } - }, - "parameters": [ - { - "name": "document_id", - "in": "path", - "required": true, - "type": "string" - } - ], - "tags": [ - "FundingService" - ] - }, - "post": { - "description": "Adds a funding to a document", - "operationId": "Create", - "responses": { - "200": { - "description": "", - "schema": { - "$ref": "#/definitions/funFundingResponse" - } - } - }, - "parameters": [ - { - "name": "document_id", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/funFundingCreatePayload" - } - } - ], - "tags": [ - "FundingService" - ] - } - }, - "/v1/documents/{document_id}/funding_agreements/{agreement_id}": { - "get": { - "description": "Get a funding agreement of a latest document", - "operationId": "Get", - "responses": { - "200": { - "description": "", - "schema": { - "$ref": "#/definitions/funFundingResponse" - } - } - }, - "parameters": [ - { - "name": "document_id", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "agreement_id", - "in": "path", - "required": true, - "type": "string" - } - ], - "tags": [ - "FundingService" - ] - }, - "put": { - "description": "Updates a funding agreement in a document", - "operationId": "Update", - "responses": { - "200": { - "description": "", - "schema": { - "$ref": "#/definitions/funFundingResponse" - } - } - }, - "parameters": [ - { - "name": "document_id", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "agreement_id", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/funFundingUpdatePayload" - } - } - ], - "tags": [ - "FundingService" - ] - } - }, - "/v1/documents/{document_id}/funding_agreements/{agreement_id}/sign": { - "post": { - "description": "Signs funding agreement in a document", - "operationId": "Sign", - "responses": { - "200": { - "description": "", - "schema": { - "$ref": "#/definitions/funFundingResponse" - } - } - }, - "parameters": [ - { - "name": "document_id", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "agreement_id", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/funRequest" - } - } - ], - "tags": [ - "FundingService" - ] - } - }, - "/v1/documents/{document_id}/versions/{version_id}/funding_agreements": { - "get": { - "description": "Get all funding agreements of a document version", - "operationId": "GetListVersion", - "responses": { - "200": { - "description": "", - "schema": { - "$ref": "#/definitions/funFundingListResponse" - } - } - }, - "parameters": [ - { - "name": "document_id", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "version_id", - "in": "path", - "required": true, - "type": "string" - } - ], - "tags": [ - "FundingService" - ] - } - }, - "/v1/documents/{document_id}/versions/{version_id}/funding_agreements/{agreement_id}": { - "get": { - "description": "Get a funding agreement of a document version", - "operationId": "GetVersion", - "responses": { - "200": { - "description": "", - "schema": { - "$ref": "#/definitions/funFundingResponse" - } - } - }, - "parameters": [ - { - "name": "document_id", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "version_id", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "agreement_id", - "in": "path", - "required": true, - "type": "string" - } - ], - "tags": [ - "FundingService" - ] - } - } - }, - "definitions": { - "documentNFT": { - "type": "object", - "properties": { - "registry": { - "type": "string" - }, - "owner": { - "type": "string", - "title": "read owner from Ethereum and empty when used in POST/PUT" - }, - "token_id": { - "type": "string" - }, - "token_index": { - "type": "string", - "title": "index of the token in the registry" - } - } - }, - "documentResponseHeader": { - "type": "object", - "properties": { - "document_id": { - "type": "string" - }, - "version_id": { - "type": "string" - }, - "author": { - "type": "string" - }, - "created_at": { - "type": "string" - }, - "read_access": { - "type": "array", - "items": { - "type": "string" - } - }, - "write_access": { - "type": "array", - "items": { - "type": "string" - } - }, - "job_id": { - "type": "string" - }, - "nfts": { - "type": "array", - "items": { - "$ref": "#/definitions/documentNFT" - } - } - } - }, - "funFundingCreatePayload": { - "type": "object", - "properties": { - "document_id": { - "type": "string" - }, - "data": { - "$ref": "#/definitions/funFundingData" - } - } - }, - "funFundingData": { - "type": "object", - "properties": { - "agreement_id": { - "type": "string" - }, - "amount": { - "type": "string" - }, - "apr": { - "type": "string" - }, - "days": { - "type": "string" - }, - "fee": { - "type": "string" - }, - "repayment_due_date": { - "type": "string" - }, - "repayment_occurred_date": { - "type": "string" - }, - "repayment_amount": { - "type": "string" - }, - "currency": { - "type": "string" - }, - "nft_address": { - "type": "string" - }, - "payment_details_id": { - "type": "string" - }, - "funder_id": { - "type": "string" - }, - "borrower_id": { - "type": "string" - } - }, - "title": "FundingData is the default funding extension schema" - }, - "funFundingListResponse": { - "type": "object", - "properties": { - "header": { - "$ref": "#/definitions/documentResponseHeader" - }, - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/funFundingResponseData" - } - } - } - }, - "funFundingResponse": { - "type": "object", - "properties": { - "header": { - "$ref": "#/definitions/documentResponseHeader" - }, - "data": { - "$ref": "#/definitions/funFundingResponseData" - } - } - }, - "funFundingResponseData": { - "type": "object", - "properties": { - "funding": { - "$ref": "#/definitions/funFundingData" - }, - "signatures": { - "type": "array", - "items": { - "$ref": "#/definitions/funFundingSignature" - } - } - } - }, - "funFundingSignature": { - "type": "object", - "properties": { - "valid": { - "type": "string" - }, - "outdated_signature": { - "type": "string" - }, - "identity": { - "type": "string" - }, - "signed_version": { - "type": "string" - } - } - }, - "funFundingUpdatePayload": { - "type": "object", - "properties": { - "document_id": { - "type": "string" - }, - "agreement_id": { - "type": "string" - }, - "data": { - "$ref": "#/definitions/funFundingData" - } - } - }, - "funRequest": { - "type": "object", - "properties": { - "document_id": { - "type": "string" - }, - "agreement_id": { - "type": "string" - } - } - } - } -} diff --git a/protobufs/gen/swagger/invoice/service.swagger.json b/protobufs/gen/swagger/invoice/service.swagger.json deleted file mode 100644 index c56873f41..000000000 --- a/protobufs/gen/swagger/invoice/service.swagger.json +++ /dev/null @@ -1,680 +0,0 @@ -{ - "swagger": "2.0", - "info": { - "title": "invoice/service.proto", - "version": "version not set" - }, - "schemes": [ - "http", - "https" - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "paths": { - "/v1/invoices": { - "post": { - "description": "Creates an invoice", - "operationId": "Create", - "responses": { - "200": { - "description": "", - "schema": { - "$ref": "#/definitions/invInvoiceResponse" - } - } - }, - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/invInvoiceCreatePayload" - } - } - ], - "tags": [ - "InvoiceService" - ] - } - }, - "/v1/invoices/{document_id}": { - "get": { - "description": "Get the current invoice", - "operationId": "Get", - "responses": { - "200": { - "description": "", - "schema": { - "$ref": "#/definitions/invInvoiceResponse" - } - } - }, - "parameters": [ - { - "name": "document_id", - "in": "path", - "required": true, - "type": "string" - } - ], - "tags": [ - "InvoiceService" - ] - }, - "put": { - "description": "Updates an invoice", - "operationId": "Update", - "responses": { - "200": { - "description": "", - "schema": { - "$ref": "#/definitions/invInvoiceResponse" - } - } - }, - "parameters": [ - { - "name": "document_id", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/invInvoiceUpdatePayload" - } - } - ], - "tags": [ - "InvoiceService" - ] - } - }, - "/v1/invoices/{document_id}/versions/{version_id}": { - "get": { - "description": "Get a specific version of an invoice", - "operationId": "GetVersion", - "responses": { - "200": { - "description": "", - "schema": { - "$ref": "#/definitions/invInvoiceResponse" - } - } - }, - "parameters": [ - { - "name": "document_id", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "version_id", - "in": "path", - "required": true, - "type": "string" - } - ], - "tags": [ - "InvoiceService" - ] - } - } - }, - "definitions": { - "documentAttribute": { - "type": "object", - "properties": { - "key": { - "type": "string", - "title": "this is the sha256 hash of the label of the attribute, is not allowed to be updated by the client" - }, - "type": { - "type": "string" - }, - "value": { - "type": "string" - } - }, - "title": "Attribute represents a custom attribute" - }, - "documentBinaryAttachment": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "file_type": { - "type": "string", - "title": "mime type of attached file" - }, - "size": { - "type": "string", - "format": "uint64", - "title": "in bytes" - }, - "data": { - "type": "string" - }, - "checksum": { - "type": "string", - "title": "the md5 checksum of the original file for easier verification - optional" - } - } - }, - "documentNFT": { - "type": "object", - "properties": { - "registry": { - "type": "string" - }, - "owner": { - "type": "string", - "title": "read owner from Ethereum and empty when used in POST/PUT" - }, - "token_id": { - "type": "string" - }, - "token_index": { - "type": "string", - "title": "index of the token in the registry" - } - } - }, - "documentPaymentDetails": { - "type": "object", - "properties": { - "id": { - "type": "string", - "title": "identifying this payment. could be a sequential number, could be a transaction hash of the crypto payment" - }, - "date_executed": { - "type": "string", - "format": "date-time" - }, - "payee": { - "type": "string", - "title": "centrifuge id of payee" - }, - "payer": { - "type": "string", - "title": "centrifuge id of payer" - }, - "amount": { - "type": "string" - }, - "currency": { - "type": "string" - }, - "reference": { - "type": "string", - "title": "payment reference (e.g. reference field on bank transfer)" - }, - "bank_name": { - "type": "string" - }, - "bank_address": { - "type": "string" - }, - "bank_country": { - "type": "string" - }, - "bank_account_number": { - "type": "string" - }, - "bank_account_currency": { - "type": "string" - }, - "bank_account_holder_name": { - "type": "string" - }, - "bank_key": { - "type": "string" - }, - "crypto_chain_uri": { - "type": "string", - "title": "the ID of the chain to use in URI format. e.g. \"ethereum://42/\u003ctokenaddress\u003e\"" - }, - "crypto_transaction_id": { - "type": "string", - "title": "the transaction in which the payment happened" - }, - "crypto_from": { - "type": "string", - "title": "from address" - }, - "crypto_to": { - "type": "string", - "title": "to address" - } - } - }, - "documentResponseHeader": { - "type": "object", - "properties": { - "document_id": { - "type": "string" - }, - "version_id": { - "type": "string" - }, - "author": { - "type": "string" - }, - "created_at": { - "type": "string" - }, - "read_access": { - "type": "array", - "items": { - "type": "string" - } - }, - "write_access": { - "type": "array", - "items": { - "type": "string" - } - }, - "job_id": { - "type": "string" - }, - "nfts": { - "type": "array", - "items": { - "$ref": "#/definitions/documentNFT" - } - } - } - }, - "invInvoiceCreatePayload": { - "type": "object", - "properties": { - "read_access": { - "type": "array", - "items": { - "type": "string" - } - }, - "write_access": { - "type": "array", - "items": { - "type": "string" - } - }, - "data": { - "$ref": "#/definitions/invInvoiceData" - }, - "attributes": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/documentAttribute" - }, - "title": "custom attributes" - } - } - }, - "invInvoiceData": { - "type": "object", - "properties": { - "number": { - "type": "string", - "title": "invoice number or reference number" - }, - "status": { - "type": "string", - "title": "invoice status" - }, - "sender_invoice_id": { - "type": "string" - }, - "recipient_invoice_id": { - "type": "string" - }, - "sender_company_name": { - "type": "string" - }, - "sender_contact_person_name": { - "type": "string" - }, - "sender_street1": { - "type": "string", - "title": "street and address details of the sender company" - }, - "sender_street2": { - "type": "string" - }, - "sender_city": { - "type": "string" - }, - "sender_zipcode": { - "type": "string" - }, - "sender_state": { - "type": "string" - }, - "sender_country": { - "type": "string", - "title": "country ISO code of the sender of this invoice" - }, - "bill_to_company_name": { - "type": "string" - }, - "bill_to_contact_person_name": { - "type": "string" - }, - "bill_to_street1": { - "type": "string" - }, - "bill_to_street2": { - "type": "string" - }, - "bill_to_city": { - "type": "string" - }, - "bill_to_zipcode": { - "type": "string" - }, - "bill_to_state": { - "type": "string" - }, - "bill_to_country": { - "type": "string" - }, - "bill_to_vat_number": { - "type": "string" - }, - "bill_to_local_tax_id": { - "type": "string" - }, - "remit_to_company_name": { - "type": "string" - }, - "remit_to_contact_person_name": { - "type": "string" - }, - "remit_to_street1": { - "type": "string" - }, - "remit_to_street2": { - "type": "string" - }, - "remit_to_city": { - "type": "string" - }, - "remit_to_zipcode": { - "type": "string" - }, - "remit_to_state": { - "type": "string" - }, - "remit_to_country": { - "type": "string" - }, - "remit_to_vat_number": { - "type": "string" - }, - "remit_to_local_tax_id": { - "type": "string" - }, - "remit_to_tax_country": { - "type": "string" - }, - "ship_to_company_name": { - "type": "string" - }, - "ship_to_contact_person_name": { - "type": "string" - }, - "ship_to_street1": { - "type": "string" - }, - "ship_to_street2": { - "type": "string" - }, - "ship_to_city": { - "type": "string" - }, - "ship_to_zipcode": { - "type": "string" - }, - "ship_to_state": { - "type": "string" - }, - "ship_to_country": { - "type": "string" - }, - "currency": { - "type": "string", - "title": "ISO currency code" - }, - "gross_amount": { - "type": "string", - "title": "invoice amount including tax" - }, - "net_amount": { - "type": "string", - "title": "invoice amount excluding tax" - }, - "tax_amount": { - "type": "string" - }, - "tax_rate": { - "type": "string" - }, - "tax_on_line_level": { - "type": "boolean", - "format": "boolean" - }, - "recipient": { - "type": "string", - "title": "centrifuge ID of the recipient" - }, - "sender": { - "type": "string", - "title": "centrifuge ID of the sender" - }, - "payee": { - "type": "string", - "title": "centrifuge ID of the payee" - }, - "comment": { - "type": "string" - }, - "shipping_terms": { - "type": "string" - }, - "requester_email": { - "type": "string" - }, - "requester_name": { - "type": "string" - }, - "delivery_number": { - "type": "string", - "title": "number of the delivery note" - }, - "is_credit_note": { - "type": "boolean", - "format": "boolean" - }, - "credit_note_invoice_number": { - "type": "string" - }, - "credit_for_invoice_date": { - "type": "string", - "format": "date-time" - }, - "date_due": { - "type": "string", - "format": "date-time" - }, - "date_paid": { - "type": "string", - "format": "date-time" - }, - "date_updated": { - "type": "string", - "format": "date-time" - }, - "date_created": { - "type": "string", - "format": "date-time" - }, - "attachments": { - "type": "array", - "items": { - "$ref": "#/definitions/documentBinaryAttachment" - } - }, - "line_items": { - "type": "array", - "items": { - "$ref": "#/definitions/invLineItem" - } - }, - "payment_details": { - "type": "array", - "items": { - "$ref": "#/definitions/documentPaymentDetails" - } - }, - "tax_items": { - "type": "array", - "items": { - "$ref": "#/definitions/invTaxItem" - } - } - }, - "title": "InvoiceData is the default invoice schema" - }, - "invInvoiceResponse": { - "type": "object", - "properties": { - "header": { - "$ref": "#/definitions/documentResponseHeader" - }, - "data": { - "$ref": "#/definitions/invInvoiceData" - }, - "attributes": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/documentAttribute" - }, - "title": "custom attributes" - } - } - }, - "invInvoiceUpdatePayload": { - "type": "object", - "properties": { - "document_id": { - "type": "string" - }, - "read_access": { - "type": "array", - "items": { - "type": "string" - } - }, - "write_access": { - "type": "array", - "items": { - "type": "string" - } - }, - "data": { - "$ref": "#/definitions/invInvoiceData" - }, - "attributes": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/documentAttribute" - }, - "title": "custom attributes" - } - } - }, - "invLineItem": { - "type": "object", - "properties": { - "item_number": { - "type": "string" - }, - "description": { - "type": "string" - }, - "sender_part_no": { - "type": "string" - }, - "price_per_unit": { - "type": "string" - }, - "quantity": { - "type": "string" - }, - "unit_of_measure": { - "type": "string" - }, - "net_weight": { - "type": "string" - }, - "tax_amount": { - "type": "string" - }, - "tax_rate": { - "type": "string" - }, - "tax_code": { - "type": "string" - }, - "total_amount": { - "type": "string", - "title": "the total amount of the line item" - }, - "purchase_order_number": { - "type": "string" - }, - "purchase_order_item_number": { - "type": "string" - }, - "delivery_note_number": { - "type": "string" - } - } - }, - "invTaxItem": { - "type": "object", - "properties": { - "item_number": { - "type": "string" - }, - "invoice_item_number": { - "type": "string" - }, - "tax_amount": { - "type": "string" - }, - "tax_rate": { - "type": "string" - }, - "tax_code": { - "type": "string" - }, - "tax_base_amount": { - "type": "string" - } - } - } - } -} diff --git a/protobufs/gen/swagger/nft/service.swagger.json b/protobufs/gen/swagger/nft/service.swagger.json deleted file mode 100644 index 903c5d516..000000000 --- a/protobufs/gen/swagger/nft/service.swagger.json +++ /dev/null @@ -1,84 +0,0 @@ -{ - "swagger": "2.0", - "info": { - "title": "nft/service.proto", - "version": "version not set" - }, - "schemes": [ - "http", - "https" - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "paths": { - "/v1/invoices/{document_id}/mint/unpaid": { - "post": { - "description": "Mints an NFT out of an Unpaid Centrifuge Invoice", - "operationId": "MintInvoiceUnpaidNFT", - "responses": { - "200": { - "description": "", - "schema": { - "$ref": "#/definitions/nftNFTMintResponse" - } - } - }, - "parameters": [ - { - "name": "document_id", - "description": "Invoice Document identifier", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/nftNFTMintInvoiceUnpaidRequest" - } - } - ], - "tags": [ - "NFTService" - ] - } - } - }, - "definitions": { - "nftNFTMintInvoiceUnpaidRequest": { - "type": "object", - "properties": { - "document_id": { - "type": "string", - "title": "Invoice Document identifier" - }, - "deposit_address": { - "type": "string", - "title": "Deposit address for NFT Token created" - } - } - }, - "nftNFTMintResponse": { - "type": "object", - "properties": { - "header": { - "$ref": "#/definitions/nftResponseHeader" - } - } - }, - "nftResponseHeader": { - "type": "object", - "properties": { - "job_id": { - "type": "string" - } - } - } - } -} diff --git a/protobufs/gen/swagger/notification/service.swagger.json b/protobufs/gen/swagger/notification/service.swagger.json deleted file mode 100644 index cbf2c8b25..000000000 --- a/protobufs/gen/swagger/notification/service.swagger.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "swagger": "2.0", - "info": { - "title": "notification/service.proto", - "version": "version not set" - }, - "schemes": [ - "http", - "https" - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "paths": { - "/dummy": { - "get": { - "description": "Dummy notification endpoint", - "operationId": "Notify", - "responses": { - "200": { - "description": "", - "schema": { - "$ref": "#/definitions/notificationNotificationMessage" - } - } - }, - "tags": [ - "NotificationDummyService" - ] - } - } - }, - "definitions": { - "notificationNotificationMessage": { - "type": "object", - "properties": { - "event_type": { - "type": "integer", - "format": "int64" - }, - "recorded": { - "type": "string", - "format": "date-time" - }, - "document_type": { - "type": "string" - }, - "status": { - "type": "string", - "title": "status - Failure or Success" - }, - "message": { - "type": "string", - "title": "message associated to status, if error, error message enclosed" - }, - "document_id": { - "type": "string", - "title": "document_id" - }, - "account_id": { - "type": "string", - "title": "account_id is the account associated to webhook" - }, - "from_id": { - "type": "string", - "title": "from_id if provided, original trigger of the event" - }, - "to_id": { - "type": "string", - "title": "to_id if provided, final destination of the event" - } - }, - "title": "NotificationMessage wraps a single document to be notified to upstream services" - } - } -} diff --git a/protobufs/gen/swagger/protocol/protocol.swagger.json b/protobufs/gen/swagger/protocol/protocol.swagger.json deleted file mode 100644 index a8a65dc17..000000000 --- a/protobufs/gen/swagger/protocol/protocol.swagger.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "swagger": "2.0", - "info": { - "title": "protocol/protocol.proto", - "version": "version not set" - }, - "schemes": [ - "http", - "https" - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "paths": {}, - "definitions": {} -} diff --git a/protobufs/gen/swagger/purchaseorder/service.swagger.json b/protobufs/gen/swagger/purchaseorder/service.swagger.json deleted file mode 100644 index 153c99157..000000000 --- a/protobufs/gen/swagger/purchaseorder/service.swagger.json +++ /dev/null @@ -1,598 +0,0 @@ -{ - "swagger": "2.0", - "info": { - "title": "purchaseorder/service.proto", - "version": "version not set" - }, - "schemes": [ - "http", - "https" - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "paths": { - "/v1/purchase_orders": { - "post": { - "description": "Creates a purchase order", - "operationId": "Create", - "responses": { - "200": { - "description": "", - "schema": { - "$ref": "#/definitions/poPurchaseOrderResponse" - } - } - }, - "parameters": [ - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/poPurchaseOrderCreatePayload" - } - } - ], - "tags": [ - "PurchaseOrderService" - ] - } - }, - "/v1/purchase_orders/{document_id}": { - "get": { - "description": "Get the current version of a purchase order", - "operationId": "Get", - "responses": { - "200": { - "description": "", - "schema": { - "$ref": "#/definitions/poPurchaseOrderResponse" - } - } - }, - "parameters": [ - { - "name": "document_id", - "in": "path", - "required": true, - "type": "string" - } - ], - "tags": [ - "PurchaseOrderService" - ] - }, - "put": { - "description": "Updates a purchase order", - "operationId": "Update", - "responses": { - "200": { - "description": "", - "schema": { - "$ref": "#/definitions/poPurchaseOrderResponse" - } - } - }, - "parameters": [ - { - "name": "document_id", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/poPurchaseOrderUpdatePayload" - } - } - ], - "tags": [ - "PurchaseOrderService" - ] - } - }, - "/v1/purchase_orders/{document_id}/versions/{version_id}": { - "get": { - "description": "Get a specific version of a purchase order", - "operationId": "GetVersion", - "responses": { - "200": { - "description": "", - "schema": { - "$ref": "#/definitions/poPurchaseOrderResponse" - } - } - }, - "parameters": [ - { - "name": "document_id", - "in": "path", - "required": true, - "type": "string" - }, - { - "name": "version_id", - "in": "path", - "required": true, - "type": "string" - } - ], - "tags": [ - "PurchaseOrderService" - ] - } - } - }, - "definitions": { - "documentAttribute": { - "type": "object", - "properties": { - "key": { - "type": "string", - "title": "this is the sha256 hash of the label of the attribute, is not allowed to be updated by the client" - }, - "type": { - "type": "string" - }, - "value": { - "type": "string" - } - }, - "title": "Attribute represents a custom attribute" - }, - "documentBinaryAttachment": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "file_type": { - "type": "string", - "title": "mime type of attached file" - }, - "size": { - "type": "string", - "format": "uint64", - "title": "in bytes" - }, - "data": { - "type": "string" - }, - "checksum": { - "type": "string", - "title": "the md5 checksum of the original file for easier verification - optional" - } - } - }, - "documentNFT": { - "type": "object", - "properties": { - "registry": { - "type": "string" - }, - "owner": { - "type": "string", - "title": "read owner from Ethereum and empty when used in POST/PUT" - }, - "token_id": { - "type": "string" - }, - "token_index": { - "type": "string", - "title": "index of the token in the registry" - } - } - }, - "documentPaymentDetails": { - "type": "object", - "properties": { - "id": { - "type": "string", - "title": "identifying this payment. could be a sequential number, could be a transaction hash of the crypto payment" - }, - "date_executed": { - "type": "string", - "format": "date-time" - }, - "payee": { - "type": "string", - "title": "centrifuge id of payee" - }, - "payer": { - "type": "string", - "title": "centrifuge id of payer" - }, - "amount": { - "type": "string" - }, - "currency": { - "type": "string" - }, - "reference": { - "type": "string", - "title": "payment reference (e.g. reference field on bank transfer)" - }, - "bank_name": { - "type": "string" - }, - "bank_address": { - "type": "string" - }, - "bank_country": { - "type": "string" - }, - "bank_account_number": { - "type": "string" - }, - "bank_account_currency": { - "type": "string" - }, - "bank_account_holder_name": { - "type": "string" - }, - "bank_key": { - "type": "string" - }, - "crypto_chain_uri": { - "type": "string", - "title": "the ID of the chain to use in URI format. e.g. \"ethereum://42/\u003ctokenaddress\u003e\"" - }, - "crypto_transaction_id": { - "type": "string", - "title": "the transaction in which the payment happened" - }, - "crypto_from": { - "type": "string", - "title": "from address" - }, - "crypto_to": { - "type": "string", - "title": "to address" - } - } - }, - "documentResponseHeader": { - "type": "object", - "properties": { - "document_id": { - "type": "string" - }, - "version_id": { - "type": "string" - }, - "author": { - "type": "string" - }, - "created_at": { - "type": "string" - }, - "read_access": { - "type": "array", - "items": { - "type": "string" - } - }, - "write_access": { - "type": "array", - "items": { - "type": "string" - } - }, - "job_id": { - "type": "string" - }, - "nfts": { - "type": "array", - "items": { - "$ref": "#/definitions/documentNFT" - } - } - } - }, - "poLineItem": { - "type": "object", - "properties": { - "status": { - "type": "string" - }, - "item_number": { - "type": "string" - }, - "description": { - "type": "string" - }, - "amount_invoiced": { - "type": "string" - }, - "amount_total": { - "type": "string" - }, - "requisition_number": { - "type": "string" - }, - "requisition_item": { - "type": "string" - }, - "part_no": { - "type": "string" - }, - "price_per_unit": { - "type": "string" - }, - "unit_of_measure": { - "type": "string" - }, - "quantity": { - "type": "string" - }, - "received_quantity": { - "type": "string" - }, - "date_updated": { - "type": "string", - "format": "date-time" - }, - "date_created": { - "type": "string", - "format": "date-time" - }, - "revision_number": { - "type": "string", - "format": "int64" - }, - "activities": { - "type": "array", - "items": { - "$ref": "#/definitions/poLineItemActivity" - } - }, - "tax_items": { - "type": "array", - "items": { - "$ref": "#/definitions/poTaxItem" - } - } - } - }, - "poLineItemActivity": { - "type": "object", - "properties": { - "item_number": { - "type": "string" - }, - "status": { - "type": "string", - "description": "delivered, returned, credited, invoiced, paid, ..." - }, - "quantity": { - "type": "string" - }, - "amount": { - "type": "string" - }, - "reference_document_id": { - "type": "string", - "description": "depending on status delivery note, invoice, ..." - }, - "reference_document_item": { - "type": "string", - "title": "line item from the reference document" - }, - "date": { - "type": "string", - "format": "date-time" - } - } - }, - "poPurchaseOrderCreatePayload": { - "type": "object", - "properties": { - "read_access": { - "type": "array", - "items": { - "type": "string" - } - }, - "write_access": { - "type": "array", - "items": { - "type": "string" - } - }, - "data": { - "$ref": "#/definitions/poPurchaseOrderData" - }, - "attributes": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/documentAttribute" - }, - "title": "custom attributes" - } - } - }, - "poPurchaseOrderData": { - "type": "object", - "properties": { - "status": { - "type": "string" - }, - "number": { - "type": "string", - "title": "purchase order number or reference number" - }, - "sender_order_id": { - "type": "string" - }, - "recipient_order_id": { - "type": "string" - }, - "requisition_id": { - "type": "string" - }, - "requester_name": { - "type": "string" - }, - "requester_email": { - "type": "string" - }, - "ship_to_company_name": { - "type": "string" - }, - "ship_to_contact_person_name": { - "type": "string" - }, - "ship_to_street1": { - "type": "string" - }, - "ship_to_street2": { - "type": "string" - }, - "ship_to_city": { - "type": "string" - }, - "ship_to_zipcode": { - "type": "string" - }, - "ship_to_state": { - "type": "string" - }, - "ship_to_country": { - "type": "string" - }, - "payment_terms": { - "type": "string" - }, - "currency": { - "type": "string" - }, - "total_amount": { - "type": "string" - }, - "recipient": { - "type": "string", - "title": "centrifuge ID of the recipient" - }, - "sender": { - "type": "string", - "title": "centrifuge ID of the sender" - }, - "comment": { - "type": "string" - }, - "date_sent": { - "type": "string", - "format": "date-time" - }, - "date_confirmed": { - "type": "string", - "format": "date-time" - }, - "date_updated": { - "type": "string", - "format": "date-time" - }, - "date_created": { - "type": "string", - "format": "date-time" - }, - "attachments": { - "type": "array", - "items": { - "$ref": "#/definitions/documentBinaryAttachment" - } - }, - "line_items": { - "type": "array", - "items": { - "$ref": "#/definitions/poLineItem" - } - }, - "payment_details": { - "type": "array", - "items": { - "$ref": "#/definitions/documentPaymentDetails" - } - } - } - }, - "poPurchaseOrderResponse": { - "type": "object", - "properties": { - "header": { - "$ref": "#/definitions/documentResponseHeader" - }, - "data": { - "$ref": "#/definitions/poPurchaseOrderData" - }, - "attributes": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/documentAttribute" - }, - "title": "custom attributes" - } - } - }, - "poPurchaseOrderUpdatePayload": { - "type": "object", - "properties": { - "document_id": { - "type": "string" - }, - "read_access": { - "type": "array", - "items": { - "type": "string" - } - }, - "write_access": { - "type": "array", - "items": { - "type": "string" - } - }, - "data": { - "$ref": "#/definitions/poPurchaseOrderData" - }, - "attributes": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/documentAttribute" - }, - "title": "custom attributes" - } - } - }, - "poTaxItem": { - "type": "object", - "properties": { - "item_number": { - "type": "string" - }, - "purchase_order_item_number": { - "type": "string" - }, - "tax_amount": { - "type": "string" - }, - "tax_rate": { - "type": "string" - }, - "tax_code": { - "type": "string" - }, - "tax_base_amount": { - "type": "string" - } - } - } - } -} diff --git a/protobufs/invoice/service.proto b/protobufs/invoice/service.proto deleted file mode 100644 index 6fb2c04c9..000000000 --- a/protobufs/invoice/service.proto +++ /dev/null @@ -1,192 +0,0 @@ -syntax = "proto3"; - -package inv; - -option go_package = "invpb"; -option java_multiple_files = true; -option java_outer_classname = "ServiceProto"; -option java_package = "com.inv"; - -import "document/service.proto"; -import "google/api/annotations.proto"; -import "google/protobuf/timestamp.proto"; -import "protoc-gen-swagger/options/annotations.proto"; - -// InvoiceService contains all common interactions for invoice documents -service InvoiceService { - rpc Create(InvoiceCreatePayload) returns (InvoiceResponse) { - option (google.api.http) = { - post: "/v1/invoices" - body: "*" - }; - option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = { - description: "Creates an invoice" - }; - } - rpc Update(InvoiceUpdatePayload) returns (InvoiceResponse) { - option (google.api.http) = { - put: "/v1/invoices/{document_id}" - body: "*" - }; - option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = { - description: "Updates an invoice" - }; - } - rpc GetVersion(GetVersionRequest) returns (InvoiceResponse) { - option (google.api.http) = { - get: "/v1/invoices/{document_id}/versions/{version_id}" - }; - option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = { - description: "Get a specific version of an invoice" - }; - } - rpc Get(GetRequest) returns (InvoiceResponse) { - option (google.api.http) = { - get: "/v1/invoices/{document_id}" - }; - option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = { - description: "Get the current invoice" - }; - } -} - -message GetRequest { - string document_id = 1; -} - -message GetVersionRequest { - string document_id = 1; - string version_id = 2; -} - -message InvoiceCreatePayload { - repeated string read_access = 1; - repeated string write_access = 2; - InvoiceData data = 3; - // custom attributes - map attributes = 4; -} - -message InvoiceUpdatePayload { - string document_id = 1; - repeated string read_access = 2; - repeated string write_access = 3; - InvoiceData data = 4; - // custom attributes - map attributes = 5; -} - -message InvoiceResponse { - document.ResponseHeader header = 1; - InvoiceData data = 2; - // custom attributes - map attributes = 3; -} - -// InvoiceData is the default invoice schema -message InvoiceData { - // invoice number or reference number - string number = 1; - // invoice status - string status = 2; - string sender_invoice_id = 3; - string recipient_invoice_id = 4; - string sender_company_name = 5; - string sender_contact_person_name = 6; - // street and address details of the sender company - string sender_street1 = 7; - string sender_street2 = 8; - string sender_city = 9; - string sender_zipcode = 10; - string sender_state = 11; - // country ISO code of the sender of this invoice - string sender_country = 12; - string bill_to_company_name = 43; - string bill_to_contact_person_name = 44; - string bill_to_street1 = 15; - string bill_to_street2 = 16; - string bill_to_city = 17; - string bill_to_zipcode = 18; - string bill_to_state = 50; - string bill_to_country = 20; - string bill_to_vat_number = 21; - string bill_to_local_tax_id = 60; - string remit_to_company_name = 23; - string remit_to_contact_person_name = 24; - string remit_to_street1 = 25; - string remit_to_street2 = 26; - string remit_to_city = 27; - string remit_to_zipcode = 28; - string remit_to_state = 30; - string remit_to_country = 31; - string remit_to_vat_number = 32; - string remit_to_local_tax_id = 33; - string remit_to_tax_country = 34; - string ship_to_company_name = 35; - string ship_to_contact_person_name = 36; - string ship_to_street1 = 37; - string ship_to_street2 = 38; - string ship_to_city = 39; - string ship_to_zipcode = 40; - string ship_to_state = 41; - string ship_to_country = 42; - // ISO currency code - string currency = 13; - // invoice amount including tax - string gross_amount = 14; - // invoice amount excluding tax - string net_amount = 45; - string tax_amount = 46; - string tax_rate = 47; - bool tax_on_line_level = 48; - // centrifuge ID of the recipient - string recipient = 49; - // centrifuge ID of the sender - string sender = 19; - // centrifuge ID of the payee - string payee = 51; - string comment = 52; - string shipping_terms = 53; - string requester_email = 54; - string requester_name = 55; - //number of the delivery note - string delivery_number = 56; - bool is_credit_note = 57; - string credit_note_invoice_number = 58; - google.protobuf.Timestamp credit_for_invoice_date = 59; - google.protobuf.Timestamp date_due = 22; - google.protobuf.Timestamp date_paid = 61; - google.protobuf.Timestamp date_updated = 62; - google.protobuf.Timestamp date_created = 63; - repeated document.BinaryAttachment attachments = 64; - repeated LineItem line_items = 65; - repeated document.PaymentDetails payment_details = 66; - repeated TaxItem tax_items = 67; -} - -message TaxItem { - string item_number = 1; - string invoice_item_number = 2; - string tax_amount = 3; - string tax_rate = 4; - string tax_code = 5; - string tax_base_amount = 6; -} - -message LineItem { - string item_number = 1; - string description = 2; - string sender_part_no = 3; - string price_per_unit = 4; - string quantity = 5; - string unit_of_measure = 6; - string net_weight = 7; - string tax_amount = 8; - string tax_rate = 9; - string tax_code = 10; - //the total amount of the line item - string total_amount = 11; - string purchase_order_number = 12; - string purchase_order_item_number = 13; - string delivery_note_number = 14; -} diff --git a/protobufs/nft/service.proto b/protobufs/nft/service.proto deleted file mode 100644 index e74c8df2d..000000000 --- a/protobufs/nft/service.proto +++ /dev/null @@ -1,39 +0,0 @@ -syntax = "proto3"; - -package nft; - -option go_package = "nftpb"; -option java_multiple_files = true; -option java_outer_classname = "ServiceProto"; -option java_package = "com.nft"; - -import "google/api/annotations.proto"; -import "protoc-gen-swagger/options/annotations.proto"; - -// NFTService contains all common interactions for NFTs -service NFTService { - rpc MintInvoiceUnpaidNFT(NFTMintInvoiceUnpaidRequest) returns (NFTMintResponse) { - option (google.api.http) = { - post: "/v1/invoices/{document_id}/mint/unpaid" - body: "*" - }; - option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = { - description: "Mints an NFT out of an Unpaid Centrifuge Invoice" - }; - } -} - -message ResponseHeader { - string job_id = 5; -} - -message NFTMintInvoiceUnpaidRequest { - // Invoice Document identifier - string document_id = 1; - // Deposit address for NFT Token created - string deposit_address = 2; -} - -message NFTMintResponse { - ResponseHeader header = 1; -} diff --git a/protobufs/notification/service.proto b/protobufs/notification/service.proto deleted file mode 100644 index ad97cf9de..000000000 --- a/protobufs/notification/service.proto +++ /dev/null @@ -1,25 +0,0 @@ -syntax = "proto3"; - -package notification; - -option go_package = "notificationpb"; -option java_multiple_files = true; -option java_outer_classname = "ServiceProto"; -option java_package = "com.notification"; - -import "google/api/annotations.proto"; -import "google/protobuf/empty.proto"; -import "notification/notification.proto"; -import "protoc-gen-swagger/options/annotations.proto"; - -// Dummy Notification Service -service NotificationDummyService { - rpc Notify(google.protobuf.Empty) returns (NotificationMessage) { - option (google.api.http) = { - get: "/dummy" - }; - option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = { - description: "Dummy notification endpoint" - }; - } -} diff --git a/protobufs/protocol/protocol.proto b/protobufs/protocol/protocol.proto deleted file mode 100644 index 884a6aee8..000000000 --- a/protobufs/protocol/protocol.proto +++ /dev/null @@ -1,13 +0,0 @@ -syntax = "proto3"; - -package protocol; - -option go_package = "protocolpb"; -option java_multiple_files = true; -option java_outer_classname = "ProtocolProto"; -option java_package = "com.protocol"; - -message P2PEnvelope { - // serialized protobuf for the actual message - bytes body = 2; -} diff --git a/protobufs/prototool.yaml b/protobufs/prototool.yaml deleted file mode 100644 index 581146644..000000000 --- a/protobufs/prototool.yaml +++ /dev/null @@ -1,61 +0,0 @@ -protoc: - version: 3.7.0 - includes: - - ../vendor/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis - - ../vendor/github.com/grpc-ecosystem/grpc-gateway/ - - ../vendor/github.com/centrifuge/ - - ../vendor/github.com/centrifuge/centrifuge-protobufs - -lint: - rules: - remove: - - REQUEST_RESPONSE_TYPES_IN_SAME_FILE - - REQUEST_RESPONSE_TYPES_UNIQUE -generate: - # Options that will apply to all plugins of type go, gogo, gogrpc, gogogrpc. - go_options: - # The base import path. This should be the go path of the prototool.yaml file. - # This is required if you have any go plugins. - import_path: github.com/centrifuge/go-centrifuge/protobufs - - # Do not include default modifiers with Mfile=package. - # By default, modifiers are included for the Well-Known Types if - # protoc_include_wkt is set, and for all files in the compilation relative - # to the import path. - # ** Generally do not set this unless you know what you are doing. ** - #no_default_modifiers: true - - # Extra modifiers to include with Mfile=package. - extra_modifiers: - google/api/annotations.proto: google.golang.org/genproto/googleapis/api/annotations - google/api/http.proto: google.golang.org/genproto/googleapis/api/annotations - protoc-gen-swagger/options/annotations.proto: github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/options - coredocument/coredocument.proto: github.com/centrifuge/centrifuge-protobufs/gen/go/coredocument - precise-proofs/proofs/proto/proof.proto: github.com/centrifuge/precise-proofs/proofs/proto - invoice/invoice.proto: github.com/centrifuge/centrifuge-protobufs/gen/go/invoice - notification/notification.proto: github.com/centrifuge/centrifuge-protobufs/gen/go/notification - purchaseorder/purchaseorder.proto: github.com/centrifuge/centrifuge-protobufs/gen/go/purchaseorder - entity/entity.proto: github.com/centrifuge/centrifuge-protobufs/gen/go/entity - - - # Plugin overrides. For example, if you set "grpc-gpp: /usr/local/bin/grpc_cpp_plugin", - # This will mean that a plugin named "grpc-gpp" in the plugins list will be looked for - # at "/usr/local/bin/grpc_cpp_plugin" by setting the - # "--plugin=protoc-gen-grpc-gpp=/usr/local/bin/grpc_cpp_plugin" flag on protoc. -# plugin_overrides: -# grpc-gpp: /usr/local/bin/grpc_cpp_plugin - - # The list of plugins. - plugins: - - name: go - type: go - output: gen/go/ - flags: plugins=grpc - - - name: swagger - type: go - output: gen/swagger/ - - - name: grpc-gateway - type: go - output: gen/go/ diff --git a/protobufs/purchaseorder/service.proto b/protobufs/purchaseorder/service.proto deleted file mode 100644 index 9c113320b..000000000 --- a/protobufs/purchaseorder/service.proto +++ /dev/null @@ -1,160 +0,0 @@ -syntax = "proto3"; - -package po; - -option go_package = "popb"; -option java_multiple_files = true; -option java_outer_classname = "ServiceProto"; -option java_package = "com.po"; - -import "document/service.proto"; -import "google/api/annotations.proto"; -import "google/protobuf/timestamp.proto"; -import "protoc-gen-swagger/options/annotations.proto"; - -// PurchaseOrderService contains all common interactions for purchase order documents -service PurchaseOrderService { - rpc Create(PurchaseOrderCreatePayload) returns (PurchaseOrderResponse) { - option (google.api.http) = { - post: "/v1/purchase_orders" - body: "*" - }; - option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = { - description: "Creates a purchase order" - }; - } - rpc Update(PurchaseOrderUpdatePayload) returns (PurchaseOrderResponse) { - option (google.api.http) = { - put: "/v1/purchase_orders/{document_id}" - body: "*" - }; - option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = { - description: "Updates a purchase order" - }; - } - rpc GetVersion(GetVersionRequest) returns (PurchaseOrderResponse) { - option (google.api.http) = { - get: "/v1/purchase_orders/{document_id}/versions/{version_id}" - }; - option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = { - description: "Get a specific version of a purchase order" - }; - } - rpc Get(GetRequest) returns (PurchaseOrderResponse) { - option (google.api.http) = { - get: "/v1/purchase_orders/{document_id}" - }; - option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = { - description: "Get the current version of a purchase order" - }; - } -} - -message GetRequest { - string document_id = 1; -} - -message GetVersionRequest { - string document_id = 1; - string version_id = 2; -} - -message PurchaseOrderCreatePayload { - repeated string read_access = 1; - repeated string write_access = 2; - PurchaseOrderData data = 3; - // custom attributes - map attributes = 4; -} - -message PurchaseOrderUpdatePayload { - string document_id = 1; - repeated string read_access = 2; - repeated string write_access = 3; - PurchaseOrderData data = 4; - // custom attributes - map attributes = 5; -} - -message PurchaseOrderResponse { - document.ResponseHeader header = 1; - PurchaseOrderData data = 2; - // custom attributes - map attributes = 3; -} - -message PurchaseOrderData { - string status = 1; - // purchase order number or reference number - string number = 2; - string sender_order_id = 3; - string recipient_order_id = 4; - string requisition_id = 5; - string requester_name = 6; - string requester_email = 7; - string ship_to_company_name = 8; - string ship_to_contact_person_name = 9; - string ship_to_street1 = 10; - string ship_to_street2 = 11; - string ship_to_city = 12; - string ship_to_zipcode = 13; - string ship_to_state = 14; - string ship_to_country = 15; - string payment_terms = 16; - string currency = 17; - string total_amount = 18; - // centrifuge ID of the recipient - string recipient = 19; - // centrifuge ID of the sender - string sender = 20; - string comment = 21; - google.protobuf.Timestamp date_sent = 22; - google.protobuf.Timestamp date_confirmed = 23; - google.protobuf.Timestamp date_updated = 24; - google.protobuf.Timestamp date_created = 25; - repeated document.BinaryAttachment attachments = 26; - repeated LineItem line_items = 27; - repeated document.PaymentDetails payment_details = 28; -} - -message LineItem { - string status = 1; - string item_number = 2; - string description = 3; - string amount_invoiced = 4; - string amount_total = 5; - string requisition_number = 6; - string requisition_item = 7; - string part_no = 8; - string price_per_unit = 9; - string unit_of_measure = 10; - string quantity = 11; - string received_quantity = 12; - google.protobuf.Timestamp date_updated = 13; - google.protobuf.Timestamp date_created = 14; - int64 revision_number = 15; - repeated LineItemActivity activities = 16; - repeated TaxItem tax_items = 17; -} - -message TaxItem { - string item_number = 1; - string purchase_order_item_number = 2; - string tax_amount = 3; - string tax_rate = 4; - string tax_code = 5; - string tax_base_amount = 6; -} - -message LineItemActivity { - string item_number = 1; - //delivered, returned, credited, invoiced, paid, ... - string status = 2; - string quantity = 3; - string amount = 4; - //depending on status delivery note, invoice, ... - string reference_document_id = 5; - //line item from the reference document - string reference_document_item = 6; - google.protobuf.Timestamp date = 7; -} diff --git a/resources/data.go b/resources/data.go index d0785f7d2..9f625567c 100644 --- a/resources/data.go +++ b/resources/data.go @@ -69,7 +69,7 @@ func (fi bindataFileInfo) Sys() interface{} { return nil } -var _goCentrifugeBuildConfigsDefault_configYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x99\x5b\x73\xdb\x38\xb2\xc7\xdf\xf5\x29\xfa\x28\x2f\xc9\xa9\x89\x4c\x82\x77\x55\x9d\x07\xea\xe6\x24\x8e\x1d\xd9\xf2\x65\xe2\x97\x53\x20\xd9\x94\x10\x91\x04\x03\x80\xba\xf8\xd3\x6f\x01\xa4\x14\xe5\xe2\xcc\x56\x66\x67\xaa\xb6\x76\xf3\x62\x15\x88\xfe\xa3\xd1\xfd\xeb\x06\x89\xbc\x80\x09\xe6\xb4\x29\x14\x64\xb8\xc1\x82\xd7\x25\x56\x0a\x14\x4a\x55\xa1\x02\xba\xa4\xac\x92\x0a\x04\xab\xd6\x98\xec\x7b\x29\x56\x4a\xb0\xbc\x59\xe2\x15\xaa\x2d\x17\xeb\x21\x88\x46\x4a\x46\xab\x15\x2b\x8a\x9e\x11\x63\x15\x82\x5a\x21\x64\x9d\x6e\xd5\xce\x94\xa0\x56\x54\xc1\xf8\xa8\x00\x25\x65\x95\xd2\xfa\xbd\xc3\x94\x61\x0f\xe0\x05\xbc\xe7\x29\x2d\x8c\x0b\xac\x5a\x42\xca\x2b\x25\x68\xaa\x80\x66\x99\x40\x29\x51\x42\x85\x98\x81\xe2\x90\x20\x48\x54\xb0\x65\x6a\x05\x58\x6d\x60\x43\x05\xa3\x49\x81\x72\xd0\x83\x83\xbd\x96\x04\x60\xd9\x10\x1c\xc7\x31\xbf\x51\xad\x50\x60\x53\x76\x3b\x78\x9b\x0d\x21\x74\xc2\xf6\x59\xc2\xb9\x92\x4a\xd0\x7a\x8e\x28\x64\x6b\xfb\x1a\xfa\x67\xac\x76\xcf\x6c\x12\x0c\xac\x81\x35\xb0\xcf\x54\x5a\x9f\x39\x21\xb1\xc8\x19\xab\x73\x79\x76\x5d\xde\x5e\xef\x92\xed\xba\x79\xfc\xf8\x71\x92\x37\x4f\xb7\xc9\x6e\x1a\xdf\xe0\xed\xd5\xf8\x3d\x7f\xda\xef\x3d\x2f\xdc\x5c\x57\xcb\xfb\xcd\xfc\xf2\xd3\xfb\x8f\xeb\xfe\x1f\x88\x3a\x07\xd1\xfb\xdc\x9f\x5e\xf9\xe5\xfa\xf3\x03\x7e\x7a\xb8\x78\x20\x9f\xe7\x8d\xed\xff\x5e\x67\xe7\xce\xfa\x1d\xb7\x6f\x9d\x72\x45\x57\xf3\x91\xb7\x40\xaf\xb2\x5b\xd1\x43\xa8\xe2\x43\xa4\xda\x0d\xe8\xed\x63\xa5\x98\xda\xcf\x68\xaa\xb8\xd8\x0f\xa1\xdf\xef\x9e\xd0\x2a\x5d\x71\x71\x83\x35\x97\xec\x9b\x47\xac\xda\x70\x96\xe2\x5d\x55\x53\x1d\xbe\x7e\xbf\x67\xb2\x73\x49\x59\xf5\x43\x56\xba\x24\xc2\xcb\x9b\x16\x96\x57\x3d\x38\x85\xa3\xf5\xe5\x05\x5c\x35\x25\x0a\x96\xc2\xdb\x09\xf0\xdc\x80\x72\x82\x44\xa7\x71\xcc\x99\x67\x77\x56\xa3\x43\x62\xa0\x60\x52\x69\xcb\x8a\x67\xf8\x3d\x53\xb5\xe0\x1b\x66\x1e\x70\xa3\x7d\xe2\xc0\xc1\xd1\x3f\x4c\xb4\xe3\x0d\x08\xf1\x06\xc4\xb2\x06\x2e\xf9\x36\xd9\x36\x99\x38\x17\x9c\x3f\x5c\xc9\x47\xf9\x10\xdc\x26\xe9\xa3\x17\x5e\x05\xf6\xdd\xf5\xe2\xc2\x9b\x7c\x7a\xfc\x5c\xce\xd6\x6f\xe6\x6f\xb6\xbb\xd9\xc5\x6d\xbc\xe7\x77\x77\x93\x30\xcb\xfb\x3f\x92\x0f\xfd\x01\xb1\xad\xe7\xe4\x27\x48\xe4\xf6\x61\xea\xe4\x84\xbd\x4b\xee\xf0\x3a\x3a\xbf\xbb\xbb\x1e\xbd\x19\x8b\x87\xf7\xc9\x28\xa5\xd1\xe5\xf9\xe5\xe7\xbc\x4c\xc6\x4b\xd1\x24\xfd\x2e\x46\xd3\x0e\xec\x63\x26\xde\x4e\xe0\x35\x74\xd9\x78\x0e\x7d\xb7\x33\x7e\x4f\x75\x78\x20\xc3\xba\xe0\x7b\xcc\x60\x51\x52\xa1\x60\xdc\x11\x25\x21\xe7\xc2\x04\x74\xc9\x36\x58\x7d\x15\xca\xef\xa9\x83\x67\xb1\xb3\x76\x79\x18\x5a\x49\xe8\x5b\xb6\xe5\x24\x99\xeb\x51\x8f\x38\x5e\xe0\xc6\x88\x63\x2b\x18\xbb\x11\xb1\x1c\x3b\x77\x83\xd0\xfe\x09\xa0\xd6\x2e\x22\xf1\xc4\x75\x47\xa3\x70\x46\x9c\x89\x97\xd9\x24\xc2\x51\x48\xa8\x67\x65\x4e\xe8\x87\xc9\xc8\x4d\xec\x14\x67\xf6\xec\x39\x94\xad\x5d\xea\xc6\x21\x8e\x48\x90\x8f\x9c\x29\x25\x63\x2b\xf2\xbc\x59\x48\xbd\x91\xed\xdb\xde\x88\xf8\x59\xe8\xcd\xc6\x23\x0c\xb1\x83\xfe\x82\x6f\x68\xbb\xeb\x13\x44\x13\x14\x15\x2d\x56\xc8\x96\x2b\x25\x7f\x0d\x6f\xf2\x27\xf1\xfe\xca\x85\x7f\x1a\x70\xdb\x72\x07\xb6\xe7\x0e\xec\x70\xe0\x7d\xd7\xcd\x0e\x04\x2e\x92\x5d\x72\x31\x4e\x1e\x57\xd1\xbb\x7b\x25\xaf\xf7\xf7\xe7\xd9\xed\x5c\x50\xf7\xa6\x5e\xc4\xae\x4a\x36\xd2\xa7\x95\x6d\x7f\xda\x9e\xc7\xe4\xa9\xff\x03\x79\x6f\x60\x87\xde\x80\x38\xc1\x73\x0b\x5c\x97\x24\x5d\x94\x62\xca\xe8\xe2\xf2\xde\x5d\xde\x6d\x82\x87\xf3\x55\xbd\xbc\xd9\xf2\x70\xcb\x67\x0b\xf9\x66\xf5\x78\x9e\x9c\x33\x87\xc6\xe1\xee\xe7\x88\x9b\xec\x3c\x0b\x38\xf9\x0b\x08\xff\x09\xe0\xb6\xe3\x93\x69\x3a\xca\x43\x3f\x88\x88\xeb\x4c\x89\x9b\xc7\xd6\x74\xec\x12\x2f\x23\x68\x5b\xb1\x15\x12\xe2\xa4\xc1\xe4\xa7\x80\x07\x76\x68\x4d\x82\xc0\xb1\xad\x0c\xd3\x30\x1e\x91\x30\xa6\xa1\x45\xa6\xa9\x15\xcd\xf2\x98\x4c\x66\xbe\x8b\x91\x15\xa4\xcf\x03\x6e\x87\x8e\x1d\x58\x6e\x68\xfb\x6e\x98\x63\x9e\xa3\x1b\xb9\xd6\xcc\x99\xc4\x71\xe6\xd0\x20\x49\x93\xc4\x4a\xbd\x38\x9e\x75\x80\xdf\xf0\x5a\x2a\xfc\x0e\xf1\x8c\x2f\x6b\xaa\xd2\xd5\xaf\xd1\xed\xfc\x49\xba\x0f\xab\xc3\xcb\xdb\x0f\x93\x0f\x90\x0a\xa4\x0a\x41\x74\xae\x6a\xc2\x8d\xce\xab\xff\xb4\x8e\xde\x06\xe0\x39\xe0\x9d\xbf\x97\x77\x2b\x73\x22\x7b\x1a\x10\x87\x78\x63\xcc\xc6\xae\x3d\x75\x43\xcb\x73\xa6\x41\x40\xc2\x90\x86\xd1\x8c\x4c\x1d\xdb\xb6\xbd\x9f\xf2\x4e\xc6\xa1\x35\xb3\x27\x34\x9f\xd0\x80\xc6\x13\x4c\xc8\xd8\x0e\xbc\xcc\x1d\xb9\x4e\x1c\x7a\xa1\x1b\x38\x53\xdb\x0e\x6c\xe7\x79\xde\xdd\x28\xc1\xc8\xb1\xac\xb1\xe3\x8f\x73\x8f\x38\x61\x32\xf3\xa3\xa9\x3b\x76\x23\xcf\xb7\x66\xb3\x30\x0f\x66\x7e\x40\xa6\xee\xc9\x5b\x8c\x7e\x69\x39\xe5\x1d\x26\x1f\xe0\xea\xc3\x2d\xdc\x2d\xa6\xff\xd3\x03\xc0\x32\xa1\x22\xa5\x19\x0a\xae\x67\xfd\x52\x09\xd8\xd6\xb3\x6c\x7e\x85\x8f\x1d\x85\x03\x9b\x90\x81\x6d\x3f\xdb\x2e\xe3\xa5\x33\x4d\x63\x25\x3e\xde\x8f\x77\xdb\x27\x7f\xed\xcb\xdb\x88\x3d\x2e\x6e\x9e\xd4\x53\x34\x09\xf6\x77\x4f\xf5\x68\x7e\x33\x9d\x3d\x89\x3b\x7e\xdf\xff\x7e\x05\xe2\x92\x01\x21\xf6\xc0\xb6\x9f\xed\xf8\x17\xe7\x5b\xb6\xfb\x1d\xab\xe6\xf7\xf8\xfe\xf3\xfa\xdd\x45\x59\xbd\x59\xc4\xef\x26\x9f\x9e\xf2\x00\xcf\x2f\xb9\xaf\x04\x67\xcb\xc7\x5d\x19\xc4\xde\xcd\xcf\x09\x2d\xdb\xe8\x3e\x47\xa8\xfd\xf7\x12\x1a\xcf\x5c\xcf\x4f\x6d\xdf\x09\x7d\xea\xbb\x79\xe6\xce\xdc\xc4\x8f\x68\x6e\x3b\x34\xf4\x27\xb9\x35\xf2\x7c\x12\x53\xcb\xfa\x29\xa1\xbe\x13\x8c\xc2\xb1\x33\x21\x71\xec\x8c\x53\x62\xf9\x93\xc8\xf5\xec\x28\xf1\xdc\x30\x22\x56\x18\xa5\xd1\xd4\x0f\xa2\xc8\x7a\x9e\xd0\x91\x87\x2e\x71\xb2\x71\x1a\xb8\x56\x32\x1a\x87\x56\x1e\x59\xbe\xed\x38\x68\x7b\xbe\x65\xe7\x51\x68\x45\x51\xe8\x78\xfe\x37\x84\x7e\x41\xea\x04\xc8\x7f\x35\x8c\x7f\x35\x8a\xff\x05\xf1\xdf\x13\xc4\x17\x30\xa1\x8a\xc2\x42\x71\x41\x97\xd8\x93\xed\xdf\xf6\x33\x7d\x4e\xd5\xca\x44\xa6\xd0\x1f\x83\x93\x11\xe4\xac\xc0\x1e\x40\x4d\xd5\x6a\x08\x67\xaa\xac\xcf\xbe\x5c\x17\xfc\x7f\x46\x15\x1d\x98\x99\x59\xa2\x75\xc7\xbc\xca\xd9\xb2\x11\x54\x31\x5e\x1d\x17\x48\xcd\xe8\xe2\xd7\x97\x69\x05\xbe\x5b\x2d\x4e\x53\xde\x54\x4a\xc2\x1a\xf7\xd0\xed\xa2\x47\xbb\x41\xbd\xce\x1a\xf7\x7a\x18\x3b\xc5\xc3\x23\x6d\xfb\xb6\x52\x28\x72\x9a\x22\x6c\x35\x40\x06\x84\x78\xfe\x16\x68\x95\xc1\x9c\xcc\x61\x81\x62\x83\xc2\xbc\xda\x60\xa5\xdf\x5d\x7a\xfa\xad\xe4\x0d\x97\xaa\xa2\x25\x0e\xe1\xf8\x89\xdf\x7b\x01\x73\x2e\x54\x27\xa3\x25\x7e\x6c\xaa\x27\x0d\x21\xb4\x42\xa2\x97\xd7\x55\xfa\x5a\xf1\xd7\x35\xa2\x80\xf4\x34\x6a\xb2\x57\x93\xba\x0d\xd2\xa2\xc6\x94\xe5\x7b\x98\xee\x94\xf9\x22\x80\xb7\xf3\x13\x6f\xb5\x28\xa4\xb4\x82\x04\x41\x20\x4d\x57\x98\x01\x55\xc0\x72\x48\x70\xc5\xaa\x0c\xae\xe2\x5b\x2d\x83\x9d\xf5\xdb\xf9\x10\xb6\x83\xdd\x60\x3f\x78\x6a\x53\xa0\xbd\x6e\x24\x66\xc7\x42\xd0\xfb\x2e\xe8\x1e\x85\x4e\x84\x71\xd7\x94\xb1\x99\x7d\xcb\x4a\xe4\x8d\xd9\x66\x05\xbc\xc6\xaa\xbb\xc5\xa9\x30\x35\x5e\xeb\xb7\x3b\xbd\x19\xd9\x83\xc3\x70\x67\x32\x84\xbe\x63\xc9\xbe\x51\x29\x59\xc5\xca\xa6\x84\x0c\x0b\xba\x37\xeb\xe2\x06\xc5\x1e\x6a\x52\x83\x40\x59\xf3\x4a\xa2\x56\xa2\x1b\xce\x32\x50\xac\xd4\xab\x50\xa5\x68\xba\x96\x46\x80\x66\x9f\x1a\xa9\x20\xa1\xda\x6f\x5e\xc1\x8a\x4b\xa5\x2d\x79\x23\x52\x94\xf0\x72\xb1\x98\xfc\x06\xe3\xf9\xdd\x6f\x90\x72\x81\x12\x06\x83\xc1\xab\xee\xfa\x89\xaf\x81\x55\x50\xf0\xa5\xa9\xfc\x21\xf4\xb5\x7f\xda\x57\xd9\x94\x98\x41\xb2\xd7\xdb\x6a\x73\xd0\xd7\x51\xdc\xfd\xdf\xcb\x0d\x2d\x1a\xbc\x41\x9a\xc1\xff\x02\x79\x05\x4c\x42\x81\xd2\xbc\xe1\x56\x60\x9e\x41\x82\x05\xdf\xfe\xa6\xa3\x57\x41\xba\xa2\xd5\x12\x8f\xfb\x98\x98\x3d\x2a\x0e\xbb\x1e\x7c\x3d\x38\x84\xbe\x67\x59\xa5\x34\xa5\x78\xdd\x60\x83\xdf\x20\x60\x22\x43\xe5\xbe\x4a\x57\x82\x57\xbc\x91\xfa\x25\x3a\x45\x29\x59\xb5\xec\x7d\xd6\x06\x2d\x20\xed\xbd\x9c\x6c\x71\x68\xca\x04\x85\x3e\x30\x74\x1f\x44\x21\xcf\xba\xad\x89\xee\x95\x7c\xcb\x8a\x42\xb3\x42\x8b\x82\xa7\x54\xb5\xb4\x48\x45\x85\x6a\xea\x1e\x68\xfb\x87\xd6\x50\x9f\x29\x96\xd1\x9f\x09\x44\x09\x4d\xad\x23\x0a\xe9\x3e\x2d\x50\xb6\x00\xb4\x4b\xe8\x80\x6c\x29\x33\x17\x7a\x5d\x2e\x75\x75\x41\xf7\xf8\x81\x32\xc3\xc0\xe5\xa2\xed\xc9\xe6\x60\xeb\x7c\x14\xa8\x04\x43\x69\x9c\xd9\x76\x08\x52\x50\x54\xea\x83\x4d\xff\xb9\x69\x27\x98\xf3\xad\x77\x72\x10\x48\x53\x13\x2c\xfd\x3a\x62\xbd\xc3\x31\xd0\x15\x0e\x16\xa8\x3b\xfc\x76\xc5\xd2\xd5\xf1\x88\x80\xae\xfe\x75\x4e\x1a\x89\x87\xb3\x95\xeb\x08\x76\x1f\x29\x99\x46\x44\x0f\xa6\x8d\x54\xbc\xec\x16\x39\x34\xa7\xee\xee\xb3\x6b\x3b\x57\xa6\x0f\xf4\xf5\x61\xd4\x3f\xde\x70\x9a\xbe\xd7\x09\x1f\xd7\x4d\x0b\x86\x95\x6a\x0b\xf6\xe5\x56\x13\xf2\xb9\x61\x02\x61\x2b\x81\x0b\x60\x75\xda\x5d\x7b\xd2\xa4\x30\xf4\xa7\xe6\xf3\xa8\x8d\xa6\xa6\x57\x1b\xde\xdd\xbc\x1f\xc2\x4a\xa9\x7a\x78\x76\xa6\xf3\x57\x68\xf2\x87\x91\xe7\x7a\x6d\x61\xd1\x9d\x29\xac\x43\x3c\x97\x54\xef\x89\xa5\x46\xaf\xee\x6a\x8d\x82\x12\xb4\x92\xd4\x54\xac\xde\xe9\x16\x99\xb1\x26\x16\x9c\x6f\x91\x41\xc5\xb7\x3d\xd0\x5a\xe7\x54\xce\xb5\xf5\x10\x88\x75\xfc\x67\xa6\x9e\x53\x09\x05\x2b\x59\x77\x7e\x66\x2c\xcf\x51\xe8\xdd\x1d\x33\x74\xac\x22\x4d\xc2\x92\xca\xf7\x66\xf6\xe1\xc6\x76\x6c\x3e\xf7\x0c\x62\x9d\xa6\x1e\x8d\xb3\xec\x02\xf7\x43\x70\x4e\x07\x6f\x70\xc3\xd7\x68\xc6\x3d\xef\x30\xdc\x9e\x9e\x63\x5e\x96\x4c\xb7\xd3\x6f\xc6\xe7\x02\x0f\x8f\xec\x2f\x52\x55\xae\x2e\x59\xa5\x86\x10\x7d\x35\x76\xab\x83\x91\xa3\x98\x09\x5e\x0e\xc1\xf6\x8e\x7b\x3c\xf4\x3a\xc5\x0d\xde\x6d\xec\xaa\x2f\xf9\x3c\x8d\x62\x97\xb9\x2c\x6b\x2f\xaf\x29\x24\x05\x4f\xd7\xe6\x18\x69\x13\x08\x4a\xb0\xe5\x12\x05\x66\x6d\x67\x54\xb8\x53\x87\xca\x68\xbb\xa3\x6f\x1d\xda\xe3\x8f\x16\x16\xba\xfd\xf0\xaa\x38\x69\x4f\xf2\x78\x83\x7f\x70\xe9\x8b\xb4\xee\x56\x5f\xcb\xdb\x5e\xa7\x7e\xa5\xf9\x3b\xf5\xbd\xe6\xbc\xd0\xd9\x3e\x56\xa3\xe2\x20\xb1\xca\xbe\x01\x85\x6f\xcc\x89\x50\xd2\xdd\xb1\x28\x49\x17\xa9\x1f\x4b\x32\x7d\xb6\x6e\x68\x61\x74\xf7\x6d\xc7\xa0\xda\xc1\xb4\x11\x86\x95\x53\x8b\x15\x95\x90\x20\x56\x90\xa1\xc2\x54\x99\x30\x1d\x04\xf4\x7a\xba\x5b\x92\xb6\x55\xd2\x6a\x0f\x19\x26\xcd\x72\xd9\x1d\x3e\xba\x36\x4d\x5b\x5b\x72\xd0\x81\xe8\x99\xa7\x6d\x0f\xc0\xca\x94\x93\x19\xd1\x5d\x5f\xdb\xf4\x40\xff\x1a\x42\x4e\x0b\x89\x66\x56\x5d\x0b\x9e\xb7\x24\x1f\x84\xf5\xe1\xa7\x47\x0f\xd3\x7a\x2d\x5a\xdd\xff\x3a\xd4\x02\xd3\x8e\x30\x25\x1a\xec\xfd\x23\x00\x00\xff\xff\x58\xad\xc1\xac\x6a\x19\x00\x00") +var _goCentrifugeBuildConfigsDefault_configYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x99\xdb\x73\xdb\xba\x73\xc7\xdf\xf5\x57\x6c\x95\x97\xa4\x73\x22\x93\xe0\x5d\x33\x7d\xa0\x6e\x4e\xe2\xd8\x91\x2d\x5f\x4e\xfc\xd2\x01\xc9\xa5\x84\x88\x24\x18\x00\xd4\xc5\x7f\x7d\x07\x20\xa5\x28\x71\x9c\xdf\x34\xa7\xe7\xcc\x74\xda\xbc\x58\x83\xcb\x17\x8b\xdd\xcf\x2e\x40\xe4\x15\x4c\x30\xa7\x4d\xa1\x20\xc3\x0d\x16\xbc\x2e\xb1\x52\xa0\x50\xaa\x0a\x15\xd0\x25\x65\x95\x54\x20\x58\xb5\xc6\x64\xdf\x4b\xb1\x52\x82\xe5\xcd\x12\xaf\x50\x6d\xb9\x58\x0f\x41\x34\x52\x32\x5a\xad\x58\x51\xf4\x8c\x18\xab\x10\xd4\x0a\x21\xeb\x74\xab\x76\xa4\x04\xb5\xa2\x0a\xc6\x47\x05\x28\x29\xab\x94\xd6\xef\x1d\x86\x0c\x7b\x00\xaf\xe0\x23\x4f\x69\x61\x4c\x60\xd5\x12\x52\x5e\x29\x41\x53\x05\x34\xcb\x04\x4a\x89\x12\x2a\xc4\x0c\x14\x87\x04\x41\xa2\x82\x2d\x53\x2b\xc0\x6a\x03\x1b\x2a\x18\x4d\x0a\x94\x83\x1e\x1c\xe6\x6b\x49\x00\x96\x0d\xc1\x71\x1c\xf3\x1b\xd5\x0a\x05\x36\x65\xb7\x83\xf7\xd9\x10\x42\x27\x6c\xfb\x12\xce\x95\x54\x82\xd6\x73\x44\x21\xdb\xb9\x6f\xa1\x7f\xc6\x6a\xf7\xcc\x26\xc1\xc0\x1a\x58\x03\xfb\x4c\xa5\xf5\x99\x13\x12\x8b\x9c\xb1\x3a\x97\x67\xd7\xe5\xed\xf5\x2e\xd9\xae\x9b\xc7\xcf\x9f\x27\x79\xf3\x74\x9b\xec\xa6\xf1\x0d\xde\x5e\x8d\x3f\xf2\xa7\xfd\xde\xf3\xc2\xcd\x75\xb5\xbc\xdf\xcc\x2f\xbf\x7c\xfc\xbc\xee\xff\x0b\x51\xe7\x20\x7a\x9f\xfb\xd3\x2b\xbf\x5c\x7f\x7d\xc0\x2f\x0f\x17\x0f\xe4\xeb\xbc\xb1\xfd\x3f\xeb\xec\xdc\x59\x7f\xe0\xf6\xad\x53\xae\xe8\x6a\x3e\xf2\x16\xe8\x55\x76\x2b\x7a\x70\x55\x7c\xf0\x54\xbb\x01\xbd\x7d\xac\x14\x53\xfb\x19\x4d\x15\x17\xfb\x21\xf4\xfb\x5d\x0f\xad\xd2\x15\x17\x37\x58\x73\xc9\x7e\xe8\x62\xd5\x86\xb3\x14\xef\xaa\x9a\x6a\xf7\xf5\xfb\x3d\x13\x9d\x4b\xca\xaa\x9f\xb2\xd2\x05\x11\x5e\xdf\xb4\xb0\xbc\xe9\xc1\x29\x1c\xad\x2d\xaf\xe0\xaa\x29\x51\xb0\x14\xde\x4f\x80\xe7\x06\x94\x13\x24\x3a\x8d\x63\xcc\x3c\xbb\x9b\x35\x3a\x04\x06\x0a\x26\x95\x9e\x59\xf1\x0c\x9f\x33\x55\x0b\xbe\x61\xa6\x83\x1b\xed\x13\x03\x0e\x86\xfe\xcb\x40\x3b\xde\x80\xb8\xfe\xc0\x76\xdc\x41\xe4\xfd\x18\x6c\x9b\x4c\x9c\x0b\xce\x1f\xae\xe4\xa3\x7c\x08\x6e\x93\xf4\xd1\x0b\xaf\x02\xfb\xee\x7a\x71\xe1\x4d\xbe\x3c\x7e\x2d\x67\xeb\x77\xf3\x77\xdb\xdd\xec\xe2\x36\xde\xf3\xbb\xbb\x49\x98\xe5\xfd\x1f\xe5\xed\x28\x1c\xd8\xbe\x3d\xb0\x43\xeb\x25\xfd\x09\x12\xb9\x7d\x98\x3a\x39\x61\x1f\x92\x3b\xbc\x8e\xce\xef\xee\xae\x47\xef\xc6\xe2\xe1\x63\x32\x4a\x69\x74\x79\x7e\xf9\x35\x2f\x93\xf1\x52\x34\x49\xbf\x73\xd2\xb4\x23\xfb\x18\x8a\xf7\x13\x78\x0b\x5d\x38\x5e\x62\xdf\xed\x26\x7f\xa4\xda\x3f\x90\x61\x5d\xf0\x3d\x66\xb0\x28\xa9\x50\x30\xee\x90\x92\x90\x73\x61\x3c\xba\x64\x1b\xac\xbe\xf3\xe5\x73\xec\xe0\x45\xee\xac\x5d\x1e\x86\x56\x12\xfa\x96\x6d\x39\x49\xe6\x7a\xd4\x23\x8e\x17\xb8\x31\xe2\xd8\x0a\xc6\x6e\x44\x2c\xc7\xce\xdd\x20\xb4\x7f\x41\xa8\xb5\x8b\x48\x3c\x71\xdd\xd1\x28\x9c\x11\x67\xe2\x65\x36\x89\x70\x14\x12\xea\x59\x99\x13\xfa\x61\x32\x72\x13\x3b\xc5\x99\x3d\x7b\x89\x65\x6b\x97\xba\x71\x88\x23\x12\xe4\x23\x67\x4a\xc9\xd8\x8a\x3c\x6f\x16\x52\x6f\x64\xfb\xb6\x37\x22\x7e\x16\x7a\xb3\xf1\x08\x43\xec\xa8\xbf\xe0\x1b\xda\xee\xfa\x84\xd1\x04\x45\x45\x8b\x15\xb2\xe5\x4a\xc9\xdf\xe3\x9b\xfc\x45\xbe\xbf\x33\xe1\xbf\x47\x38\x19\x10\xc7\x1a\xd8\xb6\xff\x12\x82\x8b\x64\x97\x5c\x8c\x93\xc7\x55\xf4\xe1\x5e\xc9\xeb\xfd\xfd\x79\x76\x3b\x17\xd4\xbd\xa9\x17\xb1\xab\x92\x8d\xf4\x69\x65\xdb\x5f\xb6\xe7\x31\x79\x7a\x86\x38\x71\xdc\x41\x40\x06\x36\x09\x5e\x92\xbf\x2e\x49\xba\x28\xc5\x94\xd1\xc5\xe5\xbd\xbb\xbc\xdb\x04\x0f\xe7\xab\x7a\x79\xb3\xe5\xe1\x96\xcf\x16\xf2\xdd\xea\xf1\x3c\x39\x67\x0e\x8d\xc3\xdd\xaf\x09\x37\xc1\x79\x91\x6f\xf2\x37\x00\xfe\x0b\xbe\x6d\xc7\x27\xd3\x74\x94\x87\x7e\x10\x11\xd7\x99\x12\x37\x8f\xad\xe9\xd8\x25\x5e\x46\xd0\xb6\x62\x2b\x24\xc4\x49\x83\xc9\x2f\xf9\x0e\xec\xd0\x9a\x04\x81\x63\x5b\x19\xa6\x61\x3c\x22\x61\x4c\x43\x8b\x4c\x53\x2b\x9a\xe5\x31\x99\xcc\x7c\x17\x23\x2b\x48\x5f\xe6\xdb\x0e\x1d\x3b\xb0\xdc\xd0\xf6\xdd\x30\xc7\x3c\x47\x37\x72\xad\x99\x33\x89\xe3\xcc\xa1\x41\x92\x26\x89\x95\x7a\x71\x3c\xeb\xf8\xbe\xe1\xb5\x54\xf8\x8c\xf0\x8c\x2f\x6b\xaa\xd2\xd5\xef\xc1\xed\xfc\x45\xb8\x0f\xab\xc3\xeb\xdb\x4f\x93\x4f\x90\x0a\xa4\x0a\x41\x74\xa6\x6a\xc0\x8d\xce\x9b\xff\x73\x15\xbd\xf5\xc0\x4b\xc4\x3b\xff\x2c\xf0\x56\xe6\x44\xf6\x34\x20\x0e\xf1\xc6\x98\x8d\x5d\x7b\xea\x86\x96\xe7\x4c\x83\x80\x84\x21\x0d\xa3\x19\x99\x3a\xb6\x6d\x7b\xbf\x04\x9e\x8c\x43\x6b\x66\x4f\x68\x3e\xa1\x01\x8d\x27\x98\x90\xb1\x1d\x78\x99\x3b\x72\x9d\x38\xf4\x42\x37\x70\xa6\xb6\x1d\xd8\xce\xcb\xc0\xbb\x51\x82\x91\x63\x59\x63\xc7\x1f\xe7\x1e\x71\xc2\x64\xe6\x47\x53\x77\xec\x46\x9e\x6f\xcd\x66\x61\x1e\xcc\xfc\x80\x4c\xdd\x93\x6b\x8c\xbe\xb5\x9c\x02\x0f\x93\x4f\x70\xf5\xe9\x16\xee\x16\xd3\x7f\xeb\x01\x60\x99\x50\x91\xd2\x0c\x05\xd7\xa3\x7e\x2b\x07\x6c\xeb\x45\x38\x9f\xf3\x43\xc8\xc0\xb6\x5f\xac\x97\xf1\xd2\x99\xa6\xb1\x12\x9f\xef\xc7\xbb\xed\x93\xbf\xf6\xe5\x6d\xc4\x1e\x17\x37\x4f\xea\x29\x9a\x04\xfb\xbb\xa7\x7a\x34\xbf\x99\xce\x9e\xc4\x1d\xbf\xef\x3f\x5f\xc1\x14\x7c\x62\x0f\x6c\xfb\xd9\x05\xf6\xb0\xc2\xc5\xf9\x96\xed\xfe\xc4\xaa\xf9\x33\xbe\xff\xba\xfe\x70\x51\x56\xef\x16\xf1\x87\xc9\x97\xa7\x3c\xc0\xf3\x4b\xee\x2b\xc1\xd9\xf2\x71\x57\x06\xb1\x77\xf3\x6b\x42\xcb\xd6\xbb\x2f\x11\x6a\xff\xb3\x84\xc6\x33\xd7\xf3\x53\xdb\x77\x42\x9f\xfa\x6e\x9e\xb9\x33\x37\xf1\x23\x9a\xdb\x0e\x0d\xfd\x49\x6e\x8d\x3c\x9f\xc4\xd4\xb2\x7e\x49\xa8\xef\x04\xa3\x70\xec\x4c\x48\x1c\x3b\xe3\x94\x58\xfe\x24\x72\x3d\x3b\x4a\x3c\x37\x8c\x88\x15\x46\x69\x34\xf5\x83\x28\xb2\x5e\x26\x74\xe4\xa1\x4b\x9c\x6c\x9c\x06\xae\x95\x8c\xc6\xa1\x95\x47\x96\x6f\x3b\x0e\xda\x9e\x6f\xd9\x79\x14\x5a\x51\x14\x3a\x9e\xff\x03\xa1\xdf\x90\x3a\x01\xf2\x7f\x1a\xc6\xbf\x1b\xc5\xff\x07\xf1\x7f\x27\x88\xaf\x60\x42\x15\x85\x85\xe2\x82\x2e\xb1\x27\xdb\xbf\xed\x77\xfa\x9c\xaa\x95\xf1\x4c\xa1\xbf\x06\x27\x23\xc8\x59\x81\x3d\x80\x9a\xaa\xd5\x10\xce\x54\x59\x9f\x7d\x7b\x2f\xf8\xcf\x8c\x2a\x3a\x30\x23\xb3\x44\xeb\x8e\x79\x95\xb3\x65\x23\xa8\x62\xbc\x3a\x2e\x90\x9a\xd6\xc5\xef\x2f\xd3\x0a\x3c\x5b\x2d\x4e\x53\xde\x54\x4a\xc2\x1a\xf7\xd0\xed\xa2\x47\xbb\x46\xbd\xce\x1a\xf7\xba\x19\x3b\xc5\x43\x97\x9e\xfb\xbe\x52\x28\x72\x9a\x22\x6c\x35\x40\x06\x84\x78\xfe\x1e\x68\x95\xc1\x9c\xcc\x61\x81\x62\x83\xc2\xdc\x6d\xb0\xd2\x97\x97\x9e\xbe\x96\xbc\xe3\x52\x55\xb4\xc4\x21\x1c\xbf\xf1\x7b\xaf\x60\xce\x85\xea\x64\xb4\xc4\xcf\xa7\xea\x41\x43\x08\xad\x90\xe8\xe5\x75\x96\xbe\x55\xfc\x6d\x8d\x28\x20\x3d\xf5\x9a\xec\xd5\xa4\x6e\x9d\xb4\xa8\x31\x65\xf9\x1e\xa6\x3b\x65\xbe\x08\xe0\xfd\xfc\xc4\x5a\x2d\x0a\x29\xad\x20\x41\x10\x48\xd3\x15\x66\x40\x15\xb0\x1c\x12\x5c\xb1\x2a\x83\xab\xf8\x56\xcb\x60\x37\xfb\xfd\x7c\x08\xdb\xc1\x6e\xb0\x1f\x3c\xb5\x21\xd0\x56\x37\x12\xb3\x63\x22\xe8\x7d\x17\x74\x8f\x42\x07\xc2\x98\x6b\xd2\xd8\x8c\xbe\x65\x25\xf2\xc6\x6c\xb3\x02\x5e\x63\xd5\x3d\xe3\x54\x98\x1a\xab\xf5\xf5\x4e\x6f\x46\xf6\xe0\xd0\xdc\x4d\x19\x42\xdf\xb1\x64\xdf\xa8\x94\xac\x62\x65\x53\x42\x86\x05\xdd\x9b\x75\x71\x83\x62\x0f\x35\xa9\x41\xa0\xac\x79\x25\x51\x2b\xd1\x0d\x67\x19\x28\x56\xea\x55\xa8\x52\x34\x5d\x4b\x23\x40\xb3\x2f\x8d\x54\x90\x50\x6d\x37\xaf\x60\xc5\xa5\xd2\x33\x79\x23\x52\x94\xf0\x7a\xb1\x98\xfc\x01\xe3\xf9\xdd\x1f\x90\x72\x81\x12\x06\x83\xc1\x9b\xee\xfd\x89\xaf\x81\x55\x50\xf0\xa5\xc9\xfc\x21\xf4\xb5\x7d\xda\x56\xd9\x94\x98\x41\xb2\xd7\xdb\x6a\x63\xd0\xd7\x5e\xdc\xfd\xc7\xeb\x0d\x2d\x1a\xbc\x41\x9a\xc1\xbf\x03\x79\x03\x4c\x42\x81\xd2\x5c\x71\x2b\x30\x7d\x90\x60\xc1\xb7\x7f\x68\xef\x55\x90\xae\x68\xb5\xc4\xe3\x3e\x26\x66\x8f\x8a\xc3\xae\x07\xdf\x37\x0e\xa1\xef\x59\x56\x29\x4d\x2a\x5e\x37\xd8\xe0\x0f\x08\x18\xcf\x50\xb9\xaf\xd2\x95\xe0\x15\x6f\xa4\xbe\x45\xa7\x28\x25\xab\x96\xbd\xaf\x7a\x42\x0b\x48\xfb\x30\x27\x5b\x1c\x9a\x32\x41\xa1\x0f\x0c\x5d\x07\x51\xc8\xb3\x6e\x6b\xa2\xbb\x93\x6f\x59\x51\x68\x56\x68\x51\xf0\x94\xaa\x96\x16\xa9\xa8\x50\x4d\xdd\x03\x3d\xff\xa1\x9d\xa8\xcf\x14\xcb\xe8\xcf\x04\xa2\x84\xa6\xd6\x1e\x85\x74\x9f\x16\x28\x5b\x00\xda\x25\xb4\x43\xb6\x94\x99\x17\xbd\x2e\x96\x3a\xbb\xa0\xeb\x7e\xa0\xcc\x30\x70\xb9\x68\x6b\xb2\x39\xd8\x3a\x1b\x05\x2a\xc1\x50\x1a\x63\xb6\x1d\x82\x14\x14\x95\xfa\x60\xd3\x7f\x6e\xda\x01\xe6\x7c\xeb\x9d\x1c\x04\xd2\xe4\x04\x4b\xbf\xf7\x58\xef\x70\x0c\x74\x89\x83\x05\xea\x0a\xbf\x5d\xb1\x74\x75\x3c\x22\xa0\xcb\x7f\x1d\x93\x46\xe2\xe1\x6c\xe5\xda\x83\xdd\x57\x4a\xa6\x11\xd1\x8d\x69\x23\x15\x2f\xbb\x45\x0e\xc5\xa9\x7b\xfc\xec\xca\xce\x95\xa9\x03\x7d\x7d\x18\xf5\x8f\x4f\x9c\xa6\xee\x75\xc2\xc7\x75\xd3\x82\x61\xa5\xda\x84\x7d\xbd\xd5\x84\x7c\x6d\x98\x40\xd8\x4a\xe0\x02\x58\x9d\x76\xef\x9e\x34\x29\x0c\xfd\xa9\xf9\x3e\x6a\xbd\xa9\xe9\xd5\x13\xef\x6e\x3e\x0e\x61\xa5\x54\x3d\x3c\x3b\xd3\xf1\x2b\x34\xf9\xc3\xc8\x73\xbd\x36\xb1\xe8\xce\x24\xd6\xc1\x9f\x4b\xaa\xf7\xc4\x52\xa3\x57\x77\xb9\x46\x41\x09\x5a\x49\x6a\x32\x56\xef\x74\x8b\xcc\xcc\x26\x16\x9c\x6f\x91\x41\xc5\xb7\x3d\xd0\x5a\xe7\x54\xce\xf5\xec\x21\x10\xeb\xf8\xcf\x0c\x3d\xa7\x12\x0a\x56\xb2\xee\xfc\xcc\x58\x9e\xa3\xd0\xbb\x3b\x46\xe8\x98\x45\x9a\x84\x25\x95\x1f\xcd\xe8\xc3\x93\xed\xd8\x7c\xef\x19\xc4\x3a\x4d\xdd\x1a\x67\xd9\x05\xee\x87\xe0\x9c\x36\xde\xe0\x86\xaf\xd1\xb4\x7b\xde\xa1\xb9\x3d\x3d\xc7\xbc\x2c\x99\x2e\xa7\x3f\xb4\xcf\x05\x1e\xba\xec\x6f\x52\x55\xae\x2e\x59\xa5\x86\x10\x7d\xd7\x76\xab\x9d\x91\xa3\x98\x09\x5e\x0e\xc1\xf6\x8e\x7b\x3c\xd4\x3a\xc5\x0d\xde\xad\xef\xaa\x6f\xf1\x3c\xf5\x62\x17\xb9\x2c\x6b\x5f\xaf\x29\x24\x05\x4f\xd7\xe6\x18\x69\x03\x08\x4a\xb0\xe5\x12\x05\x66\x6d\x65\x54\xb8\x53\x87\xcc\x68\xab\xa3\x6f\x1d\xca\xe3\xcf\x16\x16\xba\xfc\xf0\xaa\x38\x29\x4f\xf2\xf8\x84\x7f\x30\xe9\x9b\xb4\xae\x56\xdf\xcb\xdb\x5e\xa7\x7e\xa5\xf9\x3b\xb5\xbd\xe6\xbc\xd0\xd1\x3e\x66\xa3\xe2\x20\xb1\xca\x7e\x00\x85\x6f\xcc\x89\x50\xd2\xdd\x31\x29\x49\xe7\xa9\x9f\x4b\x32\x7d\xb6\x6e\x68\x61\x74\xf7\x6d\xc5\xa0\xda\xc0\xb4\x11\x86\x95\xd3\x19\x2b\x2a\x21\x41\xac\x20\x43\x85\xa9\x32\x6e\x3a\x08\xe8\xf5\x74\xb5\x24\x6d\xa9\xa4\xd5\x1e\x32\x4c\x9a\xe5\xb2\x3b\x7c\x74\x6e\x9a\xb2\xb6\xe4\xa0\x1d\xd1\x33\xbd\x6d\x0d\xc0\xca\xa4\x93\x69\xd1\x55\x5f\xcf\xe9\x81\xfe\x35\x84\x9c\x16\x12\xcd\xa8\xba\x16\x3c\x6f\x49\x3e\x08\xeb\xc3\x4f\xb7\x1e\x86\xf5\x5a\xb4\xba\xff\x76\xa8\x05\xa6\x1d\x61\x4a\x34\xd8\xfb\xaf\x00\x00\x00\xff\xff\x0e\x7f\x78\xe1\x6b\x19\x00\x00") func goCentrifugeBuildConfigsDefault_configYamlBytes() ([]byte, error) { return bindataRead( @@ -84,7 +84,7 @@ func goCentrifugeBuildConfigsDefault_configYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "go-centrifuge/build/configs/default_config.yaml", size: 6506, mode: os.FileMode(420), modTime: time.Unix(1559721901, 0)} + info := bindataFileInfo{name: "go-centrifuge/build/configs/default_config.yaml", size: 6507, mode: os.FileMode(420), modTime: time.Unix(1563203119, 0)} a := &asset{bytes: bytes, info: info} return a, nil } @@ -104,7 +104,7 @@ func goCentrifugeBuildConfigsTesting_configYaml() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "go-centrifuge/build/configs/testing_config.yaml", size: 1076, mode: os.FileMode(420), modTime: time.Unix(1552484064, 0)} + info := bindataFileInfo{name: "go-centrifuge/build/configs/testing_config.yaml", size: 1076, mode: os.FileMode(420), modTime: time.Unix(1554467633, 0)} a := &asset{bytes: bytes, info: info} return a, nil } diff --git a/testingutils/commons/mock_did_identity.go b/testingutils/commons/mock_did_identity.go index 84f68d4bb..3a52d5079 100644 --- a/testingutils/commons/mock_did_identity.go +++ b/testingutils/commons/mock_did_identity.go @@ -39,15 +39,15 @@ func (i *MockIdentityService) GetKey(did identity.DID, key [32]byte) (*identity. } // RawExecute calls the execute method on the identity contract -func (i *MockIdentityService) RawExecute(ctx context.Context, to common.Address, data []byte, gasLimit uint64) (txID identity.IDTX, done chan bool, err error) { +func (i *MockIdentityService) RawExecute(ctx context.Context, to common.Address, data []byte, gasLimit uint64) (txID identity.IDTX, done chan error, err error) { args := i.Called(ctx, to, data) - return args.Get(0).(identity.IDTX), args.Get(1).(chan bool), args.Error(2) + return args.Get(0).(identity.IDTX), args.Get(1).(chan error), args.Error(2) } -// Execute creates the abi encoding an calls the execute method on the identity contract -func (i *MockIdentityService) Execute(ctx context.Context, to common.Address, contractAbi, methodName string, args ...interface{}) (txID identity.IDTX, done chan bool, err error) { +// Execute creates the abi encoding and calls the execute method on the identity contract +func (i *MockIdentityService) Execute(ctx context.Context, to common.Address, contractAbi, methodName string, args ...interface{}) (txID identity.IDTX, done chan error, err error) { a := i.Called(ctx, to, contractAbi, methodName, args) - return a.Get(0).(identity.IDTX), a.Get(1).(chan bool), a.Error(2) + return a.Get(0).(identity.IDTX), a.Get(1).(chan error), a.Error(2) } // AddMultiPurposeKey adds a key with multiple purposes diff --git a/testingutils/config/config.go b/testingutils/config/config.go index 1fda9b146..47704b63e 100644 --- a/testingutils/config/config.go +++ b/testingutils/config/config.go @@ -115,7 +115,7 @@ func (m *MockConfig) GetEthereumDefaultAccountName() string { } func (m *MockConfig) GetEthereumAccount(accountName string) (account *config.AccountConfig, err error) { - args := m.Called() + args := m.Called(accountName) return args.Get(0).(*config.AccountConfig), args.Error(1) } @@ -125,17 +125,17 @@ func (m *MockConfig) GetNetworkString() string { } func (m *MockConfig) GetNetworkKey(k string) string { - args := m.Called() + args := m.Called(k) return args.Get(0).(string) } func (m *MockConfig) GetContractAddressString(address string) string { - args := m.Called() + args := m.Called(address) return args.Get(0).(string) } func (m *MockConfig) GetContractAddress(contractName config.ContractName) common.Address { - args := m.Called() + args := m.Called(contractName) return args.Get(0).(common.Address) } @@ -193,8 +193,8 @@ func CreateTenantContextWithContext(t *testing.T, ctx context.Context, cfg confi } func HandlerContext(service config.Service) context.Context { - tcs, _ := service.GetAllAccounts() - cid, _ := tcs[0].GetIdentityID() + tcs, _ := service.GetAccounts() + cid := tcs[0].GetIdentityID() cidHex := hexutil.Encode(cid) ctx := context.WithValue(context.Background(), config.AccountHeaderKey, cidHex) return ctx diff --git a/testingutils/documents/documents.go b/testingutils/documents/documents.go index bce501fd3..5232d7d4f 100644 --- a/testingutils/documents/documents.go +++ b/testingutils/documents/documents.go @@ -78,6 +78,25 @@ func (m *MockService) UpdateModel(ctx context.Context, payload documents.UpdateP return model, jobID, args.Error(2) } +func (m *MockService) Update(ctx context.Context, model documents.Model) (documents.Model, jobs.JobID, chan error, error) { + args := m.Called(ctx, model) + model, _ = args.Get(0).(documents.Model) + jobID, _ := args.Get(1).(jobs.JobID) + return model, jobID, make(chan error), args.Error(2) +} + +func (m *MockService) Commit(ctx context.Context, doc documents.Model) (jobs.JobID, error) { + args := m.Called(ctx, doc) + jobID, _ := args.Get(0).(jobs.JobID) + return jobID, args.Error(1) +} + +func (m *MockService) Derive(ctx context.Context, payload documents.UpdatePayload) (documents.Model, error) { + args := m.Called(ctx, payload) + model, _ := args.Get(0).(documents.Model) + return model, args.Error(1) +} + type MockModel struct { documents.Model mock.Mock @@ -162,6 +181,40 @@ func (m *MockModel) GetAttributes() []documents.Attribute { return attrs } +func (m *MockModel) IsDIDCollaborator(did identity.DID) (bool, error) { + args := m.Called(did) + ok, _ := args.Get(0).(bool) + return ok, args.Error(1) +} + +func (m *MockModel) GetAccessTokens() ([]*coredocumentpb.AccessToken, error) { + args := m.Called() + ac, _ := args.Get(0).([]*coredocumentpb.AccessToken) + return ac, args.Error(1) +} + +func (m *MockModel) AttributeExists(key documents.AttrKey) bool { + args := m.Called(key) + return args.Bool(0) +} + +func (m *MockModel) GetAttribute(key documents.AttrKey) (documents.Attribute, error) { + args := m.Called(key) + attr, _ := args.Get(0).(documents.Attribute) + return attr, args.Error(1) +} + +func (m *MockModel) AddAttributes(ca documents.CollaboratorsAccess, prepareNewVersion bool, attrs ...documents.Attribute) error { + args := m.Called(ca, prepareNewVersion, attrs) + return args.Error(0) +} + +func (m *MockModel) GetStatus() documents.Status { + args := m.Called() + st, _ := args.Get(0).(documents.Status) + return st +} + type MockRegistry struct { mock.Mock } diff --git a/testingutils/documents/entity.go b/testingutils/documents/entity.go deleted file mode 100644 index fe0a4ab31..000000000 --- a/testingutils/documents/entity.go +++ /dev/null @@ -1,47 +0,0 @@ -// +build unit integration - -package testingdocuments - -import ( - "github.com/centrifuge/centrifuge-protobufs/gen/go/entity" - "github.com/centrifuge/go-centrifuge/identity" - cliententitypb "github.com/centrifuge/go-centrifuge/protobufs/gen/go/entity" - "github.com/centrifuge/go-centrifuge/testingutils/identity" -) - -func CreateEntityData() entitypb.Entity { - did, _ := identity.NewDIDFromString("0xed03Fa80291fF5DDC284DE6b51E716B130b05e20") - return entitypb.Entity{ - Identity: did.ToAddress().Bytes(), - LegalName: "Company Test", - Contacts: []*entitypb.Contact{{Name: "Satoshi Nakamoto"}}, - Addresses: []*entitypb.Address{{ - IsMain: true, - AddressLine1: "Sample Street 1", - Zip: "12345", - State: "Germany", - }, {IsMain: false, State: "US"}}, - } -} - -func CreateEntityPayload() *cliententitypb.EntityCreatePayload { - return &cliententitypb.EntityCreatePayload{ - Data: &cliententitypb.EntityData{ - Identity: "0xed03Fa80291fF5DDC284DE6b51E716B130b05e20", - LegalName: "Company Test", - Contacts: []*entitypb.Contact{{Name: "Satoshi Nakamoto"}}, - Addresses: []*entitypb.Address{ - { - IsMain: true, - AddressLine1: "Sample Street 1", - Zip: "12345", - State: "Germany", - }, - { - IsMain: false, State: "US", - }, - }, - }, - WriteAccess: []string{testingidentity.GenerateRandomDID().String()}, - } -} diff --git a/testingutils/documents/entityrelationship.go b/testingutils/documents/entityrelationship.go deleted file mode 100644 index 4da1e7a4f..000000000 --- a/testingutils/documents/entityrelationship.go +++ /dev/null @@ -1,29 +0,0 @@ -// +build unit integration - -package testingdocuments - -import ( - "github.com/centrifuge/centrifuge-protobufs/gen/go/entity" - "github.com/centrifuge/go-centrifuge/identity" - entitypb2 "github.com/centrifuge/go-centrifuge/protobufs/gen/go/entity" - "github.com/centrifuge/go-centrifuge/utils" - "github.com/ethereum/go-ethereum/common/hexutil" -) - -func CreateRelationship() *entitypb.EntityRelationship { - did, _ := identity.StringsToDIDs("0xed03Fa80291fF5DDC284DE6b51E716B130b05e20", "0x5F9132e0F92952abCb154A9b34563891ffe1AAcb") - return &entitypb.EntityRelationship{ - OwnerIdentity: did[0][:], - TargetIdentity: did[1][:], - } -} - -func CreateRelationshipPayload() *entitypb2.RelationshipPayload { - did2 := "0x5F9132e0F92952abCb154A9b34563891ffe1AAcb" - entityID := hexutil.Encode(utils.RandomSlice(32)) - - return &entitypb2.RelationshipPayload{ - TargetIdentity: did2, - DocumentId: entityID, - } -} diff --git a/testingutils/documents/invoice.go b/testingutils/documents/invoice.go deleted file mode 100644 index 726614d88..000000000 --- a/testingutils/documents/invoice.go +++ /dev/null @@ -1,34 +0,0 @@ -// +build integration unit testworld - -package testingdocuments - -import ( - "github.com/centrifuge/centrifuge-protobufs/gen/go/invoice" - clientinvoicepb "github.com/centrifuge/go-centrifuge/protobufs/gen/go/invoice" - "github.com/centrifuge/go-centrifuge/testingutils/identity" -) - -func CreateInvoiceData() invoicepb.InvoiceData { - recipient := testingidentity.GenerateRandomDID() - sender := testingidentity.GenerateRandomDID() - payee := testingidentity.GenerateRandomDID() - return invoicepb.InvoiceData{ - Recipient: recipient[:], - Sender: sender[:], - Payee: payee[:], - GrossAmount: []byte{0, 42, 0, 0, 0, 0, 0, 0, 0, 0}, - } -} - -func CreateInvoicePayload() *clientinvoicepb.InvoiceCreatePayload { - return &clientinvoicepb.InvoiceCreatePayload{ - Data: &clientinvoicepb.InvoiceData{ - Sender: "0xed03Fa80291fF5DDC284DE6b51E716B130b05e20", - Recipient: "0xEA939D5C0494b072c51565b191eE59B5D34fbf79", - Payee: "0x087D8ca6A16E6ce8d9fF55672E551A2828Ab8e8C", - GrossAmount: "42", - Currency: "EUR", - }, - WriteAccess: []string{testingidentity.GenerateRandomDID().String()}, - } -} diff --git a/testingutils/documents/purchaseorder.go b/testingutils/documents/purchaseorder.go deleted file mode 100644 index e7c80ec74..000000000 --- a/testingutils/documents/purchaseorder.go +++ /dev/null @@ -1,28 +0,0 @@ -// +build integration unit testworld - -package testingdocuments - -import ( - "github.com/centrifuge/centrifuge-protobufs/gen/go/purchaseorder" - clientpurchaseorderpb "github.com/centrifuge/go-centrifuge/protobufs/gen/go/purchaseorder" - "github.com/centrifuge/go-centrifuge/testingutils/identity" -) - -func CreatePOData() purchaseorderpb.PurchaseOrderData { - recipient := testingidentity.GenerateRandomDID() - return purchaseorderpb.PurchaseOrderData{ - Recipient: recipient[:], - TotalAmount: []byte{0, 42, 0, 0, 0, 0, 0, 0, 0, 0}, - } -} - -func CreatePOPayload() *clientpurchaseorderpb.PurchaseOrderCreatePayload { - return &clientpurchaseorderpb.PurchaseOrderCreatePayload{ - Data: &clientpurchaseorderpb.PurchaseOrderData{ - Recipient: "0xEA939D5C0494b072c51565b191eE59B5D34fbf79", - TotalAmount: "42", - Currency: "EUR", - }, - WriteAccess: []string{testingidentity.GenerateRandomDID().String()}, - } -} diff --git a/testingutils/nfts/mock_nft_service.go b/testingutils/nfts/mock_nft_service.go index 0da225447..34c08c04c 100644 --- a/testingutils/nfts/mock_nft_service.go +++ b/testingutils/nfts/mock_nft_service.go @@ -16,23 +16,17 @@ type MockNFTService struct { mock.Mock } -func (m *MockNFTService) MintNFT(ctx context.Context, request nft.MintNFTRequest) (*nft.TokenResponse, chan bool, error) { +func (m *MockNFTService) MintNFT(ctx context.Context, request nft.MintNFTRequest) (*nft.TokenResponse, chan error, error) { args := m.Called(ctx, request) resp, _ := args.Get(0).(*nft.TokenResponse) - done, _ := args.Get(1).(chan bool) + done, _ := args.Get(1).(chan error) return resp, done, args.Error(2) } -func (m *MockNFTService) GetRequiredInvoiceUnpaidProofFields(ctx context.Context) ([]string, error) { - args := m.Called(ctx) - resp, _ := args.Get(0).([]string) - return resp, args.Error(1) -} - -func (m *MockNFTService) TransferFrom(ctx context.Context, registry common.Address, to common.Address, tokenID nft.TokenID) (*nft.TokenResponse, chan bool, error) { +func (m *MockNFTService) TransferFrom(ctx context.Context, registry common.Address, to common.Address, tokenID nft.TokenID) (*nft.TokenResponse, chan error, error) { args := m.Called(ctx) resp, _ := args.Get(0).(*nft.TokenResponse) - done, _ := args.Get(1).(chan bool) + done, _ := args.Get(1).(chan error) return resp, done, args.Error(2) } diff --git a/testingutils/setup.go b/testingutils/setup.go index da70d6740..4c8a5cd59 100644 --- a/testingutils/setup.go +++ b/testingutils/setup.go @@ -8,6 +8,7 @@ import ( "os" "os/exec" "path" + "strings" "github.com/centrifuge/go-centrifuge/bootstrap" "github.com/centrifuge/go-centrifuge/config" @@ -56,6 +57,41 @@ func RunSmartContractMigrations() { log.Fatal(err, string(out)) } +func RunDAppSmartContractMigrations() { + var err error + var out []byte + projDir := GetProjectDir() + smAddr := GetSmartContractAddresses() + fmt.Println("Using AnchorAddr for DApp Contracts", smAddr.AnchorRepositoryAddr) + migrationScript := path.Join(projDir, "build", "scripts", "migrateDApp.sh") + cmd := exec.Command(migrationScript, smAddr.AnchorRepositoryAddr, projDir) + out, err = cmd.CombinedOutput() + if err != nil { + fmt.Println(err, string(out)) + return + } + return +} + +func GetDAppSmartContractAddresses() map[string]string { + projDir := GetProjectDir() + addresses := map[string]string{} + b, err := ioutil.ReadFile(path.Join(projDir, "localAddresses")) + if err != nil { + return addresses + } + f := strings.TrimSpace(string(b)) + elems := strings.Split(f, "\n") + for i := 0; i < len(elems); i++ { + addrEntry := strings.Split(elems[i], " ") + if len(addrEntry) < 2 { + return addresses + } + addresses[addrEntry[0]] = addrEntry[1] + } + return addresses +} + // GetSmartContractAddresses finds migrated smart contract addresses for localgeth func GetSmartContractAddresses() *config.SmartContractAddresses { iddat, err := findContractDeployJSON("IdentityFactory.json") @@ -158,6 +194,7 @@ func BuildIntegrationTestingContext() map[string]interface{} { projDir := GetProjectDir() StartPOAGeth() RunSmartContractMigrations() + RunDAppSmartContractMigrations() addresses := GetSmartContractAddresses() cfg := LoadTestConfig() cfg.Set("keys.p2p.publicKey", fmt.Sprintf("%s/build/resources/p2pKey.pub.pem", projDir)) diff --git a/testingutils/testingjobs/mockjob.go b/testingutils/testingjobs/mockjob.go index e52c3f4a8..acf652ea1 100644 --- a/testingutils/testingjobs/mockjob.go +++ b/testingutils/testingjobs/mockjob.go @@ -15,9 +15,9 @@ type MockJobManager struct { jobs.Manager } -func (m MockJobManager) ExecuteWithinJob(ctx context.Context, accountID identity.DID, existingTxID jobs.JobID, desc string, work func(accountID identity.DID, txID jobs.JobID, txMan jobs.Manager, err chan<- error)) (txID jobs.JobID, done chan bool, err error) { +func (m MockJobManager) ExecuteWithinJob(ctx context.Context, accountID identity.DID, existingTxID jobs.JobID, desc string, work func(accountID identity.DID, txID jobs.JobID, txMan jobs.Manager, err chan<- error)) (txID jobs.JobID, done chan error, err error) { args := m.Called(ctx, accountID, existingTxID, desc, work) - return args.Get(0).(jobs.JobID), args.Get(1).(chan bool), args.Error(2) + return args.Get(0).(jobs.JobID), args.Get(1).(chan error), args.Error(2) } func (m MockJobManager) GetJobStatus(account identity.DID, id jobs.JobID) (jobs.StatusResponse, error) { diff --git a/testworld/config_test.go b/testworld/config_test.go index 076a5a279..def4a4aba 100644 --- a/testworld/config_test.go +++ b/testworld/config_test.go @@ -4,6 +4,7 @@ package testworld import ( "net/http" + "strings" "testing" ) @@ -14,13 +15,13 @@ func TestConfig_Happy(t *testing.T) { // check charlies main account res := getAccount(charlie.httpExpect, charlie.id.String(), http.StatusOK, charlie.id.String()) accountID2 := res.Value("identity_id").String().NotEmpty() - accountID2.Equal(charlie.id.String()) + accountID2.Equal(strings.ToLower(charlie.id.String())) // check charlies all accounts res = getAllAccounts(charlie.httpExpect, charlie.id.String(), http.StatusOK) tenants := res.Value("data").Array() accIDs := getAccounts(tenants) - if _, ok := accIDs[charlie.id.String()]; !ok { + if _, ok := accIDs[strings.ToLower(charlie.id.String())]; !ok { t.Error("Charlies id needs to exist in the accounts list") } diff --git a/testworld/core_api_docs_test.go b/testworld/core_api_docs_test.go index 4ae9a6ed1..6721b1d93 100644 --- a/testworld/core_api_docs_test.go +++ b/testworld/core_api_docs_test.go @@ -6,6 +6,8 @@ import ( "net/http" "testing" + "github.com/centrifuge/go-centrifuge/documents" + "github.com/centrifuge/go-centrifuge/httpapi/coreapi" "github.com/centrifuge/go-centrifuge/utils" "github.com/ethereum/go-ethereum/common/hexutil" ) @@ -16,7 +18,7 @@ func TestCoreAPI_DocumentInvoiceCreateAndUpdate(t *testing.T) { charlie := doctorFord.getHostTestSuite(t, "Charlie") // Alice shares document with Bob first - res := createDocument(alice.httpExpect, alice.id.String(), "documents", http.StatusCreated, invoiceCoreAPICreate([]string{bob.id.String()})) + res := createDocument(alice.httpExpect, alice.id.String(), "documents", http.StatusAccepted, invoiceCoreAPICreate([]string{bob.id.String()})) txID := getTransactionID(t, res) status, message := getTransactionStatusAndMessage(alice.httpExpect, alice.id.String(), txID) if status != "success" { @@ -37,51 +39,7 @@ func TestCoreAPI_DocumentInvoiceCreateAndUpdate(t *testing.T) { nonExistingGenericDocumentCheck(charlie.httpExpect, charlie.id.String(), docIdentifier) // Bob updates invoice and shares with Charlie as well - res = updateCoreAPIDocument(bob.httpExpect, bob.id.String(), "documents", docIdentifier, http.StatusCreated, invoiceCoreAPIUpdate([]string{alice.id.String(), charlie.id.String()})) - txID = getTransactionID(t, res) - status, message = getTransactionStatusAndMessage(bob.httpExpect, bob.id.String(), txID) - if status != "success" { - t.Error(message) - } - - docIdentifier = getDocumentIdentifier(t, res) - if docIdentifier == "" { - t.Error("docIdentifier empty") - } - params["currency"] = "EUR" - getGenericDocumentAndCheck(t, alice.httpExpect, alice.id.String(), docIdentifier, params, allAttributes()) - getGenericDocumentAndCheck(t, bob.httpExpect, bob.id.String(), docIdentifier, params, allAttributes()) - getGenericDocumentAndCheck(t, charlie.httpExpect, charlie.id.String(), docIdentifier, params, allAttributes()) -} - -func TestCoreAPI_DocumentPOCreateAndUpdate(t *testing.T) { - alice := doctorFord.getHostTestSuite(t, "Alice") - bob := doctorFord.getHostTestSuite(t, "Bob") - charlie := doctorFord.getHostTestSuite(t, "Charlie") - - // Alice shares document with Bob first - res := createDocument(alice.httpExpect, alice.id.String(), "documents", http.StatusCreated, poCoreAPICreate([]string{bob.id.String()})) - txID := getTransactionID(t, res) - status, message := getTransactionStatusAndMessage(alice.httpExpect, alice.id.String(), txID) - if status != "success" { - t.Error(message) - } - - docIdentifier := getDocumentIdentifier(t, res) - if docIdentifier == "" { - t.Error("docIdentifier empty") - } - - params := map[string]interface{}{ - "currency": "EUR", - } - - getGenericDocumentAndCheck(t, alice.httpExpect, alice.id.String(), docIdentifier, params, createAttributes()) - getGenericDocumentAndCheck(t, bob.httpExpect, bob.id.String(), docIdentifier, params, createAttributes()) - nonExistingGenericDocumentCheck(charlie.httpExpect, charlie.id.String(), docIdentifier) - - // Bob updates purchase order and shares with Charlie as well - res = updateCoreAPIDocument(bob.httpExpect, bob.id.String(), "documents", docIdentifier, http.StatusCreated, poCoreAPIUpdate([]string{alice.id.String(), charlie.id.String()})) + res = updateCoreAPIDocument(bob.httpExpect, bob.id.String(), "documents", docIdentifier, http.StatusAccepted, invoiceCoreAPIUpdate([]string{alice.id.String(), charlie.id.String()})) txID = getTransactionID(t, res) status, message = getTransactionStatusAndMessage(bob.httpExpect, bob.id.String(), txID) if status != "success" { @@ -104,7 +62,7 @@ func TestCoreAPI_DocumentGenericCreateAndUpdate(t *testing.T) { charlie := doctorFord.getHostTestSuite(t, "Charlie") // Alice shares document with Bob first - res := createDocument(alice.httpExpect, alice.id.String(), "documents", http.StatusCreated, genericCoreAPICreate([]string{bob.id.String()})) + res := createDocument(alice.httpExpect, alice.id.String(), "documents", http.StatusAccepted, genericCoreAPICreate([]string{bob.id.String()})) txID := getTransactionID(t, res) status, message := getTransactionStatusAndMessage(alice.httpExpect, alice.id.String(), txID) if status != "success" { @@ -122,7 +80,7 @@ func TestCoreAPI_DocumentGenericCreateAndUpdate(t *testing.T) { nonExistingGenericDocumentCheck(charlie.httpExpect, charlie.id.String(), docIdentifier) // Bob updates purchase order and shares with Charlie as well - res = updateCoreAPIDocument(bob.httpExpect, bob.id.String(), "documents", docIdentifier, http.StatusCreated, genericCoreAPIUpdate([]string{alice.id.String(), charlie.id.String()})) + res = updateCoreAPIDocument(bob.httpExpect, bob.id.String(), "documents", docIdentifier, http.StatusAccepted, genericCoreAPIUpdate([]string{alice.id.String(), charlie.id.String()})) txID = getTransactionID(t, res) status, message = getTransactionStatusAndMessage(bob.httpExpect, bob.id.String(), txID) if status != "success" { @@ -144,7 +102,7 @@ func TestCoreAPI_DocumentEntityCreateAndUpdate(t *testing.T) { charlie := doctorFord.getHostTestSuite(t, "Charlie") // Alice shares document with Bob first - res := createDocument(alice.httpExpect, alice.id.String(), "documents", http.StatusCreated, entityCoreAPICreate(alice.id.String(), []string{bob.id.String(), charlie.id.String()})) + res := createDocument(alice.httpExpect, alice.id.String(), "documents", http.StatusAccepted, entityCoreAPICreate(alice.id.String(), []string{bob.id.String(), charlie.id.String()})) txID := getTransactionID(t, res) status, message := getTransactionStatusAndMessage(alice.httpExpect, alice.id.String(), txID) if status != "success" { @@ -198,63 +156,51 @@ func invoiceCoreAPIUpdate(collaborators []string) map[string]interface{} { return payload } -func poCoreAPICreate(collaborators []string) map[string]interface{} { - return map[string]interface{}{ - "scheme": "purchase_order", +func entityCoreAPICreate(identity string, collaborators []string) map[string]interface{} { + p := map[string]interface{}{ + "scheme": "entity", "write_access": collaborators, - "data": map[string]interface{}{ - "number": "12345", - "status": "unpaid", - "total_amount": "12.345", - "recipient": "0xBAEb33a61f05e6F269f1c4b4CFF91A901B54DaF7", - "date_sent": "2019-05-24T14:48:44.308854Z", // rfc3339nano - "date_confirmed": "2019-05-24T14:48:44Z", // rfc3339 - "currency": "EUR", - "attachments": []map[string]interface{}{ - { - "name": "test", - "file_type": "pdf", - "size": 1000202, - "data": "0xBAEb33a61f05e6F269f1c4b4CFF91A901B54DaF7", - "checksum": "0xBAEb33a61f05e6F269f1c4b4CFF91A901B54DaF3", + "attributes": createAttributes(), + } + + data := map[string]interface{}{ + "legal_name": "test company", + "contacts": []map[string]interface{}{ + { + "name": "test name", + }, + }, + + "payment_details": []map[string]interface{}{ + { + "predefined": true, + "bank_payment_method": map[string]interface{}{ + "identifier": hexutil.Encode(utils.RandomSlice(32)), + "holder_name": "John Doe", }, }, }, - "attributes": createAttributes(), } -} -func poCoreAPIUpdate(collaborators []string) map[string]interface{} { - payload := poCoreAPICreate(collaborators) - payload["attributes"] = updateAttributes() - return payload + if identity != "" { + data["identity"] = identity + } + + p["data"] = data + return p } -func entityCoreAPICreate(identity string, collaborators []string) map[string]interface{} { - return map[string]interface{}{ +func entityCoreAPIUpdate(collabs []string) map[string]interface{} { + p := map[string]interface{}{ "scheme": "entity", - "write_access": collaborators, + "write_access": collabs, "data": map[string]interface{}{ - "identity": identity, - "legal_name": "test company", - "contacts": []map[string]interface{}{ - { - "name": "test name", - }, - }, - - "payment_details": []map[string]interface{}{ - { - "predefined": true, - "bank_payment_method": map[string]interface{}{ - "identifier": hexutil.Encode(utils.RandomSlice(32)), - "holder_name": "John Doe", - }, - }, - }, + "legal_name": "updated company", }, - "attributes": createAttributes(), + "attributes": updateAttributes(), } + + return p } func genericCoreAPICreate(collaborators []string) map[string]interface{} { @@ -272,25 +218,33 @@ func genericCoreAPIUpdate(collaborators []string) map[string]interface{} { return payload } -func createAttributes() map[string]map[string]string { - return map[string]map[string]string{ - "string_test": { - "type": "string", - "value": "hello, world", +func createAttributes() coreapi.AttributeMapRequest { + dec, _ := documents.NewDecimal("100001.002") + return coreapi.AttributeMapRequest{ + "string_test": coreapi.AttributeRequest{ + Type: "string", + Value: "hello, world", + }, + "monetary_test": coreapi.AttributeRequest{ + Type: "monetary", + MonetaryValue: &coreapi.MonetaryValue{ + Value: dec, + ID: "USD", + }, }, } } -func updateAttributes() map[string]map[string]string { - return map[string]map[string]string{ - "decimal_test": { - "type": "decimal", - "value": "100.001", +func updateAttributes() coreapi.AttributeMapRequest { + return coreapi.AttributeMapRequest{ + "decimal_test": coreapi.AttributeRequest{ + Type: "decimal", + Value: "100.001", }, } } -func allAttributes() map[string]map[string]string { +func allAttributes() coreapi.AttributeMapRequest { attrs := createAttributes() for k, v := range updateAttributes() { attrs[k] = v diff --git a/testworld/document_consensus_test.go b/testworld/document_consensus_test.go index 29cfb910e..6cb8e7b9a 100644 --- a/testworld/document_consensus_test.go +++ b/testworld/document_consensus_test.go @@ -34,11 +34,6 @@ func TestHost_AddExternalCollaborator(t *testing.T) { typeInvoice, multiHostMultiAccount, }, - { - "PO_AddExternalCollaborator", - typePO, - multiHost, - }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { @@ -62,7 +57,7 @@ func addExternalCollaborator_withinHost(t *testing.T, documentType string) { c := accounts[2] // a shares document with b first - res := createDocument(bob.httpExpect, a, documentType, http.StatusOK, defaultDocumentPayload(documentType, []string{b})) + res := createDocument(bob.httpExpect, a, documentType, http.StatusAccepted, defaultDocumentPayload(documentType, []string{b})) txID := getTransactionID(t, res) status, message := getTransactionStatusAndMessage(bob.httpExpect, a, txID) if status != "success" { @@ -88,12 +83,12 @@ func addExternalCollaborator_withinHost(t *testing.T, documentType string) { // account b sends a webhook for received anchored doc msg, err = doctorFord.maeve.getReceivedMsg(b, int(notification.ReceivedPayload), docIdentifier) assert.NoError(t, err) - assert.Equal(t, strings.ToLower(a), strings.ToLower(msg.FromId)) + assert.Equal(t, strings.ToLower(a), strings.ToLower(msg.FromID)) log.Debug("Host test success") nonExistingDocumentCheck(bob.httpExpect, c, documentType, params) // b updates invoice and shares with c as well - res = updateDocument(bob.httpExpect, b, documentType, http.StatusOK, docIdentifier, updatedDocumentPayload(documentType, []string{a, c})) + res = updateDocument(bob.httpExpect, b, documentType, http.StatusAccepted, docIdentifier, updatedDocumentPayload(documentType, []string{a, c})) txID = getTransactionID(t, res) status, message = getTransactionStatusAndMessage(bob.httpExpect, b, txID) if status != "success" { @@ -111,7 +106,7 @@ func addExternalCollaborator_withinHost(t *testing.T, documentType string) { // account c sends a webhook for received anchored doc msg, err = doctorFord.maeve.getReceivedMsg(c, int(notification.ReceivedPayload), docIdentifier) assert.NoError(t, err) - assert.Equal(t, strings.ToLower(b), strings.ToLower(msg.FromId)) + assert.Equal(t, strings.ToLower(b), strings.ToLower(msg.FromID)) } func addExternalCollaborator_multiHostMultiAccount(t *testing.T, documentType string) { @@ -128,7 +123,7 @@ func addExternalCollaborator_multiHostMultiAccount(t *testing.T, documentType st f := accounts2[2] // Alice shares document with Bobs accounts a and b - res := createDocument(alice.httpExpect, alice.id.String(), documentType, http.StatusOK, defaultDocumentPayload(documentType, []string{a, b})) + res := createDocument(alice.httpExpect, alice.id.String(), documentType, http.StatusAccepted, defaultDocumentPayload(documentType, []string{a, b})) txID := getTransactionID(t, res) status, message := getTransactionStatusAndMessage(alice.httpExpect, alice.id.String(), txID) if status != "success" { @@ -155,11 +150,11 @@ func addExternalCollaborator_multiHostMultiAccount(t *testing.T, documentType st // bobs account b sends a webhook for received anchored doc msg, err = doctorFord.maeve.getReceivedMsg(b, int(notification.ReceivedPayload), docIdentifier) assert.NoError(t, err) - assert.Equal(t, strings.ToLower(alice.id.String()), strings.ToLower(msg.FromId)) + assert.Equal(t, strings.ToLower(alice.id.String()), strings.ToLower(msg.FromID)) nonExistingDocumentCheck(bob.httpExpect, c, documentType, params) // Bob updates invoice and shares with bobs account c as well using account a and to accounts d and e of Charlie - res = updateDocument(bob.httpExpect, a, documentType, http.StatusOK, docIdentifier, updatedDocumentPayload(documentType, []string{alice.id.String(), b, c, d, e})) + res = updateDocument(bob.httpExpect, a, documentType, http.StatusAccepted, docIdentifier, updatedDocumentPayload(documentType, []string{alice.id.String(), b, c, d, e})) txID = getTransactionID(t, res) status, message = getTransactionStatusAndMessage(bob.httpExpect, a, txID) if status != "success" { @@ -187,7 +182,7 @@ func addExternalCollaborator(t *testing.T, documentType string) { charlie := doctorFord.getHostTestSuite(t, "Charlie") // Alice shares document with Bob first - res := createDocument(alice.httpExpect, alice.id.String(), documentType, http.StatusOK, defaultDocumentPayload(documentType, []string{bob.id.String()})) + res := createDocument(alice.httpExpect, alice.id.String(), documentType, http.StatusAccepted, defaultDocumentPayload(documentType, []string{bob.id.String()})) txID := getTransactionID(t, res) status, message := getTransactionStatusAndMessage(alice.httpExpect, alice.id.String(), txID) if status != "success" { @@ -208,7 +203,7 @@ func addExternalCollaborator(t *testing.T, documentType string) { nonExistingDocumentCheck(charlie.httpExpect, charlie.id.String(), documentType, params) // Bob updates invoice and shares with Charlie as well - res = updateDocument(bob.httpExpect, bob.id.String(), documentType, http.StatusOK, docIdentifier, updatedDocumentPayload(documentType, []string{alice.id.String(), charlie.id.String()})) + res = updateDocument(bob.httpExpect, bob.id.String(), documentType, http.StatusAccepted, docIdentifier, updatedDocumentPayload(documentType, []string{alice.id.String(), charlie.id.String()})) txID = getTransactionID(t, res) status, message = getTransactionStatusAndMessage(bob.httpExpect, bob.id.String(), txID) if status != "success" { @@ -232,7 +227,6 @@ func TestHost_CollaboratorTimeOut(t *testing.T) { //currently can't be run in parallel (because of node kill) collaboratorTimeOut(t, typeInvoice) - collaboratorTimeOut(t, typePO) } func collaboratorTimeOut(t *testing.T, documentType string) { @@ -240,7 +234,7 @@ func collaboratorTimeOut(t *testing.T, documentType string) { bob := doctorFord.getHostTestSuite(t, "Bob") // Kenny shares a document with Bob - response := createDocument(kenny.httpExpect, kenny.id.String(), documentType, http.StatusOK, defaultInvoicePayload([]string{bob.id.String()})) + response := createDocument(kenny.httpExpect, kenny.id.String(), documentType, http.StatusAccepted, defaultInvoicePayload([]string{bob.id.String()})) txID := getTransactionID(t, response) status, message := getTransactionStatusAndMessage(kenny.httpExpect, kenny.id.String(), txID) if status != "success" { @@ -263,7 +257,7 @@ func collaboratorTimeOut(t *testing.T, documentType string) { updatedPayload := updatedDocumentPayload(documentType, []string{kenny.id.String()}) // Bob will anchor the document without Kennys signature - response = updateDocument(bob.httpExpect, bob.id.String(), documentType, http.StatusOK, docIdentifier, updatedPayload) + response = updateDocument(bob.httpExpect, bob.id.String(), documentType, http.StatusAccepted, docIdentifier, updatedPayload) txID = getTransactionID(t, response) status, message = getTransactionStatusAndMessage(bob.httpExpect, bob.id.String(), txID) if status != "failed" { @@ -290,7 +284,78 @@ func TestDocument_invalidAttributes(t *testing.T) { bob := doctorFord.getHostTestSuite(t, "Bob") // Kenny shares a document with Bob - response := createDocument(kenny.httpExpect, kenny.id.String(), typeInvoice, http.StatusInternalServerError, wrongInvoicePayload([]string{bob.id.String()})) - errMsg := response.Raw()["error"].(string) - assert.Contains(t, errMsg, "model attribute error") + response := createDocument(kenny.httpExpect, kenny.id.String(), typeInvoice, http.StatusBadRequest, wrongInvoicePayload([]string{bob.id.String()})) + + errMsg := response.Raw()["message"].(string) + assert.Contains(t, errMsg, "some invalid time stamp\" as \"2006-01-02T15:04:05.999999999Z07:00\": cannot parse \"some invalid ti") +} + +func TestDocument_latestDocumentVersion(t *testing.T) { + alice := doctorFord.getHostTestSuite(t, "Alice") + bob := doctorFord.getHostTestSuite(t, "Bob") + charlie := doctorFord.getHostTestSuite(t, "Charlie") + kenny := doctorFord.getHostTestSuite(t, "Kenny") + documentType := typeInvoice + + // alice creates a document with bob and kenny + res := createDocument(alice.httpExpect, alice.id.String(), documentType, http.StatusAccepted, defaultDocumentPayload(documentType, []string{bob.id.String(), kenny.id.String()})) + txID := getTransactionID(t, res) + status, message := getTransactionStatusAndMessage(alice.httpExpect, alice.id.String(), txID) + if status != "success" { + t.Error(message) + } + + docIdentifier := getDocumentIdentifier(t, res) + versionID := getDocumentCurrentVersion(t, res) + if versionID != docIdentifier { + t.Errorf("docID(%s) != versionID(%s)\n", docIdentifier, versionID) + } + + params := map[string]interface{}{ + "document_id": docIdentifier, + "version_id": versionID, + "currency": "USD", + } + getDocumentAndCheck(t, alice.httpExpect, alice.id.String(), documentType, params, true) + getDocumentAndCheck(t, bob.httpExpect, bob.id.String(), documentType, params, true) + getDocumentAndCheck(t, kenny.httpExpect, kenny.id.String(), documentType, params, true) + nonExistingDocumentCheck(charlie.httpExpect, charlie.id.String(), documentType, params) + + // Bob updates invoice and shares with Charlie as well but kenny is offline and miss the update + kenny.host.kill() + res = updateDocument(bob.httpExpect, bob.id.String(), documentType, http.StatusAccepted, docIdentifier, updatedDocumentPayload(documentType, []string{charlie.id.String()})) + txID = getTransactionID(t, res) + status, message = getTransactionStatusAndMessage(bob.httpExpect, bob.id.String(), txID) + if status != "failed" { + t.Error(message) + } + + docIdentifier = getDocumentIdentifier(t, res) + versionID = getDocumentCurrentVersion(t, res) + params["currency"] = "EUR" + params["version_id"] = versionID + getDocumentAndCheck(t, alice.httpExpect, alice.id.String(), documentType, params, true) + getDocumentAndCheck(t, bob.httpExpect, bob.id.String(), documentType, params, true) + getDocumentAndCheck(t, charlie.httpExpect, charlie.id.String(), documentType, params, true) + // bring kenny back and should not have the latest version + doctorFord.reLive(t, kenny.name) + nonExistingDocumentVersionCheck(kenny.httpExpect, kenny.id.String(), documentType, params) + + // alice updates document + res = updateDocument(alice.httpExpect, alice.id.String(), documentType, http.StatusAccepted, docIdentifier, updatedDocumentPayload(documentType, nil)) + txID = getTransactionID(t, res) + status, message = getTransactionStatusAndMessage(alice.httpExpect, alice.id.String(), txID) + if status != "success" { + t.Error(message) + } + + docIdentifier = getDocumentIdentifier(t, res) + versionID = getDocumentCurrentVersion(t, res) + params["version_id"] = versionID + + // everyone should have the latest version + getDocumentAndCheck(t, alice.httpExpect, alice.id.String(), documentType, params, true) + getDocumentAndCheck(t, bob.httpExpect, bob.id.String(), documentType, params, true) + getDocumentAndCheck(t, charlie.httpExpect, charlie.id.String(), documentType, params, true) + getDocumentAndCheck(t, kenny.httpExpect, kenny.id.String(), documentType, params, true) } diff --git a/testworld/entity_relationships_test.go b/testworld/entity_relationships_test.go index 30421dcf2..0d00cd3c0 100644 --- a/testworld/entity_relationships_test.go +++ b/testworld/entity_relationships_test.go @@ -19,7 +19,7 @@ func TestHost_Entity_EntityRelationships(t *testing.T) { charlie := doctorFord.getHostTestSuite(t, "Charlie") // Alice anchors entity - res := createDocument(alice.httpExpect, alice.id.String(), typeEntity, http.StatusOK, defaultEntityPayload(alice.id.String(), []string{})) + res := createDocument(alice.httpExpect, alice.id.String(), typeEntity, http.StatusAccepted, defaultEntityPayload(alice.id.String(), []string{})) entityIdentifier := getDocumentIdentifier(t, res) txID := getTransactionID(t, res) status, message := getTransactionStatusAndMessage(alice.httpExpect, alice.id.String(), txID) @@ -28,7 +28,7 @@ func TestHost_Entity_EntityRelationships(t *testing.T) { } // Alice creates an EntityRelationship with Bob - resB := shareEntity(alice.httpExpect, alice.id.String(), entityIdentifier, http.StatusOK, defaultRelationshipPayload(entityIdentifier, bob.id.String())) + resB := shareEntity(alice.httpExpect, alice.id.String(), entityIdentifier, http.StatusAccepted, defaultRelationshipPayload(entityIdentifier, bob.id.String())) relationshipIdentifierB := getDocumentIdentifier(t, resB) txID = getTransactionID(t, resB) status, message = getTransactionStatusAndMessage(alice.httpExpect, alice.id.String(), txID) @@ -47,7 +47,7 @@ func TestHost_Entity_EntityRelationships(t *testing.T) { response.Path("$.data.entity.legal_name").String().Equal("test company") // Alice updates her entity - res = updateDocument(alice.httpExpect, alice.id.String(), typeEntity, http.StatusOK, entityIdentifier, updatedEntityPayload(alice.id.String(), []string{})) + res = updateDocument(alice.httpExpect, alice.id.String(), typeEntity, http.StatusAccepted, entityIdentifier, updatedEntityPayload(alice.id.String(), []string{})) txID = getTransactionID(t, res) status, message = getTransactionStatusAndMessage(alice.httpExpect, alice.id.String(), txID) if status != "success" { @@ -61,10 +61,10 @@ func TestHost_Entity_EntityRelationships(t *testing.T) { // Alice wants to list all relationships associated with her entity, this should return her one (with Bob) response = getEntity(alice.httpExpect, alice.id.String(), entityIdentifier) response.Path("$.data.relationships[0].active").Boolean().Equal(true) - response.Path("$.data.relationships[0].identity").String().Equal(bob.id.String()) + response.Path("$.data.relationships[0].target_identity").String().Equal(bob.id.String()) // Alice creates an EntityRelationship with Charlie - resC := shareEntity(alice.httpExpect, alice.id.String(), entityIdentifier, http.StatusOK, defaultRelationshipPayload(entityIdentifier, charlie.id.String())) + resC := shareEntity(alice.httpExpect, alice.id.String(), entityIdentifier, http.StatusAccepted, defaultRelationshipPayload(entityIdentifier, charlie.id.String())) relationshipIdentifierC := getDocumentIdentifier(t, resC) txID = getTransactionID(t, resC) status, message = getTransactionStatusAndMessage(alice.httpExpect, alice.id.String(), txID) @@ -86,7 +86,7 @@ func TestHost_Entity_EntityRelationships(t *testing.T) { response.Path("$.data.relationships[" + bIdx + "].active").Boolean().Equal(true) // Alice revokes the EntityRelationship with Bob - resB = revokeEntity(alice.httpExpect, alice.id.String(), entityIdentifier, http.StatusOK, defaultRelationshipPayload(entityIdentifier, bob.id.String())) + resB = revokeEntity(alice.httpExpect, alice.id.String(), entityIdentifier, http.StatusAccepted, defaultRelationshipPayload(entityIdentifier, bob.id.String())) txID = getTransactionID(t, resB) status, message = getTransactionStatusAndMessage(alice.httpExpect, alice.id.String(), txID) if status != "success" { @@ -107,7 +107,7 @@ func TestHost_Entity_EntityRelationships(t *testing.T) { func checkRelationships(response *httpexpect.Value, charlieDID, bobDID string) (string, string) { response.Path("$.data.relationships").Array().Length().Equal(2) - firstR := response.Path("$.data.relationships[0].identity").String().Raw() + firstR := response.Path("$.data.relationships[0].target_identity").String().Raw() charlieIdx := 0 if firstR != charlieDID { charlieIdx = 1 @@ -115,8 +115,8 @@ func checkRelationships(response *httpexpect.Value, charlieDID, bobDID string) ( } bIdx := strconv.Itoa(1 - charlieIdx) cIdx := strconv.Itoa(charlieIdx) - response.Path("$.data.relationships[" + cIdx + "].identity").String().Equal(charlieDID) - response.Path("$.data.relationships[" + bIdx + "].identity").String().Equal(bobDID) + response.Path("$.data.relationships[" + cIdx + "].target_identity").String().Equal(charlieDID) + response.Path("$.data.relationships[" + bIdx + "].target_identity").String().Equal(bobDID) return cIdx, bIdx } diff --git a/testworld/entity_test.go b/testworld/entity_test.go index 1abed27a1..59fc5918e 100644 --- a/testworld/entity_test.go +++ b/testworld/entity_test.go @@ -6,12 +6,6 @@ import ( "fmt" "net/http" "testing" - - "github.com/centrifuge/go-centrifuge/documents/entityrelationship" - entitypb2 "github.com/centrifuge/go-centrifuge/protobufs/gen/go/entity" - "github.com/centrifuge/go-centrifuge/testingutils/config" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/stretchr/testify/assert" ) func TestHost_BasicEntity(t *testing.T) { @@ -23,7 +17,7 @@ func TestHost_BasicEntity(t *testing.T) { charlie := doctorFord.getHostTestSuite(t, "Charlie") // Alice shares a document with Bob and Charlie - res := createDocument(alice.httpExpect, alice.id.String(), typeEntity, http.StatusOK, defaultEntityPayload(alice.id.String(), []string{bob.id.String(), charlie.id.String()})) + res := createDocument(alice.httpExpect, alice.id.String(), typeEntity, http.StatusAccepted, defaultEntityPayload(alice.id.String(), []string{bob.id.String(), charlie.id.String()})) docIdentifier := getDocumentIdentifier(t, res) txID := getTransactionID(t, res) status, message := getTransactionStatusAndMessage(alice.httpExpect, alice.id.String(), txID) @@ -49,7 +43,7 @@ func TestHost_EntityShareGet(t *testing.T) { bob := doctorFord.getHostTestSuite(t, "Bob") // Alice anchors Entity - res := createDocument(alice.httpExpect, alice.id.String(), typeEntity, http.StatusOK, defaultEntityPayload(alice.id.String(), []string{})) + res := createDocument(alice.httpExpect, alice.id.String(), typeEntity, http.StatusAccepted, defaultEntityPayload(alice.id.String(), []string{})) txID := getTransactionID(t, res) status, message := getTransactionStatusAndMessage(alice.httpExpect, alice.id.String(), txID) if status != "success" { @@ -58,27 +52,15 @@ func TestHost_EntityShareGet(t *testing.T) { entityIdentifier := getDocumentIdentifier(t, res) // Alice creates an EntityRelationship with Bob - ctxAlice := testingconfig.CreateAccountContext(t, alice.host.config) - relationshipData := &entitypb2.RelationshipData{ - EntityIdentifier: entityIdentifier, - OwnerIdentity: alice.id.String(), - TargetIdentity: bob.id.String(), + resB := shareEntity(alice.httpExpect, alice.id.String(), entityIdentifier, http.StatusAccepted, defaultRelationshipPayload(entityIdentifier, bob.id.String())) + relationshipIdentifier := getDocumentIdentifier(t, resB) + txID = getTransactionID(t, resB) + status, message = getTransactionStatusAndMessage(alice.httpExpect, alice.id.String(), txID) + if status != "success" { + t.Error(message) } - - relationship := entityrelationship.EntityRelationship{} - err := relationship.InitEntityRelationshipInput(ctxAlice, entityIdentifier, relationshipData) - assert.NoError(t, err) - - relationshipModel, _, isDone, err := alice.host.entityService.Share(ctxAlice, &relationship) - assert.NoError(t, err) - done := <-isDone - assert.True(t, done) - cd, err := relationshipModel.PackCoreDocument() - assert.NoError(t, err) - - relationshipIdentifier := cd.DocumentIdentifier params := map[string]interface{}{ - "r_identifier": hexutil.Encode(relationshipIdentifier), + "r_identifier": relationshipIdentifier, } response := getEntityWithRelation(bob.httpExpect, bob.id.String(), typeEntity, params) response.Path("$.data.entity.legal_name").String().Equal("test company") diff --git a/testworld/funding_test.go b/testworld/funding_test.go index ea35ab1bd..2f8c77ed7 100644 --- a/testworld/funding_test.go +++ b/testworld/funding_test.go @@ -31,7 +31,7 @@ func Test_SignUpdate(t *testing.T) { func updateTest(t *testing.T, alice, bob, charlie hostTestSuite, fundingId, docIdentifier string) { // alice adds a funding and shares with charlie - res := updateFunding(alice.httpExpect, alice.id.String(), fundingId, http.StatusOK, docIdentifier, updateFundingPayload(fundingId, alice.id.String(), charlie.id.String())) + res := updateFunding(alice.httpExpect, alice.id.String(), fundingId, http.StatusAccepted, docIdentifier, updateFundingPayload(fundingId, alice.id.String(), charlie.id.String())) txID := getTransactionID(t, res) status, message := getTransactionStatusAndMessage(alice.httpExpect, alice.id.String(), txID) if status != "success" { @@ -56,7 +56,7 @@ func updateTest(t *testing.T, alice, bob, charlie hostTestSuite, fundingId, docI func listTest(t *testing.T, alice, bob, charlie hostTestSuite, docIdentifier string) { var fundings []string for i := 0; i < 5; i++ { - res := createFunding(alice.httpExpect, alice.id.String(), docIdentifier, http.StatusOK, defaultFundingPayload(alice.id.String(), bob.id.String())) + res := createFunding(alice.httpExpect, alice.id.String(), docIdentifier, http.StatusAccepted, defaultFundingPayload(alice.id.String(), bob.id.String())) txID := getTransactionID(t, res) status, message := getTransactionStatusAndMessage(alice.httpExpect, alice.id.String(), txID) if status != "success" { @@ -81,7 +81,7 @@ func listTest(t *testing.T, alice, bob, charlie hostTestSuite, docIdentifier str func signTest(t *testing.T, alice, bob, charlie hostTestSuite, fundingId, docIdentifier string) { // alice adds a funding and shares with charlie - res := signFunding(alice.httpExpect, alice.id.String(), docIdentifier, fundingId, http.StatusOK) + res := signFunding(alice.httpExpect, alice.id.String(), docIdentifier, fundingId, http.StatusAccepted) txID := getTransactionID(t, res) status, message := getTransactionStatusAndMessage(alice.httpExpect, alice.id.String(), txID) if status != "success" { @@ -104,7 +104,7 @@ func signTest(t *testing.T, alice, bob, charlie hostTestSuite, fundingId, docIde } func createInvoiceWithFunding(t *testing.T, alice, bob, charlie hostTestSuite) (agreementId, docIdentifier string) { - res := createDocument(alice.httpExpect, alice.id.String(), typeInvoice, http.StatusOK, defaultInvoicePayload([]string{bob.id.String()})) + res := createDocument(alice.httpExpect, alice.id.String(), typeInvoice, http.StatusAccepted, defaultInvoicePayload([]string{bob.id.String()})) txID := getTransactionID(t, res) status, message := getTransactionStatusAndMessage(alice.httpExpect, alice.id.String(), txID) if status != "success" { @@ -121,7 +121,7 @@ func createInvoiceWithFunding(t *testing.T, alice, bob, charlie hostTestSuite) ( getDocumentAndCheck(t, bob.httpExpect, bob.id.String(), typeInvoice, params, true) // alice adds a funding and shares with charlie - res = createFunding(alice.httpExpect, alice.id.String(), docIdentifier, http.StatusOK, defaultFundingPayload(alice.id.String(), charlie.id.String())) + res = createFunding(alice.httpExpect, alice.id.String(), docIdentifier, http.StatusAccepted, defaultFundingPayload(alice.id.String(), charlie.id.String())) txID = getTransactionID(t, res) status, message = getTransactionStatusAndMessage(alice.httpExpect, alice.id.String(), txID) if status != "success" { diff --git a/testworld/httputils.go b/testworld/httputils.go index 19009ebad..f1a897c1d 100644 --- a/testworld/httputils.go +++ b/testworld/httputils.go @@ -4,18 +4,19 @@ package testworld import ( "crypto/tls" + "encoding/json" "net/http" "os" "testing" "time" + "github.com/centrifuge/go-centrifuge/httpapi/coreapi" "github.com/gavv/httpexpect" "github.com/stretchr/testify/assert" ) const typeInvoice string = "invoices" const typeEntity string = "entities" -const typePO string = "purchase_orders" var isRunningOnCI = len(os.Getenv("TRAVIS")) != 0 @@ -112,6 +113,9 @@ func getDocumentAndCheck(t *testing.T, e *httpexpect.Expect, auth string, docume Expect().Status(http.StatusOK).JSON().NotNull() objGet.Path("$.header.document_id").String().Equal(docIdentifier) objGet.Path("$.data.currency").String().Equal(params["currency"].(string)) + if versionID, ok := params["version_id"]; ok { + objGet.Path("$.header.version_id").String().Equal(versionID.(string)) + } if checkattrs { attrs := objGet.Path("$.attributes").Object().Raw() eattrs := defaultAttributePayload() @@ -164,7 +168,7 @@ func nonexistentEntityWithRelation(e *httpexpect.Expect, auth string, documentTy relationshipIdentifier := params["r_identifier"].(string) objGet := addCommonHeaders(e.GET("/v1/relationships/"+relationshipIdentifier+"/entity"), auth). - Expect().Status(500).JSON().NotNull() + Expect().Status(http.StatusNotFound).JSON().NotNull() return objGet } @@ -180,7 +184,16 @@ func nonExistingDocumentCheck(e *httpexpect.Expect, auth string, documentType st docIdentifier := params["document_id"].(string) objGet := addCommonHeaders(e.GET("/v1/"+documentType+"/"+docIdentifier), auth). - Expect().Status(500).JSON().NotNull() + Expect().Status(http.StatusNotFound).JSON().NotNull() + return objGet +} + +func nonExistingDocumentVersionCheck(e *httpexpect.Expect, auth string, documentType string, params map[string]interface{}) *httpexpect.Value { + docIdentifier := params["document_id"].(string) + versionID := params["version_id"].(string) + + objGet := addCommonHeaders(e.GET("/v1/"+documentType+"/"+docIdentifier+"/versions/"+versionID), auth). + Expect().Status(http.StatusNotFound).JSON().NotNull() return objGet } @@ -191,6 +204,32 @@ func createDocument(e *httpexpect.Expect, auth string, documentType string, stat return obj } +func createDocumentV2(e *httpexpect.Expect, auth string, documentType string, status int, payload map[string]interface{}) *httpexpect.Object { + obj := addCommonHeaders(e.POST("/v2/"+documentType), auth). + WithJSON(payload). + Expect().Status(status).JSON().Object() + return obj +} + +func updateDocumentV2(e *httpexpect.Expect, auth string, documentType string, status int, payload map[string]interface{}) *httpexpect.Object { + obj := addCommonHeaders(e.PATCH("/v2/"+documentType+"/"+payload["document_id"].(string)), auth). + WithJSON(payload). + Expect().Status(status).JSON().Object() + return obj +} + +func checkDocumentParams(obj *httpexpect.Object, params map[string]string) { + for k, v := range params { + obj.Path("$.data." + k).String().Equal(v) + } +} + +func commitDocument(e *httpexpect.Expect, auth string, documentType string, status int, docIdentifier string) *httpexpect.Object { + obj := addCommonHeaders(e.POST("/v2/"+documentType+"/"+docIdentifier+"/commit"), auth). + Expect().Status(status).JSON().Object() + return obj +} + func updateCoreAPIDocument(e *httpexpect.Expect, auth string, documentType string, docID string, status int, payload map[string]interface{}) *httpexpect.Object { obj := addCommonHeaders(e.PUT("/v1/"+documentType+"/"+docID), auth). WithJSON(payload). @@ -248,12 +287,14 @@ func updateDocument(e *httpexpect.Expect, auth string, documentType string, stat func getDocumentIdentifier(t *testing.T, response *httpexpect.Object) string { docIdentifier := response.Value("header").Path("$.document_id").String().NotEmpty().Raw() - if docIdentifier == "" { - t.Error("docIdentifier empty") - } return docIdentifier } +func getDocumentStatus(t *testing.T, response *httpexpect.Object) string { + status := response.Value("header").Path("$.status").String().NotEmpty().Raw() + return status +} + func getAgreementId(t *testing.T, response *httpexpect.Object) string { agreementID := response.Value("data").Path("$.funding.agreement_id").String().NotEmpty().Raw() if agreementID == "" { @@ -280,7 +321,7 @@ func getTransactionID(t *testing.T, resp *httpexpect.Object) string { } func getDocumentCurrentVersion(t *testing.T, resp *httpexpect.Object) string { - versionID := resp.Value("header").Path("$.version").String().Raw() + versionID := resp.Value("header").Path("$.version_id").String().Raw() if versionID == "" { t.Error("version ID empty") } @@ -401,7 +442,7 @@ func getAccounts(accounts *httpexpect.Array) map[string]string { return accIDs } -func getGenericDocumentAndCheck(t *testing.T, e *httpexpect.Expect, auth string, documentID string, params map[string]interface{}, attrs map[string]map[string]string) *httpexpect.Value { +func getGenericDocumentAndCheck(t *testing.T, e *httpexpect.Expect, auth string, documentID string, params map[string]interface{}, attrs coreapi.AttributeMapRequest) *httpexpect.Value { objGet := addCommonHeaders(e.GET("/v1/documents/"+documentID), auth). Expect().Status(http.StatusOK).JSON().NotNull() objGet.Path("$.header.document_id").String().Equal(documentID) @@ -410,20 +451,29 @@ func getGenericDocumentAndCheck(t *testing.T, e *httpexpect.Expect, auth string, } if len(attrs) > 0 { - gattrs := objGet.Path("$.attributes").Object().Raw() - cattrs := make(map[string]map[string]string) - for k, v := range gattrs { - atr := v.(map[string]interface{}) - delete(atr, "key") - atri := make(map[string]string) - for k1, v1 := range atr { - atri[k1] = v1.(string) - } + reqJson, err := json.Marshal(attrs) + if err != nil { + assert.Fail(t, err.Error()) + } - cattrs[k] = atri + gattrs := objGet.Path("$.attributes").Object().Raw() + // Since we want to perform an equals check on the request attributes and response attributes we need to marshal and + // unmarshal twice over the object + respJson, err := json.Marshal(gattrs) + if err != nil { + assert.Fail(t, err.Error()) + } + var cattrs coreapi.AttributeMapRequest + err = json.Unmarshal(respJson, &cattrs) + if err != nil { + assert.Fail(t, err.Error()) + } + respJson, err = json.Marshal(cattrs) + if err != nil { + assert.Fail(t, err.Error()) } - assert.Equal(t, attrs, cattrs) + assert.Equal(t, reqJson, respJson) } return objGet } @@ -433,3 +483,9 @@ func nonExistingGenericDocumentCheck(e *httpexpect.Expect, auth string, document Expect().Status(404).JSON().NotNull() return objGet } + +func getV2DocumentWithStatus(e *httpexpect.Expect, auth, docID, status string, code int) *httpexpect.Value { + objGet := addCommonHeaders(e.GET("/v2/documents/"+docID+"/"+status), auth). + Expect().Status(code).JSON().NotNull() + return objGet +} diff --git a/testworld/incorrect_proto_test.go b/testworld/incorrect_proto_test.go index 547b2b2f9..7811be1aa 100644 --- a/testworld/incorrect_proto_test.go +++ b/testworld/incorrect_proto_test.go @@ -27,7 +27,7 @@ func TestIncorrectProto_ValidMessage(t *testing.T) { publicKey, privateKey := GetSigningKeyPair(t, eve.host.idService, eve.id, ctxh) collaborators := [][]byte{bob.id[:]} - dm := createCDWithEmbeddedPO(t, collaborators, eve.id, publicKey, privateKey, eve.host.config.GetContractAddress(config.AnchorRepo)) + dm := createCDWithEmbeddedInvoice(t, collaborators, eve.id, publicKey, privateKey, eve.host.config.GetContractAddress(config.AnchorRepo)) p := p2p.AccessPeer(eve.host.p2pClient) @@ -53,7 +53,7 @@ func TestIncorrectProto_DifferentVersion(t *testing.T) { publicKey, privateKey := GetSigningKeyPair(t, eve.host.idService, eve.id, ctxh) collaborators := [][]byte{bob.id[:]} - dm := createCDWithEmbeddedPO(t, collaborators, eve.id, publicKey, privateKey, eve.host.config.GetContractAddress(config.AnchorRepo)) + dm := createCDWithEmbeddedInvoice(t, collaborators, eve.id, publicKey, privateKey, eve.host.config.GetContractAddress(config.AnchorRepo)) p := p2p.AccessPeer(eve.host.p2pClient) @@ -80,7 +80,7 @@ func TestIncorrectProto_InvalidBody(t *testing.T) { publicKey, privateKey := GetSigningKeyPair(t, eve.host.idService, eve.id, ctxh) collaborators := [][]byte{bob.id[:]} - dm := createCDWithEmbeddedPO(t, collaborators, eve.id, publicKey, privateKey, eve.host.config.GetContractAddress(config.AnchorRepo)) + dm := createCDWithEmbeddedInvoice(t, collaborators, eve.id, publicKey, privateKey, eve.host.config.GetContractAddress(config.AnchorRepo)) p := p2p.AccessPeer(eve.host.p2pClient) @@ -107,7 +107,7 @@ func TestIncorrectProto_InvalidHeader(t *testing.T) { publicKey, privateKey := GetSigningKeyPair(t, eve.host.idService, eve.id, ctxh) collaborators := [][]byte{bob.id[:]} - dm := createCDWithEmbeddedPO(t, collaborators, eve.id, publicKey, privateKey, eve.host.config.GetContractAddress(config.AnchorRepo)) + dm := createCDWithEmbeddedInvoice(t, collaborators, eve.id, publicKey, privateKey, eve.host.config.GetContractAddress(config.AnchorRepo)) p := p2p.AccessPeer(eve.host.p2pClient) @@ -133,7 +133,7 @@ func TestIncorrectProto_AboveMaxSize(t *testing.T) { publicKey, privateKey := GetSigningKeyPair(t, eve.host.idService, eve.id, ctxh) collaborators := [][]byte{bob.id[:]} - dm := createCDWithEmbeddedPO(t, collaborators, eve.id, publicKey, privateKey, eve.host.config.GetContractAddress(config.AnchorRepo)) + dm := createCDWithEmbeddedInvoice(t, collaborators, eve.id, publicKey, privateKey, eve.host.config.GetContractAddress(config.AnchorRepo)) p := p2p.AccessPeer(eve.host.p2pClient) diff --git a/testworld/missing_node_test.go b/testworld/missing_node_test.go index 3cc0edcfc..48d16842b 100644 --- a/testworld/missing_node_test.go +++ b/testworld/missing_node_test.go @@ -28,7 +28,7 @@ func TestMissingNode_InvalidIdentity(t *testing.T) { randomDID := testingidentity.GenerateRandomDID() // Alice shares a document with a randomly generated DID - res := createDocument(alice.httpExpect, alice.id.String(), typeInvoice, http.StatusOK, defaultInvoicePayload([]string{randomDID.String()})) + res := createDocument(alice.httpExpect, alice.id.String(), typeInvoice, http.StatusAccepted, defaultInvoicePayload([]string{randomDID.String()})) // Transaction should fail with invalid identity errorMessage := "failed to send document to the node: bytecode for deployed identity contract " + randomDID.String() + " not correct" @@ -45,7 +45,7 @@ func TestMissingNode_MissingP2PKey(t *testing.T) { randomDID := createIdentity(t, alice.host.idFactory, alice.host.idService, alice.host.config) // Alice shares a document with a randomly generated DID with missing P2P Key - res := createDocument(alice.httpExpect, alice.id.String(), typeInvoice, http.StatusOK, defaultInvoicePayload([]string{randomDID.String()})) + res := createDocument(alice.httpExpect, alice.id.String(), typeInvoice, http.StatusAccepted, defaultInvoicePayload([]string{randomDID.String()})) // Transaction should fail with missing p2p key error errorMessage := "failed to send document to the node: error fetching p2p key: missing p2p key" diff --git a/testworld/nft_test.go b/testworld/nft_test.go index 7c71d0a33..dc156204c 100644 --- a/testworld/nft_test.go +++ b/testworld/nft_test.go @@ -40,7 +40,7 @@ func invoiceUnpaidMint(t *testing.T, documentType string, grantNFTAccess, tokenP registry := alice.host.config.GetContractAddress(config.InvoiceUnpaidNFT) // Alice shares document with Bob - res := createDocument(alice.httpExpect, alice.id.String(), documentType, http.StatusOK, defaultNFTPayload(documentType, []string{bob.id.String()}, alice.id.String())) + res := createDocument(alice.httpExpect, alice.id.String(), documentType, http.StatusAccepted, defaultNFTPayload(documentType, []string{bob.id.String()}, alice.id.String())) txID := getTransactionID(t, res) status, message := getTransactionStatusAndMessage(alice.httpExpect, alice.id.String(), txID) if status != "success" { @@ -87,7 +87,7 @@ func invoiceUnpaidMint(t *testing.T, documentType string, grantNFTAccess, tokenP "submit_nft_owner_access_proof": nftReadAccessProof, "grant_nft_access": grantNFTAccess, } - response, err = alice.host.mintNFT(alice.httpExpect, alice.id.String(), http.StatusCreated, payload) + response, err = alice.host.mintNFT(alice.httpExpect, alice.id.String(), http.StatusAccepted, payload) } else { // mint a PO NFT @@ -95,7 +95,7 @@ func invoiceUnpaidMint(t *testing.T, documentType string, grantNFTAccess, tokenP "document_id": docIdentifier, "deposit_address": depositAddress, // Centrifuge address } - response, err = alice.host.mintUnpaidInvoiceNFT(alice.httpExpect, alice.id.String(), http.StatusOK, docIdentifier, payload) + response, err = alice.host.mintUnpaidInvoiceNFT(alice.httpExpect, alice.id.String(), http.StatusAccepted, docIdentifier, payload) } assert.NoError(t, err, "mintNFT should be successful") diff --git a/testworld/park_test.go b/testworld/park_test.go index 367e24840..fc144aa6c 100644 --- a/testworld/park_test.go +++ b/testworld/park_test.go @@ -23,7 +23,7 @@ func TestHost_BasicDocumentShare(t *testing.T) { charlie := doctorFord.getHostTestSuite(t, "Charlie") // alice shares a document with bob and charlie - res := createDocument(alice.httpExpect, alice.id.String(), typeInvoice, http.StatusOK, defaultInvoicePayload([]string{bob.id.String(), charlie.id.String()})) + res := createDocument(alice.httpExpect, alice.id.String(), typeInvoice, http.StatusAccepted, defaultInvoicePayload([]string{bob.id.String(), charlie.id.String()})) txID := getTransactionID(t, res) status, message := getTransactionStatusAndMessage(alice.httpExpect, alice.id.String(), txID) if status != "success" { @@ -47,7 +47,7 @@ func TestHost_BasicDocumentShare(t *testing.T) { // bobs node sends a webhook for received anchored doc msg, err = doctorFord.maeve.getReceivedMsg(bob.id.String(), int(notification.ReceivedPayload), docIdentifier) assert.NoError(t, err) - assert.Equal(t, strings.ToLower(alice.id.String()), strings.ToLower(msg.FromId)) + assert.Equal(t, strings.ToLower(alice.id.String()), strings.ToLower(msg.FromID)) fmt.Println("Host test success") } @@ -77,7 +77,7 @@ func TestHost_RestartWithAccounts(t *testing.T) { acc1 := sleepyHost.accounts[0] res := getAccount(sleepyTS.httpExpect, sleepyTS.id.String(), http.StatusOK, acc1) acc1Res := res.Value("identity_id").String().NotEmpty() - acc1Res.Equal(acc1) + acc1Res.Equal(strings.ToLower(acc1)) // Stop host sleepyHost.kill() @@ -91,5 +91,5 @@ func TestHost_RestartWithAccounts(t *testing.T) { // Verify accounts are available after restart res = getAccount(sleepyTS.httpExpect, sleepyTS.id.String(), http.StatusOK, acc1) acc1Res = res.Value("identity_id").String().NotEmpty() - acc1Res.Equal(acc1) + acc1Res.Equal(strings.ToLower(acc1)) } diff --git a/testworld/payloads.go b/testworld/payloads.go index 05b5bd2f7..7a54197b0 100644 --- a/testworld/payloads.go +++ b/testworld/payloads.go @@ -10,26 +10,11 @@ func defaultDocumentPayload(documentType string, collaborators []string) map[str switch documentType { case typeInvoice: return defaultInvoicePayload(collaborators) - case typePO: - return defaultPOPayload(collaborators) default: return defaultInvoicePayload(collaborators) } } -func defaultPOPayload(collaborators []string) map[string]interface{} { - return map[string]interface{}{ - "data": map[string]interface{}{ - "number": "12324", - "date_created": "2018-09-26T23:12:37.902198664Z", - "total_amount": "40", - "currency": "USD", - }, - "write_access": collaborators, - "attributes": defaultAttributePayload(), - } -} - func defaultEntityPayload(identity string, collaborators []string) map[string]interface{} { return map[string]interface{}{ "data": map[string]interface{}{ @@ -169,25 +154,10 @@ func invoiceNFTPayload(collaborators []string, sender string) map[string]interfa } } -func poNFTPayload(collaborators []string) map[string]interface{} { - return map[string]interface{}{ - "data": map[string]interface{}{ - "number": "123245", - "date_created": "2018-09-26T23:12:37.902198664Z", - "currency": "USD", - "total_amount": "40", - "document_type": "po", - }, - "write_access": collaborators, - } -} - func defaultNFTPayload(documentType string, collaborators []string, sender string) map[string]interface{} { switch documentType { case typeInvoice: return invoiceNFTPayload(collaborators, sender) - case typePO: - return poNFTPayload(collaborators) default: return invoiceNFTPayload(collaborators, sender) } @@ -198,26 +168,11 @@ func updatedDocumentPayload(documentType string, collaborators []string) map[str switch documentType { case typeInvoice: return updatedInvoicePayload(collaborators) - case typePO: - return updatedPOPayload(collaborators) default: return updatedInvoicePayload(collaborators) } } -func updatedPOPayload(collaborators []string) map[string]interface{} { - return map[string]interface{}{ - "data": map[string]interface{}{ - "number": "12324", - "date_created": "2018-09-26T23:12:37.902198664Z", - "currency": "EUR", - "total_amount": "42", - }, - "write_access": collaborators, - } - -} - func updatedInvoicePayload(collaborators []string) map[string]interface{} { return map[string]interface{}{ "data": map[string]interface{}{ @@ -248,16 +203,13 @@ func updatedEntityPayload(identity string, collaborators []string) map[string]in } func defaultProofPayload(documentType string) map[string]interface{} { - if documentType == typeInvoice { - - return map[string]interface{}{ - "type": "http://github.com/centrifuge/centrifuge-protobufs/invoice/#invoice.InvoiceData", - "fields": []string{"invoice.net_amount", "invoice.currency"}, - } + if documentType != typeInvoice { + return nil } + return map[string]interface{}{ - "type": "http://github.com/centrifuge/centrifuge-protobufs/purchaseorder/#purchaseorder.PurchaseOrderData", - "fields": []string{"po.total_amount", "po.currency"}, + "type": "http://github.com/centrifuge/centrifuge-protobufs/invoice/#invoice.InvoiceData", + "fields": []string{"invoice.net_amount", "invoice.currency"}, } } diff --git a/testworld/proof_test.go b/testworld/proof_test.go index 167d3eac4..1b34bc880 100644 --- a/testworld/proof_test.go +++ b/testworld/proof_test.go @@ -14,17 +14,12 @@ func TestProofWithMultipleFields_invoice_successful(t *testing.T) { proofWithMultipleFields_successful(t, typeInvoice) } -func TestProofWithMultipleFields_po_successful(t *testing.T) { - t.Parallel() - proofWithMultipleFields_successful(t, typePO) -} - func proofWithMultipleFields_successful(t *testing.T, documentType string) { alice := doctorFord.getHostTestSuite(t, "Alice") bob := doctorFord.getHostTestSuite(t, "Bob") // Alice shares a document with Bob - res := createDocument(alice.httpExpect, alice.id.String(), documentType, http.StatusOK, defaultDocumentPayload(documentType, []string{bob.id.String()})) + res := createDocument(alice.httpExpect, alice.id.String(), documentType, http.StatusAccepted, defaultDocumentPayload(documentType, []string{bob.id.String()})) txID := getTransactionID(t, res) status, message := getTransactionStatusAndMessage(alice.httpExpect, alice.id.String(), txID) if status != "success" { @@ -49,13 +44,6 @@ func checkProof(objProof *httpexpect.Object, documentType string, docIdentifier compactPrefix := "0x00010000" // invoice prefix prop1 := "0000002d" // invoice.net_amount prop2 := "0000000d" // invoice.currency - - if documentType == typePO { - compactPrefix = "0x00020000" // po prefix - prop1 = "00000012" // po.total_amount - prop2 = "00000011" // po.currency - } - objProof.Path("$.header.document_id").String().Equal(docIdentifier) objProof.Path("$.field_proofs[0].property").String().Equal(compactPrefix + prop1) objProof.Path("$.field_proofs[0].sorted_hashes").NotNull() diff --git a/testworld/signature_test.go b/testworld/signature_test.go index 4e015d3da..f3719cdca 100644 --- a/testworld/signature_test.go +++ b/testworld/signature_test.go @@ -12,14 +12,12 @@ import ( "github.com/centrifuge/go-centrifuge/crypto" "github.com/centrifuge/go-centrifuge/crypto/secp256k1" "github.com/centrifuge/go-centrifuge/documents" - "github.com/centrifuge/go-centrifuge/documents/purchaseorder" + "github.com/centrifuge/go-centrifuge/documents/invoice" "github.com/centrifuge/go-centrifuge/identity" "github.com/centrifuge/go-centrifuge/testingutils/config" - "github.com/centrifuge/go-centrifuge/testingutils/documents" mockdoc "github.com/centrifuge/go-centrifuge/testingutils/documents" "github.com/centrifuge/go-centrifuge/utils" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -33,7 +31,7 @@ func TestHost_GetSignatureFromCollaboratorBasedOnWrongSignature(t *testing.T) { publicKey, privateKey := GetSigningKeyPair(t, mallory.host.idService, mallory.id, mctxh) collaborators := [][]byte{alice.id[:]} - dm := createCDWithEmbeddedPOWithWrongSignature(t, collaborators, alice.id, publicKey, privateKey, mallory.host.config.GetContractAddress(config.AnchorRepo)) + dm := createCDWithEmbeddedInvoiceWithWrongSignature(t, collaborators, alice.id, publicKey, privateKey, mallory.host.config.GetContractAddress(config.AnchorRepo)) signatures, signatureErrors, err := mallory.host.p2pClient.GetSignaturesForDocument(mctxh, dm) assert.NoError(t, err) @@ -52,9 +50,9 @@ func TestHost_ReturnSignatureComputedBaseOnAnotherSigningRoot(t *testing.T) { publicKey, privateKey := GetSigningKeyPair(t, alice.host.idService, alice.id, actxh) collaborators := [][]byte{mallory.id[:]} - dm := createCDWithEmbeddedPO(t, collaborators, alice.id, publicKey, privateKey, alice.host.config.GetContractAddress(config.AnchorRepo)) + dm := createCDWithEmbeddedInvoice(t, collaborators, alice.id, publicKey, privateKey, alice.host.config.GetContractAddress(config.AnchorRepo)) - dm2 := createCDWithEmbeddedPO(t, collaborators, alice.id, publicKey, privateKey, alice.host.config.GetContractAddress(config.AnchorRepo)) + dm2 := createCDWithEmbeddedInvoice(t, collaborators, alice.id, publicKey, privateKey, alice.host.config.GetContractAddress(config.AnchorRepo)) sr, err := dm2.CalculateSigningRoot() assert.NoError(t, err) @@ -93,7 +91,7 @@ func TestHost_SignKeyNotInCollaboration(t *testing.T) { publicKey, privateKey := GetSigningKeyPair(t, alice.host.idService, alice.id, actxh) collaborators := [][]byte{mallory.id[:]} - dm := createCDWithEmbeddedPO(t, collaborators, alice.id, publicKey, privateKey, alice.host.config.GetContractAddress(config.AnchorRepo)) + dm := createCDWithEmbeddedInvoice(t, collaborators, alice.id, publicKey, privateKey, alice.host.config.GetContractAddress(config.AnchorRepo)) sr, err := dm.CalculateSigningRoot() assert.NoError(t, err) @@ -156,7 +154,7 @@ func TestHost_ValidSignature(t *testing.T) { publicKey, privateKey := GetSigningKeyPair(t, eve.host.idService, eve.id, ctxh) collaborators := [][]byte{bob.id[:]} - dm := createCDWithEmbeddedPO(t, collaborators, eve.id, publicKey, privateKey, eve.host.config.GetContractAddress(config.AnchorRepo)) + dm := createCDWithEmbeddedInvoice(t, collaborators, eve.id, publicKey, privateKey, eve.host.config.GetContractAddress(config.AnchorRepo)) signatures, signatureErrors, err := eve.host.p2pClient.GetSignaturesForDocument(ctxh, dm) assert.NoError(t, err) @@ -177,7 +175,7 @@ func TestHost_FakedSignature(t *testing.T) { publicKey, privateKey := GetSigningKeyPair(t, alice.host.idService, alice.id, actxh) collaborators := [][]byte{bob.id[:]} - dm := createCDWithEmbeddedPO(t, collaborators, eve.id, publicKey, privateKey, eve.host.config.GetContractAddress(config.AnchorRepo)) + dm := createCDWithEmbeddedInvoice(t, collaborators, eve.id, publicKey, privateKey, eve.host.config.GetContractAddress(config.AnchorRepo)) signatures, signatureErrors, err := eve.host.p2pClient.GetSignaturesForDocument(ectxh, dm) assert.NoError(t, err) @@ -202,7 +200,7 @@ func TestHost_RevokedSigningKey(t *testing.T) { // Eve creates document with Bob and signs with Revoked key collaborators := [][]byte{bob.id[:]} - dm := createCDWithEmbeddedPO(t, collaborators, eve.id, publicKey, privateKey, eve.host.config.GetContractAddress(config.AnchorRepo)) + dm := createCDWithEmbeddedInvoice(t, collaborators, eve.id, publicKey, privateKey, eve.host.config.GetContractAddress(config.AnchorRepo)) signatures, signatureErrors, err := eve.host.p2pClient.GetSignaturesForDocument(ctxh, dm) assert.NoError(t, err) @@ -216,7 +214,7 @@ func TestHost_RevokedSigningKey(t *testing.T) { // Revoke Key RevokeKey(t, eve.host.idService, keys[0].GetKey(), eve.id, ctxh) - res := createDocument(bob.httpExpect, bob.id.String(), typeInvoice, http.StatusOK, defaultInvoicePayload([]string{eve.id.String()})) + res := createDocument(bob.httpExpect, bob.id.String(), typeInvoice, http.StatusAccepted, defaultInvoicePayload([]string{eve.id.String()})) txID := getTransactionID(t, res) status, _ := getTransactionStatusAndMessage(bob.httpExpect, bob.id.String(), txID) // Even though there was a signature validation error, as of now, we keep anchoring document @@ -224,26 +222,24 @@ func TestHost_RevokedSigningKey(t *testing.T) { } // Helper Methods -func createCDWithEmbeddedPO(t *testing.T, collaborators [][]byte, identityDID identity.DID, publicKey []byte, privateKey []byte, anchorRepo common.Address) documents.Model { - payload := testingdocuments.CreatePOPayload() - var cs []string - for _, c := range collaborators { - cs = append(cs, hexutil.Encode(c)) - } - payload.WriteAccess = cs - - po := new(purchaseorder.PurchaseOrder) - err := po.InitPurchaseOrderInput(payload, identityDID) +func createCDWithEmbeddedInvoice(t *testing.T, collaborators [][]byte, identityDID identity.DID, publicKey []byte, privateKey []byte, anchorRepo common.Address) documents.Model { + payload := invoice.CreateInvoicePayload(t, nil) + var cs []identity.DID + collabs, err := identity.BytesToDIDs(collaborators...) assert.NoError(t, err) - - po.SetUsedAnchorRepoAddress(anchorRepo) - err = po.AddUpdateLog(identityDID) + for _, c := range collabs { + cs = append(cs, *c) + } + payload.Collaborators.ReadWriteCollaborators = cs + inv := invoice.InitInvoice(t, identityDID, payload) + inv.SetUsedAnchorRepoAddress(anchorRepo) + err = inv.AddUpdateLog(identityDID) assert.NoError(t, err) - _, err = po.CalculateDataRoot() + _, err = inv.CalculateDataRoot() assert.NoError(t, err) - sr, err := po.CalculateSigningRoot() + sr, err := inv.CalculateSigningRoot() assert.NoError(t, err) s, err := crypto.SignMessage(privateKey, sr, crypto.CurveSecp256K1) @@ -255,35 +251,34 @@ func createCDWithEmbeddedPO(t *testing.T, collaborators [][]byte, identityDID id PublicKey: publicKey, Signature: s, } - po.AppendSignatures(sig) + inv.AppendSignatures(sig) - _, err = po.CalculateDocumentRoot() + _, err = inv.CalculateDocumentRoot() assert.NoError(t, err) - return po + return inv } -func createCDWithEmbeddedPOWithWrongSignature(t *testing.T, collaborators [][]byte, identityDID identity.DID, publicKey []byte, privateKey []byte, anchorRepo common.Address) documents.Model { - payload := testingdocuments.CreatePOPayload() - var cs []string - for _, c := range collaborators { - cs = append(cs, hexutil.Encode(c)) - } - payload.WriteAccess = cs - - po := new(purchaseorder.PurchaseOrder) - err := po.InitPurchaseOrderInput(payload, identityDID) +func createCDWithEmbeddedInvoiceWithWrongSignature(t *testing.T, collaborators [][]byte, identityDID identity.DID, publicKey []byte, privateKey []byte, anchorRepo common.Address) documents.Model { + payload := invoice.CreateInvoicePayload(t, nil) + var cs []identity.DID + collabs, err := identity.BytesToDIDs(collaborators...) assert.NoError(t, err) + for _, c := range collabs { + cs = append(cs, *c) + } + payload.Collaborators.ReadWriteCollaborators = cs - po.SetUsedAnchorRepoAddress(anchorRepo) - err = po.AddUpdateLog(identityDID) + inv := invoice.InitInvoice(t, identityDID, payload) + inv.SetUsedAnchorRepoAddress(anchorRepo) + err = inv.AddUpdateLog(identityDID) assert.NoError(t, err) - _, err = po.CalculateDataRoot() + _, err = inv.CalculateDataRoot() assert.NoError(t, err) //Wrong Signing Root will cause wrong signature - sr, err := po.CalculateSignaturesRoot() + sr, err := inv.CalculateSignaturesRoot() assert.NoError(t, err) s, err := crypto.SignMessage(privateKey, sr, crypto.CurveSecp256K1) @@ -295,12 +290,12 @@ func createCDWithEmbeddedPOWithWrongSignature(t *testing.T, collaborators [][]by PublicKey: publicKey, Signature: s, } - po.AppendSignatures(sig) + inv.AppendSignatures(sig) - _, err = po.CalculateDocumentRoot() + _, err = inv.CalculateDocumentRoot() assert.NoError(t, err) - return po + return inv } func RevokeKey(t *testing.T, idService identity.Service, key [32]byte, identityDID identity.DID, ctx context.Context) { diff --git a/testworld/transfer_detail_test.go b/testworld/transfer_detail_test.go index 8fb43eccb..8221a4c9f 100644 --- a/testworld/transfer_detail_test.go +++ b/testworld/transfer_detail_test.go @@ -19,7 +19,7 @@ func Test_CreateGetUpdateTransfers(t *testing.T) { } func createInvoiceWithTransfer(t *testing.T, alice, bob hostTestSuite) (transferId, docIdentifier string) { - res := createDocument(alice.httpExpect, alice.id.String(), typeInvoice, http.StatusOK, defaultInvoicePayload([]string{bob.id.String()})) + res := createDocument(alice.httpExpect, alice.id.String(), typeInvoice, http.StatusAccepted, defaultInvoicePayload([]string{bob.id.String()})) txID := getTransactionID(t, res) status, message := getTransactionStatusAndMessage(alice.httpExpect, alice.id.String(), txID) if status != "success" { @@ -35,7 +35,7 @@ func createInvoiceWithTransfer(t *testing.T, alice, bob hostTestSuite) (transfer getDocumentAndCheck(t, bob.httpExpect, bob.id.String(), typeInvoice, params, true) // Alice creates a transfer designating Bob as the recipient - res = createTransfer(alice.httpExpect, alice.id.String(), docIdentifier, http.StatusCreated, defaultTransferPayload(alice.id.String(), bob.id.String())) + res = createTransfer(alice.httpExpect, alice.id.String(), docIdentifier, http.StatusAccepted, defaultTransferPayload(alice.id.String(), bob.id.String())) txID = getTransactionID(t, res) status, message = getTransactionStatusAndMessage(alice.httpExpect, alice.id.String(), txID) if status != "success" { @@ -58,7 +58,7 @@ func createInvoiceWithTransfer(t *testing.T, alice, bob hostTestSuite) (transfer func listTransfer(t *testing.T, alice, bob hostTestSuite, docIdentifier string) { var transfers []string for i := 0; i < 5; i++ { - res := createTransfer(alice.httpExpect, alice.id.String(), docIdentifier, http.StatusCreated, defaultTransferPayload(alice.id.String(), bob.id.String())) + res := createTransfer(alice.httpExpect, alice.id.String(), docIdentifier, http.StatusAccepted, defaultTransferPayload(alice.id.String(), bob.id.String())) txID := getTransactionID(t, res) status, message := getTransactionStatusAndMessage(alice.httpExpect, alice.id.String(), txID) if status != "success" { @@ -79,7 +79,7 @@ func listTransfer(t *testing.T, alice, bob hostTestSuite, docIdentifier string) } func testUpdateTransfer(t *testing.T, alice, bob hostTestSuite, docID, transferID string) { - res := updateTransfer(alice.httpExpect, alice.id.String(), http.StatusCreated, docID, transferID, updateTransferPayload(alice.id.String(), bob.id.String())) + res := updateTransfer(alice.httpExpect, alice.id.String(), http.StatusAccepted, docID, transferID, updateTransferPayload(alice.id.String(), bob.id.String())) txID := getTransactionID(t, res) status, message := getTransactionStatusAndMessage(alice.httpExpect, alice.id.String(), txID) if status != "success" { diff --git a/testworld/v2_documents_test.go b/testworld/v2_documents_test.go new file mode 100644 index 000000000..94628c735 --- /dev/null +++ b/testworld/v2_documents_test.go @@ -0,0 +1,177 @@ +// +build testworld + +package testworld + +import ( + "net/http" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestV2InvoiceCreateAndCommit_new_document(t *testing.T) { + createNewDocument(t, func(dids []string) (map[string]interface{}, map[string]string) { + params := map[string]string{ + "currency": "EUR", + "number": "12345", + } + return invoiceCoreAPICreate(dids), params + }, func(dids []string) (map[string]interface{}, map[string]string) { + // Alice updates the document + payload := invoiceCoreAPIUpdate(dids) + // update currency to USD and number to 56789 + data := payload["data"].(map[string]interface{}) + data["currency"] = "USD" + data["number"] = "56789" + payload["data"] = data + return payload, map[string]string{ + "currency": "USD", + "number": "56789", + } + }) +} + +func TestV2InvoiceCreate_next_version(t *testing.T) { + createNextDocument(t, invoiceCoreAPICreate) +} + +func TestV2GenericCreateAndCommit_new_document(t *testing.T) { + createNewDocument(t, func(dids []string) (map[string]interface{}, map[string]string) { + return genericCoreAPICreate(dids), nil + }, func(dids []string) (map[string]interface{}, map[string]string) { + return genericCoreAPIUpdate(dids), nil + }) +} + +func TestV2GenericCreate_next_version(t *testing.T) { + createNextDocument(t, genericCoreAPICreate) +} + +func TestV2EntityCreateAndCommit_new_document(t *testing.T) { + createNewDocument(t, func(dids []string) (map[string]interface{}, map[string]string) { + params := map[string]string{ + "legal_name": "test company", + "identity": dids[0], + } + return entityCoreAPICreate(dids[0], dids), params + }, func(dids []string) (map[string]interface{}, map[string]string) { + p := entityCoreAPIUpdate(dids) + params := map[string]string{ + "legal_name": "updated company", + } + + return p, params + }) +} + +func TestV2EntityCreate_next_version(t *testing.T) { + createNextDocument(t, func(dids []string) map[string]interface{} { + var id string + if len(dids) > 0 { + id = dids[0] + } + return entityCoreAPICreate(id, dids) + }) +} + +func createNewDocument( + t *testing.T, + createPayloadParams, updatePayloadParams func([]string) (map[string]interface{}, map[string]string)) { + alice := doctorFord.getHostTestSuite(t, "Alice") + bob := doctorFord.getHostTestSuite(t, "Bob") + + // Alice prepares document to share with Bob + payload, params := createPayloadParams([]string{bob.id.String()}) + res := createDocumentV2(alice.httpExpect, alice.id.String(), "documents", http.StatusCreated, payload) + status := getDocumentStatus(t, res) + assert.Equal(t, status, "pending") + + checkDocumentParams(res, params) + docID := getDocumentIdentifier(t, res) + assert.NotEmpty(t, docID) + + // getting pending document should be successful + getV2DocumentWithStatus(alice.httpExpect, alice.id.String(), docID, "pending", http.StatusOK) + + // committed shouldn't be success + getV2DocumentWithStatus(alice.httpExpect, alice.id.String(), docID, "committed", http.StatusNotFound) + + // Alice updates the document + payload, params = updatePayloadParams([]string{bob.id.String()}) + payload["document_id"] = docID + res = updateDocumentV2(alice.httpExpect, alice.id.String(), "documents", http.StatusOK, payload) + status = getDocumentStatus(t, res) + assert.Equal(t, status, "pending") + checkDocumentParams(res, params) + getV2DocumentWithStatus(alice.httpExpect, alice.id.String(), docID, "pending", http.StatusOK) + + // Commits document and shares with Bob + res = commitDocument(alice.httpExpect, alice.id.String(), "documents", http.StatusAccepted, docID) + txID := getTransactionID(t, res) + status, message := getTransactionStatusAndMessage(alice.httpExpect, alice.id.String(), txID) + assert.Equal(t, status, "success", message) + getGenericDocumentAndCheck(t, alice.httpExpect, alice.id.String(), docID, nil, updateAttributes()) + + // pending document should fail + getV2DocumentWithStatus(alice.httpExpect, alice.id.String(), docID, "pending", http.StatusNotFound) + + // committed should be successful + getV2DocumentWithStatus(alice.httpExpect, alice.id.String(), docID, "committed", http.StatusOK) + + // Bob should have the document + getGenericDocumentAndCheck(t, bob.httpExpect, bob.id.String(), docID, nil, updateAttributes()) + + // try to commit same document again - failure + commitDocument(alice.httpExpect, alice.id.String(), "documents", http.StatusBadRequest, docID) +} + +func createNextDocument(t *testing.T, createPayload func([]string) map[string]interface{}) { + alice := doctorFord.getHostTestSuite(t, "Alice") + bob := doctorFord.getHostTestSuite(t, "Bob") + + // Alice shares document with Bob + res := createDocument(alice.httpExpect, alice.id.String(), "documents", http.StatusAccepted, createPayload([]string{bob.id.String()})) + txID := getTransactionID(t, res) + status, message := getTransactionStatusAndMessage(alice.httpExpect, alice.id.String(), txID) + assert.Equal(t, status, "success", message) + docID := getDocumentIdentifier(t, res) + versionID := getDocumentCurrentVersion(t, res) + assert.Equal(t, docID, versionID, "failed to create a fresh document") + getGenericDocumentAndCheck(t, bob.httpExpect, bob.id.String(), docID, nil, createAttributes()) + + // there should be no pending document with alice + getV2DocumentWithStatus(alice.httpExpect, alice.id.String(), docID, "pending", http.StatusNotFound) + + // bob creates a next pending version of the document + payload := createPayload(nil) + payload["document_id"] = docID + res = createDocumentV2(bob.httpExpect, bob.id.String(), "documents", http.StatusCreated, payload) + status = getDocumentStatus(t, res) + assert.Equal(t, status, "pending", "document must be in pending status") + edocID := getDocumentIdentifier(t, res) + assert.Equal(t, docID, edocID, "document identifiers mismatch") + eversionID := getDocumentCurrentVersion(t, res) + assert.NotEqual(t, docID, eversionID, "document ID and versionID must not be equal") + params := map[string]interface{}{ + "document_id": docID, + "version_id": eversionID, + } + + // alice should not have this version + nonExistingDocumentVersionCheck(alice.httpExpect, alice.id.String(), "documents", params) + + // bob has pending document + getV2DocumentWithStatus(bob.httpExpect, bob.id.String(), docID, "pending", http.StatusOK) + + // commit the document + // Commits document and shares with alice + res = commitDocument(bob.httpExpect, bob.id.String(), "documents", http.StatusAccepted, docID) + txID = getTransactionID(t, res) + status, message = getTransactionStatusAndMessage(bob.httpExpect, bob.id.String(), txID) + assert.Equal(t, status, "success", message) + + // bob shouldn't have any pending documents but has a committed one + getV2DocumentWithStatus(bob.httpExpect, bob.id.String(), docID, "pending", http.StatusNotFound) + getV2DocumentWithStatus(bob.httpExpect, bob.id.String(), docID, "committed", http.StatusOK) + getV2DocumentWithStatus(alice.httpExpect, alice.id.String(), docID, "committed", http.StatusOK) +} diff --git a/testworld/webhook.go b/testworld/webhook.go index af330b661..dcfad3617 100644 --- a/testworld/webhook.go +++ b/testworld/webhook.go @@ -7,11 +7,12 @@ import ( "encoding/json" "net/http" "strconv" + "strings" "sync" "time" - "github.com/centrifuge/centrifuge-protobufs/gen/go/notification" "github.com/centrifuge/go-centrifuge/errors" + "github.com/centrifuge/go-centrifuge/notification" ) type webhookReceiver struct { @@ -19,7 +20,7 @@ type webhookReceiver struct { endpoint string // receivedMsgs maps accountID+documentID to expected messages - receivedMsgs map[string]*notificationpb.NotificationMessage + receivedMsgs map[string]notification.Message receivedMsgsLock sync.RWMutex s *http.Server @@ -29,7 +30,7 @@ func newWebhookReceiver(port int, endpoint string) *webhookReceiver { return &webhookReceiver{ port: port, endpoint: endpoint, - receivedMsgs: make(map[string]*notificationpb.NotificationMessage), + receivedMsgs: make(map[string]notification.Message), receivedMsgsLock: sync.RWMutex{}, } } @@ -71,25 +72,25 @@ func (w *webhookReceiver) start(ctx context.Context) { func (w *webhookReceiver) ServeHTTP(rw http.ResponseWriter, r *http.Request) { decoder := json.NewDecoder(r.Body) - var msg *notificationpb.NotificationMessage + var msg notification.Message err := decoder.Decode(&msg) if err != nil { log.Error(err) } - log.Infof("webhook received for received document %s from collaborator %s for node %s", msg.DocumentId, msg.FromId, msg.AccountId) + log.Infof("webhook received for received document %s from collaborator %s for node %s", msg.DocumentID, msg.FromID, msg.AccountID) // store w.receivedMsgsLock.Lock() defer w.receivedMsgsLock.Unlock() - w.receivedMsgs[msg.AccountId+"-"+strconv.Itoa(int(msg.EventType))+"-"+msg.DocumentId] = msg + w.receivedMsgs[strings.ToLower(msg.AccountID)+"-"+strconv.Itoa(int(msg.EventType))+"-"+msg.DocumentID] = msg } -func (w *webhookReceiver) getReceivedMsg(accountID string, eventType int, docID string) (*notificationpb.NotificationMessage, error) { +func (w *webhookReceiver) getReceivedMsg(accountID string, eventType int, docID string) (notification.Message, error) { w.receivedMsgsLock.RLock() defer w.receivedMsgsLock.RUnlock() - n, ok := w.receivedMsgs[accountID+"-"+strconv.Itoa(eventType)+"-"+docID] + n, ok := w.receivedMsgs[strings.ToLower(accountID)+"-"+strconv.Itoa(eventType)+"-"+docID] if !ok { - return nil, errors.New("not found") + return n, errors.New("not found") } return n, nil diff --git a/utils/byteutils/bytes.go b/utils/byteutils/bytes.go index a2251dc1e..db7c70b0d 100644 --- a/utils/byteutils/bytes.go +++ b/utils/byteutils/bytes.go @@ -153,3 +153,27 @@ func (h HexBytes) Bytes() []byte { copy(d, h[:]) return d } + +// OptionalHex can have empty hex string and doesn't error. +type OptionalHex struct { + HexBytes +} + +// UnmarshalJSON unmarshalls hex encoded string to bytes. +// if the data is an empty string, it decoded value is empty bytes. +func (o *OptionalHex) UnmarshalJSON(data []byte) error { + str := string(data) + str = strings.TrimSpace(strings.Trim(str, "\"")) + if str == "" { + o.HexBytes = nil + return nil + } + + d, err := hexutil.Decode(str) + if err != nil { + return err + } + + *o = OptionalHex{HexBytes(d)} + return nil +} diff --git a/utils/byteutils/bytes_test.go b/utils/byteutils/bytes_test.go index 4789def1d..71296a1b9 100644 --- a/utils/byteutils/bytes_test.go +++ b/utils/byteutils/bytes_test.go @@ -389,3 +389,48 @@ func TestHexBytes_UnmarshalJSON(t *testing.T) { assert.Equal(t, hexutil.Encode(c.d), th.Hex.String()) } } + +func TestOptionalHex_UnmarshalJSON(t *testing.T) { + tests := []struct { + str string + d []byte + err bool + }{ + // nil + {str: "{}"}, + + // empty + {str: `{"hex": ""}`}, + + // invalid + {str: `{"hex": "12345dswr"}`, err: true}, + + // valid + {str: `{"hex": "0x"}`}, + + // valid + {str: `{"hex": "0xde0a3a82af"}`, d: []byte{222, 10, 58, 130, 175}}, + } + + type testHex struct { + Hex OptionalHex `json:"hex"` + } + + for _, c := range tests { + th := new(testHex) + err := json.Unmarshal([]byte(c.str), th) + if c.err { + assert.Error(t, err) + continue + } + + assert.NoError(t, err) + assert.Equal(t, len(c.d), len(th.Hex.Bytes())) + if len(c.d) < 1 { + continue + } + + assert.Equal(t, c.d, th.Hex.Bytes()) + assert.Equal(t, hexutil.Encode(c.d), th.Hex.String()) + } +} diff --git a/version/check.go b/version/check.go index dda535bd5..8fb482688 100644 --- a/version/check.go +++ b/version/check.go @@ -1,11 +1,8 @@ package version import ( - "fmt" - "github.com/Masterminds/semver" - "github.com/centrifuge/go-centrifuge/centerrors" - "github.com/centrifuge/go-centrifuge/code" + "github.com/centrifuge/go-centrifuge/errors" logging "github.com/ipfs/go-log" ) @@ -33,5 +30,5 @@ func CheckVersion(peerVersion string) bool { // IncompatibleVersionError for any peer with incompatible versions func IncompatibleVersionError(nodeVersion string) error { - return centerrors.New(code.VersionMismatch, fmt.Sprintf("Incompatible version: node version: %s, client version: %s", GetVersion(), nodeVersion)) + return errors.New("Incompatible version: node version: %s, client version: %s", GetVersion(), nodeVersion) } diff --git a/version/version.go b/version/version.go index 667316c2c..f1ed7bc70 100644 --- a/version/version.go +++ b/version/version.go @@ -10,7 +10,7 @@ import ( var gitCommit = "master" // CentrifugeNodeVersion is the current version of the app -const CentrifugeNodeVersion = "0.0.5" +const CentrifugeNodeVersion = "0.0.6" // GetVersion returns current cent node version in semvar format. func GetVersion() *semver.Version {