diff --git a/src/Collection/Collection.service.ts b/src/Collection/Collection.service.ts index 42927979..5173221e 100644 --- a/src/Collection/Collection.service.ts +++ b/src/Collection/Collection.service.ts @@ -608,11 +608,35 @@ export class CollectionService { return acc }, {} as Record) const thirdPartyIds = Object.keys(thirdPartyById) - const allCollections = await Collection.findAll({ + let allCollections = await Collection.findAll({ ...params, thirdPartyIds, }) + // Verify collections ownership + if (params.address !== undefined) { + const collectionsIds = allCollections.map( + (collection) => collection.contract_address! + ) + const remoteCollections = await collectionAPI.fetchCollections({ + ids: collectionsIds, + }) + + if (remoteCollections.length > 0) { + // Create a map of remote collections for fast lookup + const remoteCollectionMap = Object.fromEntries( + remoteCollections.map(({ id, creator }) => [id, creator]) + ) + + // If exists the remote collection, filter by the creator field + allCollections = allCollections.filter( + ({ contract_address }) => + !remoteCollectionMap[contract_address!] || + remoteCollectionMap[contract_address!] === params.address + ) + } + } + // Set if the collection is programmatic for (const collection of allCollections) { if ( diff --git a/src/Item/Item.router.spec.ts b/src/Item/Item.router.spec.ts index ef40ea08..0558fbdb 100644 --- a/src/Item/Item.router.spec.ts +++ b/src/Item/Item.router.spec.ts @@ -181,7 +181,8 @@ describe('Item router', () => { beneficiary: itemFragment.beneficiary, collection_id: dbItem.collection_id, blockchain_item_id: dbItem.blockchain_item_id, - urn: itemFragmentMock.urn, + urn: itemFragment.urn, + eth_address: itemFragment.collection.creator, }, ok: true, }) @@ -280,7 +281,8 @@ describe('Item router', () => { { ...resultingItem, beneficiary: itemFragment.beneficiary, - urn: itemFragmentMock.urn, + urn: itemFragment.urn, + eth_address: itemFragment.collection.creator, }, resultItemNotPublished, resultingTPItem, @@ -348,7 +350,8 @@ describe('Item router', () => { beneficiary: itemFragment.beneficiary, collection_id: dbItem.collection_id, blockchain_item_id: dbItem.blockchain_item_id, - urn: itemFragmentMock.urn, + urn: itemFragment.urn, + eth_address: itemFragment.collection.creator, }, resultItemNotPublished, ], @@ -403,7 +406,8 @@ describe('Item router', () => { beneficiary: itemFragment.beneficiary, collection_id: dbItem.collection_id, blockchain_item_id: dbItem.blockchain_item_id, - urn: itemFragmentMock.urn, + urn: itemFragment.urn, + eth_address: itemFragment.collection.creator, }, resultItemNotPublished, resultingTPItem, @@ -453,7 +457,8 @@ describe('Item router', () => { beneficiary: itemFragment.beneficiary, collection_id: dbItem.collection_id, blockchain_item_id: dbItem.blockchain_item_id, - urn: itemFragmentMock.urn, + urn: itemFragment.urn, + eth_address: itemFragment.collection.creator, }, resultItemNotPublished, resultingTPItem, @@ -516,7 +521,8 @@ describe('Item router', () => { beneficiary: itemFragment.beneficiary, collection_id: dbItem.collection_id, blockchain_item_id: dbItem.blockchain_item_id, - urn: itemFragmentMock.urn, + urn: itemFragment.urn, + eth_address: itemFragment.collection.creator, }, resultItemNotPublished, ], @@ -567,6 +573,7 @@ describe('Item router', () => { collection_id: dbItem.collection_id, blockchain_item_id: dbItem.blockchain_item_id, urn: itemFragmentMock.urn, + eth_address: itemFragmentMock.collection.creator, }, resultItemNotPublished, ], @@ -1418,6 +1425,8 @@ describe('Item router', () => { }) describe('and is being moved into a published collection', () => { + let mockCollectionApi: jest.Mock + beforeEach(() => { mockItem.findOne.mockReset() mockItem.findOne.mockResolvedValueOnce({ @@ -1427,7 +1436,7 @@ describe('Item router', () => { mockCollection.findByIds .mockResolvedValueOnce([{ ...dbCollectionMock, item_count: 1 }]) .mockResolvedValueOnce([{ ...dbCollectionMock, item_count: 1 }]) - ;(collectionAPI.fetchCollection as jest.Mock).mockImplementationOnce( + mockCollectionApi = (collectionAPI.fetchCollection as jest.Mock).mockImplementation( () => Promise.resolve({ ...itemFragment.collection, @@ -1437,6 +1446,10 @@ describe('Item router', () => { mockOwnableCanUpsert(Item, itemToUpsert.id, wallet.address, true) }) + afterEach(() => { + mockCollectionApi.mockClear() + }) + it('should fail with can not add the item to a published collection message', async () => { const response = await server .put(buildURL(url)) @@ -2081,8 +2094,8 @@ describe('Item router', () => { data: { ...Bridge.toFullItem(dbItem), local_content_hash: expect.any(String), - eth_address: ethAddress, - beneficiary: 'aBeneficiary', + eth_address: itemFragment.collection.creator, + beneficiary: itemFragment.beneficiary, in_catalyst: true, is_published: true, urn: wearable.id, @@ -2222,7 +2235,7 @@ describe('Item router', () => { is_published: true, urn: wearable.id, local_content_hash: expect.any(String), - eth_address: wallet.address, + eth_address: itemFragment.collection.creator, created_at: dbItem.created_at.toISOString(), updated_at: currentDate.toISOString(), }, diff --git a/src/Item/Item.service.spec.ts b/src/Item/Item.service.spec.ts index aae31365..ff4f92db 100644 --- a/src/Item/Item.service.spec.ts +++ b/src/Item/Item.service.spec.ts @@ -1,6 +1,7 @@ import { dbCollectionMock as baseDbCollectionMock, dbTPCollectionMock as baseDbTPCollectionMock, + thirdPartyFragmentMock, } from '../../spec/mocks/collections' import { dbItemMock, @@ -19,6 +20,7 @@ import { CollectionService } from '../Collection/Collection.service' import { Bridge } from '../ethereum/api/Bridge' import { collectionAPI } from '../ethereum/api/collection' import { peerAPI } from '../ethereum/api/peer' +import { thirdPartyAPI } from '../ethereum/api/thirdParty' import { ItemCantBeMovedFromCollectionError, MaximunAmountOfTagsReachedError, @@ -32,7 +34,12 @@ import { VIDEO_PATH } from './utils' jest.mock('../ethereum/api/collection') jest.mock('../Collection/Collection.model') jest.mock('../ethereum/api/peer') +jest.mock('../ethereum/api/thirdParty') jest.mock('./Item.model') +jest.mock('../Curation/ItemCuration/ItemCuration.model') +jest.mock('../ThirdParty/VirtualThirdParty.model') + +const thirdPartyAPIMock = thirdPartyAPI as jest.Mocked describe('Item Service', () => { let dbItem: ItemAttributes @@ -91,6 +98,9 @@ describe('Item Service', () => { ids.includes(collection.id) ) ) + thirdPartyAPIMock.fetchThirdParty.mockResolvedValueOnce( + thirdPartyFragmentMock + ) }) it('should throw the ItemCantBeMovedFromCollectionError error', () => { @@ -113,6 +123,9 @@ describe('Item Service', () => { ids.includes(collection.id) ) ) + thirdPartyAPIMock.fetchThirdParty.mockResolvedValueOnce( + thirdPartyFragmentMock + ) }) it('should throw the ItemCantBeMovedFromCollectionError error', () => { diff --git a/src/Item/Item.service.ts b/src/Item/Item.service.ts index 20b738b5..c966f8a7 100644 --- a/src/Item/Item.service.ts +++ b/src/Item/Item.service.ts @@ -98,10 +98,10 @@ export class ItemService { const [dbItemCollection, itemCollection] = await Promise.all([ collectionId - ? this.collectionService.getDBCollection(collectionId) + ? this.collectionService.getCollection(collectionId) : undefined, isMovingItemFromACollectionToAnother || isMovingOrphanItemIntoACollection - ? this.collectionService.getDBCollection(item.collection_id!) + ? this.collectionService.getCollection(item.collection_id!) : undefined, ]) diff --git a/src/ethereum/api/Bridge.ts b/src/ethereum/api/Bridge.ts index 3fbebf94..fd5cf8e6 100644 --- a/src/ethereum/api/Bridge.ts +++ b/src/ethereum/api/Bridge.ts @@ -406,6 +406,7 @@ export class Bridge { in_catalyst, is_published: true, is_approved: remoteCollection.isApproved, + eth_address: remoteCollection.creator, price: remoteItem.price, beneficiary: remoteItem.beneficiary, blockchain_item_id: remoteItem.blockchainId, diff --git a/src/ethereum/api/collection.ts b/src/ethereum/api/collection.ts index 2b2e77e4..9f8a5134 100644 --- a/src/ethereum/api/collection.ts +++ b/src/ethereum/api/collection.ts @@ -23,6 +23,7 @@ import { } from './BaseGraphAPI' export type CollectionQueryFilters = { + ids?: string[] isApproved?: boolean } @@ -35,9 +36,13 @@ const getCollectionByIdQuery = () => gql` ${collectionFragment()} ` -const getCollectionsQuery = ({ isApproved }: CollectionQueryFilters) => { +const getCollectionsQuery = ({ ids, isApproved }: CollectionQueryFilters) => { const where: string[] = [] + if (ids !== undefined && ids.length > 0) { + where.push(`id_in: ${JSON.stringify(ids)}`) + } + if (isApproved !== undefined) { where.push(`isApproved : ${isApproved}`) }