Skip to content

Commit

Permalink
Merge d13dd36 into 6ae2a75
Browse files Browse the repository at this point in the history
  • Loading branch information
juanmahidalgo committed May 12, 2022
2 parents 6ae2a75 + d13dd36 commit ea53ea6
Show file tree
Hide file tree
Showing 7 changed files with 415 additions and 94 deletions.
133 changes: 128 additions & 5 deletions src/Collection/Collection.model.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { Model, raw, SQL } from 'decentraland-server'
import { DEFAULT_LIMIT } from '../Pagination/utils'
import { CurationStatusFilter, CurationStatusSort } from '../Curation'
import { CollectionCuration } from '../Curation/CollectionCuration'
import { database } from '../database/database'
import { Item } from '../Item/Item.model'
import { CollectionAttributes } from './Collection.types'
Expand All @@ -7,16 +10,136 @@ type CollectionWithItemCount = CollectionAttributes & {
item_count: number
}

export type CollectionWithCounts = CollectionWithItemCount & {
collection_count: number
}

export type FindCollectionParams = {
limit?: number
offset?: number
q?: string
address?: string
thirdPartyIds?: string[]
assignee?: string
status?: CurationStatusFilter
sort?: CurationStatusSort
isPublished?: boolean
}

export class Collection extends Model<CollectionAttributes> {
static tableName = 'collections'

static findAll() {
return this.query<CollectionWithItemCount>(SQL`
SELECT *, (SELECT COUNT(*) FROM ${raw(
static getOrderByStatement(sort?: CurationStatusSort) {
switch (sort) {
case CurationStatusSort.MOST_RELEVANT:
return SQL`
ORDER BY
CASE WHEN(
collection_curations.assignee is NULL)
THEN 0
WHEN(collection_curations.assignee is NOT NULL AND collection_curations.status = ${CurationStatusFilter.PENDING})
THEN 1
WHEN(collection_curations.status = ${CurationStatusFilter.APPROVED})
THEN 2
WHEN(collection_curations.status = ${CurationStatusFilter.REJECTED})
THEN 3
ELSE 4
END
`
case CurationStatusSort.NAME_ASC:
return SQL`ORDER BY collections.name ASC`
case CurationStatusSort.NAME_DESC:
return SQL`ORDER BY collections.name DESC`
case CurationStatusSort.NEWEST:
return SQL`ORDER BY collections.created_at DESC`
default:
return SQL``
}
}

static getFindAllWhereStatement({
q,
assignee,
status,
address,
thirdPartyIds,
}: Pick<
FindCollectionParams,
'q' | 'assignee' | 'status' | 'address' | 'thirdPartyIds'
>) {
if (!q && !assignee && !status && !address && !thirdPartyIds?.length) {
return SQL``
}
const conditions = [
q ? SQL`collections.name LIKE '%' || ${q} || '%'` : undefined,
assignee ? SQL`collection_curations.assignee = ${assignee}` : undefined,
address
? thirdPartyIds?.length
? SQL`(collections.eth_address = ${address} OR third_party_id = ANY(${thirdPartyIds}))`
: SQL`collections.eth_address = ${address}`
: undefined,
status
? [
CurationStatusFilter.PENDING,
CurationStatusFilter.APPROVED,
CurationStatusFilter.REJECTED,
].includes(status)
? SQL`collection_curations.status = ${status}`
: status === CurationStatusFilter.TO_REVIEW
? SQL`collection_curations.assignee is NULL`
: status === CurationStatusFilter.UNDER_REVIEW
? SQL`collection_curations.assignee is NOT NULL AND collection_curations.status = ${CurationStatusFilter.PENDING}`
: SQL``
: undefined,
].filter(Boolean)

if (!conditions.length) {
return SQL``
}

const result = SQL`WHERE `
conditions.forEach((condition, index) => {
if (condition) {
result.append(condition)
if (conditions[index + 1]) {
result.append(SQL` AND `)
}
}
})

return result
}

static getPublishedJoinStatement(isPublished = false) {
return isPublished
? SQL`JOIN ${raw(
Item.tableName
)} items on items.collection_id = collections.id AND items.blockchain_item_id is NOT NULL`
: SQL``
}

static findAll({
limit = DEFAULT_LIMIT,
offset = 0,
sort,
isPublished,
...whereFilters
}: FindCollectionParams) {
const query = SQL`
SELECT collections.*, COUNT(*) OVER() as collection_count, (SELECT COUNT(*) FROM ${raw(
Item.tableName
)} WHERE items.collection_id = collections.id) as item_count
FROM ${raw(this.tableName)}
`)
FROM ${raw(this.tableName)} collections
${SQL`LEFT JOIN ${raw(
CollectionCuration.tableName
)} collection_curations ON collection_curations.collection_id = collections.id`}
${SQL`${this.getPublishedJoinStatement(isPublished)}`}
${SQL`${this.getFindAllWhereStatement(whereFilters)}`}
${SQL`${this.getOrderByStatement(sort)}`}
LIMIT ${limit}
OFFSET ${offset}
`
return this.query<CollectionWithCounts>(query)
}

static findByAllByAddress(address: string) {
Expand Down
152 changes: 133 additions & 19 deletions src/Collection/Collection.router.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -800,40 +800,154 @@ describe('Collection router', () => {
describe('when retrieving all the collections', () => {
beforeEach(() => {
;(isCommitteeMember as jest.Mock).mockResolvedValueOnce(true)
;(Collection.findAll as jest.Mock)
.mockResolvedValueOnce([dbCollection])
.mockResolvedValueOnce([])
;(Collection.findByContractAddresses as jest.Mock).mockResolvedValueOnce(
[]
)
;(collectionAPI.fetchCollections as jest.Mock).mockResolvedValueOnce([])
thirdPartyAPIMock.fetchThirdParties.mockResolvedValueOnce([])
url = `/collections`
})

it('should respond with all the collections with the URN', () => {
return server
.get(buildURL(url))
.set(createAuthHeaders('get', url))
.expect(200)
.then((response: any) => {
expect(response.body).toEqual({
data: [
{
...resultingCollectionAttributes,
urn: `urn:decentraland:mumbai:collections-v2:${dbCollection.contract_address}`,
describe('and sending pagination params', () => {
let page: number, limit: number
let baseUrl: string
let totalCollectionsFromDb: number
beforeEach(() => {
;(page = 1), (limit = 3)
totalCollectionsFromDb = 1
baseUrl = '/collections'
url = `${baseUrl}?limit=${limit}&page=${page}`
;(Collection.findAll as jest.Mock).mockResolvedValueOnce([
{ ...dbCollection, collection_count: totalCollectionsFromDb },
])
})
it('should respond with pagination data and should have call the findAll method with the params', () => {
return server
.get(buildURL(url))
.set(createAuthHeaders('get', baseUrl))
.expect(200)
.then((response: any) => {
expect(response.body).toEqual({
data: {
total: totalCollectionsFromDb,
pages: totalCollectionsFromDb,
page,
limit,
results: [
{
...resultingCollectionAttributes,
urn: `urn:decentraland:mumbai:collections-v2:${dbCollection.contract_address}`,
},
],
},
],
ok: true,

ok: true,
})
expect(Collection.findAll).toHaveBeenCalledWith({
assignee: undefined,
isPublished: false,
q: undefined,
sort: undefined,
status: undefined,
limit,
offset: page - 1, // it's the offset,
thirdPartyIds: [],
})
})
})
})
})

describe('and sending pagination params plus filtering options', () => {
let page: number,
limit: number,
baseUrl: string,
totalCollectionsFromDb: number,
q: string,
assignee: string,
status: string,
sort: string,
isPublished: string
beforeEach(() => {
;(page = 1), (limit = 3)
assignee = '0x1234567890123456789012345678901234567890'
status = 'published'
sort = 'NAME_DESC'
isPublished = 'true'
q = 'collection name 1'
totalCollectionsFromDb = 1
baseUrl = '/collections'
url = `${baseUrl}?limit=${limit}&page=${page}&assignee=${assignee}&status=${status}&sort=${sort}&is_published=${isPublished}&q=${q}`
;(Collection.findAll as jest.Mock).mockResolvedValueOnce([
{ ...dbCollection, collection_count: totalCollectionsFromDb },
])
})
it('should respond with pagination data and should have call the findAll method with the right params', () => {
return server
.get(buildURL(url))
.set(createAuthHeaders('get', baseUrl))
.expect(200)
.then((response: any) => {
expect(response.body).toEqual({
data: {
total: totalCollectionsFromDb,
pages: totalCollectionsFromDb,
page,
limit,
results: [
{
...resultingCollectionAttributes,
urn: `urn:decentraland:mumbai:collections-v2:${dbCollection.contract_address}`,
},
],
},

ok: true,
})
expect(Collection.findAll).toHaveBeenCalledWith({
q,
assignee,
status,
sort,
isPublished: true,
offset: page - 1, // it's the offset
limit,
thirdPartyIds: [],
})
})
})
})

describe('and not sending any pagination params ', () => {
beforeEach(() => {
url = `/collections`
;(Collection.findAll as jest.Mock)
.mockResolvedValueOnce([dbCollection])
.mockResolvedValueOnce([])
})
it('should respond with all the collections with the URN and the legacy response', () => {
return server
.get(buildURL(url))
.set(createAuthHeaders('get', url))
.expect(200)
.then((response: any) => {
expect(response.body).toEqual({
data: [
{
...resultingCollectionAttributes,
urn: `urn:decentraland:mumbai:collections-v2:${dbCollection.contract_address}`,
},
],
ok: true,
})
})
})
})
})

describe('when retrieving the collections of an address', () => {
beforeEach(() => {
;(Collection.findByAllByAddress as jest.Mock).mockReturnValueOnce([
;(Collection.findAll as jest.Mock).mockReturnValueOnce([
dbCollection,
dbTPCollection,
])
;(Collection.findByContractAddresses as jest.Mock).mockReturnValueOnce([])
;(Collection.findByThirdPartyIds as jest.Mock).mockReturnValueOnce([
Expand Down
Loading

0 comments on commit ea53ea6

Please sign in to comment.