diff --git a/documents/purchaseorder/bootstrapper.go b/documents/purchaseorder/bootstrapper.go index a128993ce..2bfe8c271 100644 --- a/documents/purchaseorder/bootstrapper.go +++ b/documents/purchaseorder/bootstrapper.go @@ -11,8 +11,6 @@ import ( "github.com/centrifuge/go-centrifuge/documents" "github.com/centrifuge/go-centrifuge/identity" "github.com/centrifuge/go-centrifuge/p2p" - "github.com/centrifuge/go-centrifuge/storage" - "github.com/syndtr/goleveldb/leveldb" ) // Bootstrapper implements bootstrap.Bootstrapper. @@ -25,11 +23,6 @@ func (Bootstrapper) Bootstrap(ctx map[string]interface{}) error { } cfg := ctx[config.BootstrappedConfig].(config.Configuration) - ldb, ok := ctx[storage.BootstrappedLevelDB].(*leveldb.DB) - if !ok { - return errors.New("initializing LevelDB repository failed") - } - p2pClient, ok := ctx[p2p.BootstrappedP2PClient].(p2p.Client) if !ok { return fmt.Errorf("p2p client not initialised") @@ -50,8 +43,14 @@ func (Bootstrapper) Bootstrap(ctx map[string]interface{}) error { return fmt.Errorf("identity service not initialised") } + repo, ok := ctx[documents.BootstrappedDocumentRepository].(documents.Repository) + if !ok { + return fmt.Errorf("document db repository not initialised") + } + repo.Register(&PurchaseOrder{}) + // register service - srv := DefaultService(cfg, getRepository(ldb), coredocument.DefaultProcessor(idService, p2pClient, anchorRepo, cfg), anchorRepo, idService) + srv := DefaultService(cfg, repo, coredocument.DefaultProcessor(idService, p2pClient, anchorRepo, cfg), anchorRepo, idService) err := registry.Register(documenttypes.PurchaseOrderDataTypeUrl, srv) if err != nil { return fmt.Errorf("failed to register purchase order service") diff --git a/documents/purchaseorder/repository.go b/documents/purchaseorder/repository.go deleted file mode 100644 index b05faad69..000000000 --- a/documents/purchaseorder/repository.go +++ /dev/null @@ -1,21 +0,0 @@ -package purchaseorder - -import ( - "github.com/centrifuge/go-centrifuge/documents" - "github.com/syndtr/goleveldb/leveldb" -) - -// repository is the purchase order repository -type repository struct { - documents.LevelDBRepository -} - -// getRepository returns the implemented documents.legacyRepo for purchase orders -func getRepository(ldb *leveldb.DB) documents.LegacyRepository { - return &repository{ - documents.LevelDBRepository{ - KeyPrefix: "purchaseorder", - LevelDB: ldb, - }, - } -} diff --git a/documents/purchaseorder/repository_test.go b/documents/purchaseorder/repository_test.go deleted file mode 100644 index c4e5043e3..000000000 --- a/documents/purchaseorder/repository_test.go +++ /dev/null @@ -1,30 +0,0 @@ -// +build unit - -package purchaseorder - -import ( - "testing" - - "github.com/centrifuge/go-centrifuge/documents" - "github.com/centrifuge/go-centrifuge/storage" - "github.com/stretchr/testify/assert" -) - -func TestRepository_getRepository(t *testing.T) { - r := testRepo() - assert.NotNil(t, r) - assert.Equal(t, "purchaseorder", r.(*repository).KeyPrefix) -} - -var testRepoGlobal documents.LegacyRepository - -func testRepo() documents.LegacyRepository { - if testRepoGlobal == nil { - ldb, err := storage.NewLevelDBStorage(storage.GetRandomTestStoragePath()) - if err != nil { - panic(err) - } - testRepoGlobal = getRepository(ldb) - } - return testRepoGlobal -} diff --git a/documents/purchaseorder/service.go b/documents/purchaseorder/service.go index 7dfdf340d..23a6fb148 100644 --- a/documents/purchaseorder/service.go +++ b/documents/purchaseorder/service.go @@ -52,7 +52,8 @@ type Service interface { // 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 { - repo documents.LegacyRepository + config documents.Config + repo documents.Repository coreDocProcessor coredocument.Processor notifier notification.Sender anchorRepository anchors.AnchorRepository @@ -60,8 +61,8 @@ type service struct { } // DefaultService returns the default implementation of the service -func DefaultService(config config.Configuration, repo documents.LegacyRepository, processor coredocument.Processor, anchorRepository anchors.AnchorRepository, identityService identity.Service) Service { - return service{repo: repo, coreDocProcessor: processor, notifier: notification.NewWebhookSender(config), anchorRepository: anchorRepository, identityService: identityService} +func DefaultService(config config.Configuration, repo documents.Repository, processor coredocument.Processor, anchorRepository anchors.AnchorRepository, identityService identity.Service) Service { + return service{config: config, repo: repo, coreDocProcessor: processor, notifier: notification.NewWebhookSender(config), anchorRepository: anchorRepository, identityService: identityService} } // DeriveFromCoreDocument takes a core document and returns a purchase order @@ -94,8 +95,14 @@ func (s service) calculateDataRoot(old, new documents.Model, validator documents return nil, errors.NewTypedError(documents.ErrDocumentInvalid, err) } + // get tenant ID + tenantID, err := s.config.GetIdentityID() + if err != nil { + return nil, errors.NewTypedError(documents.ErrDocumentConfigTenantID, err) + } + // we use CurrentVersion as the id since that will be unique across multiple versions of the same document - err = s.repo.Create(po.CoreDocument.CurrentVersion, po) + err = s.repo.Create(tenantID, po.CoreDocument.CurrentVersion, po) if err != nil { return nil, errors.NewTypedError(documents.ErrDocumentPersistence, err) } @@ -110,7 +117,7 @@ func (s service) Create(ctx *header.ContextHeader, po documents.Model) (document return nil, err } - po, err = documents.AnchorDocument(ctx, po, s.coreDocProcessor, s.repo.Update) + po, err = documents.AnchorDocument(ctx, po, s.coreDocProcessor, s.updater) if err != nil { return nil, err } @@ -118,6 +125,16 @@ func (s service) Create(ctx *header.ContextHeader, po documents.Model) (document return po, nil } +// updater wraps logic related to updating documents so that it can be executed as a closure +func (s service) updater(id []byte, model documents.Model) error { + // get tenant ID + tenantID, err := s.config.GetIdentityID() + if err != nil { + return errors.NewTypedError(documents.ErrDocumentConfigTenantID, err) + } + return s.repo.Update(tenantID, id, model) +} + // Update validates, persists, and anchors a new version of purchase order func (s service) Update(ctx *header.ContextHeader, po documents.Model) (documents.Model, error) { cd, err := po.PackCoreDocument() @@ -135,7 +152,7 @@ func (s service) Update(ctx *header.ContextHeader, po documents.Model) (document return nil, err } - po, err = documents.AnchorDocument(ctx, po, s.coreDocProcessor, s.repo.Update) + po, err = documents.AnchorDocument(ctx, po, s.coreDocProcessor, s.updater) if err != nil { return nil, err } @@ -241,8 +258,12 @@ func (s service) DerivePurchaseOrderResponse(doc documents.Model) (*clientpopb.P } func (s service) getPurchaseOrderVersion(documentID, version []byte) (model *PurchaseOrder, err error) { - var doc documents.Model = new(PurchaseOrder) - err = s.repo.LoadByID(version, doc) + // get tenant ID + tenantID, err := s.config.GetIdentityID() + if err != nil { + return nil, errors.NewTypedError(documents.ErrDocumentConfigTenantID, err) + } + doc, err := s.repo.Get(tenantID, version) if err != nil { return nil, errors.NewTypedError(documents.ErrDocumentVersionNotFound, err) } @@ -351,15 +372,21 @@ func (s service) RequestDocumentSignature(contextHeader *header.ContextHeader, m return nil, errors.NewTypedError(documents.ErrDocumentUnPackingCoreDocument, err) } + // get tenant ID + tenantID, err := s.config.GetIdentityID() + if err != nil { + return nil, errors.NewTypedError(documents.ErrDocumentConfigTenantID, err) + } + // Logic for receiving version n (n > 1) of the document for the first time - if !s.repo.Exists(cd.DocumentIdentifier) && !utils.IsSameByteSlice(cd.DocumentIdentifier, cd.CurrentVersion) { - err = s.repo.Create(cd.DocumentIdentifier, model) + if !s.repo.Exists(tenantID, cd.DocumentIdentifier) && !utils.IsSameByteSlice(cd.DocumentIdentifier, cd.CurrentVersion) { + err = s.repo.Create(tenantID, cd.DocumentIdentifier, model) if err != nil { return nil, errors.NewTypedError(documents.ErrDocumentPersistence, err) } } - err = s.repo.Create(cd.CurrentVersion, model) + err = s.repo.Create(tenantID, cd.CurrentVersion, model) if err != nil { return nil, errors.NewTypedError(documents.ErrDocumentPersistence, err) } @@ -381,7 +408,13 @@ func (s service) ReceiveAnchoredDocument(model documents.Model, headers *p2ppb.C return errors.NewTypedError(documents.ErrDocumentPackingCoreDocument, err) } - err = s.repo.Update(doc.CurrentVersion, model) + // get tenant ID + tenantID, err := s.config.GetIdentityID() + if err != nil { + return errors.NewTypedError(documents.ErrDocumentConfigTenantID, err) + } + + err = s.repo.Update(tenantID, doc.CurrentVersion, model) if err != nil { return errors.NewTypedError(documents.ErrDocumentPersistence, err) } @@ -403,5 +436,10 @@ func (s service) ReceiveAnchoredDocument(model documents.Model, headers *p2ppb.C // Exists checks if an purchase order exists func (s service) Exists(documentID []byte) bool { - return s.repo.Exists(documentID) + // get tenant ID + tenantID, err := s.config.GetIdentityID() + if err != nil { + return false + } + return s.repo.Exists(tenantID, documentID) } diff --git a/documents/purchaseorder/service_test.go b/documents/purchaseorder/service_test.go index 46284820d..70eda819f 100644 --- a/documents/purchaseorder/service_test.go +++ b/documents/purchaseorder/service_test.go @@ -16,6 +16,7 @@ import ( "github.com/centrifuge/go-centrifuge/header" "github.com/centrifuge/go-centrifuge/identity" clientpurchaseorderpb "github.com/centrifuge/go-centrifuge/protobufs/gen/go/purchaseorder" + "github.com/centrifuge/go-centrifuge/storage" "github.com/centrifuge/go-centrifuge/testingutils/commons" "github.com/centrifuge/go-centrifuge/testingutils/config" "github.com/centrifuge/go-centrifuge/testingutils/coredocument" @@ -44,13 +45,17 @@ func (r *mockAnchorRepo) GetDocumentRootOf(anchorID anchors.AnchorID) (anchors.D } func getServiceWithMockedLayers() (*testingcommons.MockIDService, Service) { + c := &testingconfig.MockConfig{} + c.On("GetIdentityID").Return(centIDBytes, nil) idService := &testingcommons.MockIDService{} idService.On("ValidateSignature", mock.Anything, mock.Anything).Return(nil) - return idService, DefaultService(new(testingconfig.MockConfig), testRepo(), &testingcoredocument.MockCoreDocumentProcessor{}, &mockAnchorRepo{}, idService) + return idService, DefaultService(c, testRepo(), &testingcoredocument.MockCoreDocumentProcessor{}, &mockAnchorRepo{}, idService) } func TestService_Update(t *testing.T) { - poSrv := service{repo: testRepo()} + c := &testingconfig.MockConfig{} + c.On("GetIdentityID").Return(centIDBytes, nil) + poSrv := service{config: c, repo: testRepo()} ctxh, err := header.NewContextHeader(context.Background(), cfg) assert.Nil(t, err) @@ -79,7 +84,7 @@ func TestService_Update(t *testing.T) { assert.Nil(t, err) cd.DocumentRoot = utils.RandomSlice(32) po.(*PurchaseOrder).CoreDocument = cd - testRepo().Create(cd.CurrentVersion, po) + testRepo().Create(centIDBytes, cd.CurrentVersion, po) // calculate data root fails model = &testingdocuments.MockModel{} @@ -118,9 +123,9 @@ func TestService_Update(t *testing.T) { newCD, err := po.PackCoreDocument() assert.Nil(t, err) - assert.True(t, testRepo().Exists(newCD.DocumentIdentifier)) - assert.True(t, testRepo().Exists(newCD.CurrentVersion)) - assert.True(t, testRepo().Exists(newCD.PreviousVersion)) + assert.True(t, testRepo().Exists(centIDBytes, newCD.DocumentIdentifier)) + assert.True(t, testRepo().Exists(centIDBytes, newCD.CurrentVersion)) + assert.True(t, testRepo().Exists(centIDBytes, newCD.PreviousVersion)) newData, err = poSrv.DerivePurchaseOrderData(po) assert.Nil(t, err) @@ -128,7 +133,9 @@ func TestService_Update(t *testing.T) { } func TestService_DeriveFromUpdatePayload(t *testing.T) { - poSrv := service{repo: testRepo()} + c := &testingconfig.MockConfig{} + c.On("GetIdentityID").Return(centIDBytes, nil) + poSrv := service{config: c, repo: testRepo()} // nil payload doc, err := poSrv.DeriveFromUpdatePayload(nil, nil) @@ -166,7 +173,7 @@ func TestService_DeriveFromUpdatePayload(t *testing.T) { old.CoreDocument.DocumentIdentifier = id old.CoreDocument.CurrentVersion = id old.CoreDocument.DocumentRoot = utils.RandomSlice(32) - err = testRepo().Create(id, old) + err = testRepo().Create(centIDBytes, id, old) assert.Nil(t, err) payload.Data = &clientpurchaseorderpb.PurchaseOrderData{ Recipient: "0x010203040506", @@ -266,7 +273,9 @@ func TestService_DeriveFromCoreDocument(t *testing.T) { func TestService_Create(t *testing.T) { ctxh, err := header.NewContextHeader(context.Background(), cfg) assert.Nil(t, err) - poSrv := service{repo: testRepo()} + c := &testingconfig.MockConfig{} + c.On("GetIdentityID").Return(centIDBytes, nil) + poSrv := service{config: c, repo: testRepo()} // calculate data root fails m, err := poSrv.Create(ctxh, &testingdocuments.MockModel{}) @@ -303,8 +312,8 @@ func TestService_Create(t *testing.T) { newCD, err := m.PackCoreDocument() assert.Nil(t, err) - assert.True(t, testRepo().Exists(newCD.DocumentIdentifier)) - assert.True(t, testRepo().Exists(newCD.CurrentVersion)) + assert.True(t, testRepo().Exists(centIDBytes, newCD.DocumentIdentifier)) + assert.True(t, testRepo().Exists(centIDBytes, newCD.CurrentVersion)) } func createAnchoredMockDocument(t *testing.T, skipSave bool) (*PurchaseOrder, error) { @@ -355,7 +364,7 @@ func createAnchoredMockDocument(t *testing.T, skipSave bool) (*PurchaseOrder, er } if !skipSave { - err = testRepo().Create(i.CoreDocument.CurrentVersion, i) + err = testRepo().Create(centIDBytes, i.CoreDocument.CurrentVersion, i) if err != nil { return nil, err } @@ -400,7 +409,7 @@ func TestService_CreateProofsValidationFails(t *testing.T) { i, err := createAnchoredMockDocument(t, false) assert.Nil(t, err) i.CoreDocument.SigningRoot = nil - err = testRepo().Update(i.CoreDocument.CurrentVersion, i) + err = testRepo().Update(centIDBytes, i.CoreDocument.CurrentVersion, i) assert.Nil(t, err) idService = mockSignatureCheck(i, idService, poSrv) _, err = poSrv.CreateProofs(i.CoreDocument.DocumentIdentifier, []string{"po.po_number"}) @@ -454,7 +463,7 @@ func updatedAnchoredMockDocument(t *testing.T, model *PurchaseOrder) (*PurchaseO if err != nil { return nil, err } - err = testRepo().Create(model.CoreDocument.CurrentVersion, model) + err = testRepo().Create(centIDBytes, model.CoreDocument.CurrentVersion, model) if err != nil { return nil, err } @@ -566,12 +575,14 @@ func createMockDocument() (*PurchaseOrder, error) { NextVersion: nextIdentifier, }, } - err := testRepo().Create(documentIdentifier, model) + err := testRepo().Create(centIDBytes, documentIdentifier, model) return model, err } func TestService_GetCurrentVersion(t *testing.T) { - poSrv := service{repo: testRepo()} + c := &testingconfig.MockConfig{} + c.On("GetIdentityID").Return(centIDBytes, nil) + poSrv := service{config: c, repo: testRepo()} thirdIdentifier := utils.RandomSlice(32) doc, err := createMockDocument() assert.Nil(t, err) @@ -591,7 +602,7 @@ func TestService_GetCurrentVersion(t *testing.T) { }, } - err = testRepo().Create(doc.CoreDocument.NextVersion, po2) + err = testRepo().Create(centIDBytes, doc.CoreDocument.NextVersion, po2) assert.Nil(t, err) mod2, err := poSrv.GetCurrentVersion(doc.CoreDocument.DocumentIdentifier) @@ -603,7 +614,9 @@ func TestService_GetCurrentVersion(t *testing.T) { } func TestService_GetVersion_invalid_version(t *testing.T) { - poSrv := service{repo: testRepo()} + c := &testingconfig.MockConfig{} + c.On("GetIdentityID").Return(centIDBytes, nil) + poSrv := service{config: c, repo: testRepo()} currentVersion := utils.RandomSlice(32) po := &PurchaseOrder{ @@ -613,7 +626,7 @@ func TestService_GetVersion_invalid_version(t *testing.T) { CurrentVersion: currentVersion, }, } - err := testRepo().Create(currentVersion, po) + err := testRepo().Create(centIDBytes, currentVersion, po) assert.Nil(t, err) mod, err := poSrv.GetVersion(utils.RandomSlice(32), currentVersion) @@ -622,7 +635,9 @@ func TestService_GetVersion_invalid_version(t *testing.T) { } func TestService_GetVersion(t *testing.T) { - poSrv := service{repo: testRepo()} + c := &testingconfig.MockConfig{} + c.On("GetIdentityID").Return(centIDBytes, nil) + poSrv := service{config: c, repo: testRepo()} documentIdentifier := utils.RandomSlice(32) currentVersion := utils.RandomSlice(32) @@ -633,7 +648,7 @@ func TestService_GetVersion(t *testing.T) { CurrentVersion: currentVersion, }, } - err := testRepo().Create(currentVersion, po) + err := testRepo().Create(centIDBytes, currentVersion, po) assert.Nil(t, err) mod, err := poSrv.GetVersion(documentIdentifier, currentVersion) @@ -656,7 +671,7 @@ func TestService_Exists(t *testing.T) { CurrentVersion: documentIdentifier, }, } - err := testRepo().Create(documentIdentifier, po) + err := testRepo().Create(centIDBytes, documentIdentifier, po) assert.Nil(t, err) exists := poSrv.Exists(documentIdentifier) @@ -683,7 +698,9 @@ func TestService_RequestDocumentSignature(t *testing.T) { } func TestService_calculateDataRoot(t *testing.T) { - poSrv := service{repo: testRepo()} + c := &testingconfig.MockConfig{} + c.On("GetIdentityID").Return(centIDBytes, nil) + poSrv := service{config: c, repo: testRepo()} ctxh, err := header.NewContextHeader(context.Background(), cfg) assert.Nil(t, err) @@ -709,12 +726,12 @@ func TestService_calculateDataRoot(t *testing.T) { po, err = poSrv.DeriveFromCreatePayload(testingdocuments.CreatePOPayload(), ctxh) assert.Nil(t, err) assert.Nil(t, po.(*PurchaseOrder).CoreDocument.DataRoot) - err = poSrv.repo.Create(po.(*PurchaseOrder).CoreDocument.CurrentVersion, po) + err = poSrv.repo.Create(centIDBytes, po.(*PurchaseOrder).CoreDocument.CurrentVersion, po) assert.Nil(t, err) po, err = poSrv.calculateDataRoot(nil, po, CreateValidator()) assert.Nil(t, po) assert.Error(t, err) - assert.Contains(t, err.Error(), "document already exists") + assert.Contains(t, err.Error(), documents.ErrDocumentRepositoryModelAllReadyExists) // success po, err = poSrv.DeriveFromCreatePayload(testingdocuments.CreatePOPayload(), ctxh) @@ -725,3 +742,17 @@ func TestService_calculateDataRoot(t *testing.T) { assert.NotNil(t, po) assert.NotNil(t, po.(*PurchaseOrder).CoreDocument.DataRoot) } + +var testRepoGlobal documents.Repository + +func testRepo() documents.Repository { + if testRepoGlobal == nil { + ldb, err := storage.NewLevelDBStorage(storage.GetRandomTestStoragePath()) + if err != nil { + panic(err) + } + testRepoGlobal = documents.NewLevelDBRepository(ldb) + testRepoGlobal.Register(&PurchaseOrder{}) + } + return testRepoGlobal +}