Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: make graphql queries compatible with new Squid #380

Merged
merged 2 commits into from
Aug 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/adapters/handlers/nfts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@ export function createNFTsHandler(
const isLand = params.getBoolean('isLand')
const isOnRent = params.getBoolean('isOnRent')
const isWearableHead = params.getBoolean('isWearableHead')
? params.getString('isWearableHead') === 'true'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is needed to know if it needs to be applied in the query as parameter or not
https://github.com/decentraland/nft-server/pull/380/files#diff-d1debe4191e7ee45f023c53cecc0a2d6c05c4dba8f24d44c3a2c69261102a0bcR310

: undefined
const isWearableAccessory = params.getBoolean('isWearableAccessory')
? params.getString('isWearableAccessory') === 'true'
: undefined
const isWearableSmart = params.getBoolean('isWearableSmart')
const wearableCategory = params.getValue<WearableCategory>(
'wearableCategory',
Expand Down
6 changes: 5 additions & 1 deletion src/adapters/handlers/prices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ export function createPricesHandler(
const category = params.getString('category') as PriceFilterCategory
const assetType = params.getString('assetType') as AssetType
const isWearableHead = params.getBoolean('isWearableHead')
? params.getString('isWearableAccessory') === 'true'
: undefined
const isWearableAccessory = params.getBoolean('isWearableAccessory')
? params.getString('isWearableAccessory') === 'true'
: undefined
const isWearableSmart = params.getBoolean('isWearableSmart')
const wearableCategory = params.getValue<WearableCategory>(
'wearableCategory',
Expand Down Expand Up @@ -81,7 +85,7 @@ export function createPricesHandler(
maxEstateSize,
minEstateSize,
emoteHasGeometry,
emoteHasSound
emoteHasSound,
}),
}),
{
Expand Down
2 changes: 1 addition & 1 deletion src/logic/nfts/collections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ export function fromCollectionsFragment(
break
}
default: {
throw new Error(`Uknown itemType=${fragment.itemType}`)
throw new Error(`Unknown itemType=${fragment.itemType}`)
}
}

Expand Down
76 changes: 53 additions & 23 deletions src/logic/prices/collections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@ import { AssetType, PriceFilters } from '../../ports/prices/types'

const MAX_RESULTS = 1000

const PRICES_ITEMS_FILTERS_DICT: Record<string, string> = {
wearableCategory: '$wearableCategory: String',
emoteCategory: '$emoteCategory: String',
isWearableHead: '$isWearableHead: Boolean',
isWearableAccessory: '$isWearableAccessory: Boolean',
}

const PRICES_FILTERS_DICT: Record<string, string> = {
expiresAt: '$expiresAt: BigInt',
...PRICES_ITEMS_FILTERS_DICT,
}

export function collectionsShouldFetch(filters: PriceFilters) {
const isCorrectNetworkFilter =
!filters.network || !!(filters.network && filters.network === Network.MATIC)
Expand Down Expand Up @@ -72,12 +84,17 @@ export function collectionsNFTsPricesQuery(filters: PriceFilters) {
},
AssetType.NFT
)
return `query NFTPrices(
$expiresAt: String,
$wearableCategory: String
$emoteCategory: String
$isWearableHead: Boolean
$isWearableAccessory: Boolean) {

const variablesBasedOnFilters = Object.entries(filters)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the variables in the GraphQL query must be used somewhere in the query, so this new logic is to put them in dynamically just if they are being used after.

.filter(([key, value]) => value !== undefined && key in PRICES_FILTERS_DICT)
.map(([key]) => (PRICES_FILTERS_DICT[key] ? PRICES_FILTERS_DICT[key] : ''))

const variablesDefinition = variablesBasedOnFilters.length
? `query NFTPrices(${variablesBasedOnFilters.join('\n')})`
: `query NFTPrices`

return `
${variablesDefinition} {
prices: nfts (
first: ${MAX_RESULTS},
orderBy: id,
Expand All @@ -103,12 +120,15 @@ export function collectionsNFTsPricesQueryById(filters: PriceFilters) {
AssetType.NFT
)
return `query NFTPrices(
$lastId: ID,
$expiresAt: String
$wearableCategory: String
$emoteCategory: String
$isWearableHead: Boolean
$isWearableAccessory: Boolean
$lastId: String,
${Object.entries(filters)
.filter(
([key, value]) => value !== undefined && key in PRICES_FILTERS_DICT
)
.map(([key]) =>
PRICES_FILTERS_DICT[key] ? PRICES_FILTERS_DICT[key] : ''
)
.join('\n')}
) {
prices: nfts(
first: ${MAX_RESULTS},
Expand Down Expand Up @@ -141,12 +161,18 @@ export function collectionsItemsPricesQuery(filters: PriceFilters) {
? '[wearable_v1, wearable_v2, smart_wearable_v1]'
: '[emote_v1]'

return `query ItemPrices(
$wearableCategory: String
$emoteCategory: String
$isWearableHead: Boolean
$isWearableAccessory: Boolean
) {
const variablesBasedOnFilters = Object.entries(filters)
.filter(
([key, value]) => value !== undefined && key in PRICES_ITEMS_FILTERS_DICT
)
.map(([key]) => (PRICES_FILTERS_DICT[key] ? PRICES_FILTERS_DICT[key] : ''))

const variablesDefinition = variablesBasedOnFilters.length
? `query ItemPrices(${variablesBasedOnFilters.join('\n')})`
: `query ItemPrices`

return `
${variablesDefinition} {
prices: items (
first: ${MAX_RESULTS},
orderBy: id,
Expand Down Expand Up @@ -179,11 +205,15 @@ export function collectionsItemsPricesQueryById(filters: PriceFilters) {
: '[emote_v1]'

return `query ItemPrices(
$lastId: ID,
$wearableCategory: String
$emoteCategory: String
$isWearableHead: Boolean
$isWearableAccessory: Boolean
$lastId: String,
${Object.entries(filters)
.filter(
([key, value]) => value !== undefined && key in PRICES_FILTERS_DICT
)
.map(([key]) =>
PRICES_FILTERS_DICT[key] ? PRICES_FILTERS_DICT[key] : ''
)
.join('\n')}
) {
prices: items (
first: ${MAX_RESULTS},
Expand Down
44 changes: 30 additions & 14 deletions src/logic/prices/marketplace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ import {

const MAX_RESULTS = 1000

const PRICES_FILTERS_DICT: Record<string, string> = {
expiresAt: '$expiresAt: BigInt',
wearableCategory: '$wearableCategory: String',
emoteCategory: '$emoteCategory: String',
isWearableHead: '$isWearableHead: Boolean',
isWearableAccessory: '$isWearableAccessory: Boolean',
}

export function marketplaceShouldFetch(filters: PriceFilters) {
const isCorrectNetworkFilter =
!filters.network ||
Expand Down Expand Up @@ -96,13 +104,18 @@ export function marketplacePricesQuery(filters: PriceFilters) {
searchEstateSize_gt: 0,
${additionalWheres.join('\n')}
`
return `query NFTPrices(
$expiresAt: String
$expiresAtSec: String
$wearableCategory: String
$isWearableHead: Boolean
$isWearableAccessory: Boolean
) {

const variablesBasedOnFilters = Object.entries(filters)
.filter(([key, value]) => value !== undefined && key in PRICES_FILTERS_DICT)
.map(([key]) => (PRICES_FILTERS_DICT[key] ? PRICES_FILTERS_DICT[key] : ''))
Comment on lines +108 to +110
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about abstracting this mechanism so we can use it in all of these queries?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, great idea, let me prepare that in a following up PR so we can review that exclusively.


const variablesDefinition = variablesBasedOnFilters.length
? `query NFTPrices($expiresAtSec: BigInt, ${variablesBasedOnFilters.join(
'\n'
)})`
: `query NFTPrices($expiresAtSec: BigInt, $expiresAt: BigInt)`

return `${variablesDefinition} {
prices: nfts(
first: ${MAX_RESULTS},
orderBy: tokenId,
Expand Down Expand Up @@ -130,13 +143,16 @@ export function marketplacePricesQueryById(filters: PriceFilters) {
const { category, ...rest } = filters
const additionalWheres: string[] = getExtraWheres(rest)
const categories = getNFTCategoryFromPriceCategory(category)
return `query NFTPrices(
$lastId: ID,
$expiresAt: String
$wearableCategory: String
$isWearableHead: Boolean
$isWearableAccessory: Boolean
) {
const variablesBasedOnFilters = Object.entries(filters)
.filter(([key, value]) => value !== undefined && key in PRICES_FILTERS_DICT)
.map(([key]) => (PRICES_FILTERS_DICT[key] ? PRICES_FILTERS_DICT[key] : ''))

const variablesDefinition = variablesBasedOnFilters.length
? `query NFTPrices($lastId: BigInt, ${variablesBasedOnFilters.join('\n')})`
: `query NFTPrices($lastId: BigInt)`

return `
${variablesDefinition} {
prices: nfts(
orderBy: tokenId,
orderDirection: asc,
Expand Down
4 changes: 2 additions & 2 deletions src/ports/nfts/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ export type NFTResult = {
export type QueryVariables = Omit<NFTFilters, 'sortBy' | 'rentalDays'> & {
orderBy: string
orderDirection: 'asc' | 'desc'
expiresAt: string
expiresAtSec: string
expiresAt?: string
expiresAtSec?: string
}

export interface INFTsComponent {
Expand Down
61 changes: 37 additions & 24 deletions src/ports/nfts/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,16 @@ import { QueryVariables } from './types'

export const NFT_DEFAULT_SORT_BY = NFTSortBy.NEWEST

const NFTS_FILTERS = `
$first: Int
$skip: Int
$orderBy: String
$orderDirection: String
$expiresAt: String
$expiresAtSec: String
$owner: String
$wearableCategory: String
$isWearableHead: Boolean
$isWearableAccessory: Boolean
`
const NFT_FILTERS_DICT: Record<string, string> = {
orderBy: '$orderBy: NFT_orderBy',
orderDirection: '$orderDirection: OrderDirection',
expiresAt: '$expiresAt: BigInt',
expiresAtSec: '$expiresAtSec: BigInt',
owner: '$owner: String',
wearableCategory: '$wearableCategory: String',
isWearableHead: '$isWearableHead: Boolean',
isWearableAccessory: '$isWearableAccessory: Boolean',
}

const getArguments = (total: number) => `
first: ${total}
Expand Down Expand Up @@ -46,14 +44,21 @@ export function getQueryVariables<T>(
const { sortBy, ...variables } = options
const orderBy = getOrderBy(sortBy) as string
const orderDirection = getOrderDirection(sortBy)
const expiresAt = Date.now()
const expiresAtSec = Math.trunc(expiresAt / 1000)
if (options.isOnSale) {
const expiresAt = Date.now()
const expiresAtSec = Math.trunc(expiresAt / 1000)
return {
...variables,
orderBy,
orderDirection,
expiresAt: expiresAt.toString(),
expiresAtSec: expiresAtSec.toString(),
}
}
return {
...variables,
orderBy,
orderDirection,
expiresAt: expiresAt.toString(),
expiresAtSec: expiresAtSec.toString(),
}
}

Expand Down Expand Up @@ -208,7 +213,7 @@ export function getFetchQuery(
}

if (filters.owner) {
where.push('owner: $owner')
where.push('owner_: {address: $owner}')
}

if (
Expand Down Expand Up @@ -302,7 +307,10 @@ export function getFetchQuery(
}

const query = `query NFTs(
${NFTS_FILTERS}
${Object.entries(filters)
.filter(([key, value]) => value !== undefined && key in NFT_FILTERS_DICT)
.map(([key]) => (NFT_FILTERS_DICT[key] ? NFT_FILTERS_DICT[key] : ''))
.join('\n')}
${getExtraVariables ? getExtraVariables(filters).join('\n') : ''}
) {
nfts(
Expand All @@ -312,18 +320,19 @@ export function getFetchQuery(
}
}`

return `
const result = `
${query}
${isCount ? '' : getNFTFragment()}
`
return result
}

export function getFetchOneQuery(
fragmentName: string,
getFragment: () => string
) {
return `
query NFTByTokenId($contractAddress: String, $tokenId: String) {
query NFTByTokenId($contractAddress: String, $tokenId: BigInt) {
nfts(
where: { contractAddress: $contractAddress, tokenId: $tokenId }
first: 1
Expand All @@ -340,7 +349,7 @@ export function getByTokenIdQuery(
getFragment: () => string
) {
return `
query NFTByTokenId($tokenIds: [String!]) {
query NFTByTokenId($tokenIds: [BigInt!]) {
nfts(
where: { id_in: $tokenIds }
first: 1000
Expand All @@ -357,7 +366,11 @@ export function getId(contractAddress: string, tokenId: string) {
}

export function getFuzzySearchQueryForENS(schema: string, searchTerm: string) {
return SQL`SELECT id from `
.append(schema)
.append(SQL`.ens_active WHERE subdomain % ${searchTerm}`)
const query =
SQL`SELECT subdomain, id, similarity(subdomain, ${searchTerm}) AS match_similarity from `
.append(schema)
.append(
SQL`.ens_active WHERE subdomain % ${searchTerm} ORDER BY match_similarity DESC;`
)
return query
}
2 changes: 1 addition & 1 deletion src/ports/prices/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ export function createPricesComponent(options: {
let lastId = ''
let priceFragments: PriceFragment[] = []
while (true) {
const query = getPricesQuery(queryGetter, filters, lastId)
const expiresAt = Date.now()
const expiresAtSec = Math.trunc(expiresAt / 1000)
const queryVariables = {
Expand All @@ -50,6 +49,7 @@ export function createPricesComponent(options: {
expiresAt: expiresAt.toString(),
expiresAtSec: expiresAtSec.toString(),
}
const query = getPricesQuery(queryGetter, queryVariables, lastId)
const { prices: fragments } = await subgraph.query<{
prices: PriceFragment[]
}>(query, queryVariables)
Expand Down
8 changes: 6 additions & 2 deletions src/ports/sales/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ChainId, Sale, SaleFilters, SaleSortBy } from '@dcl/schemas'
import { ChainId, Network, Sale, SaleFilters, SaleSortBy } from '@dcl/schemas'
import { AssetsNetworks } from '../../types'
import { SaleFragment } from './types'

Expand All @@ -10,7 +10,11 @@ export function fromSaleFragment(
chainId: ChainId
): Sale {
const sale: Sale = {
id: 'sale-' + network.toLowerCase() + '-' + fragment.id,
id:
// squid fragments will already have the network as part of the id
fragment.id.includes(Network.ETHEREUM) || fragment.id.includes('POLYGON')
? `sale-${fragment.id.toLowerCase()}`
: 'sale-' + network.toLowerCase() + '-' + fragment.id,
type: fragment.type,
buyer: fragment.buyer,
seller: fragment.seller,
Expand Down
6 changes: 5 additions & 1 deletion src/ports/stats/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,12 @@ export function getEstatesSizesQuery(filters: StatsEstateFilters) {
]`
}

const variables = wrapWhere
? `$lastId: String, $expiresAt: BigInt, $expiresAtSec: BigInt`
: `$lastId: String`

return `
query EstateSizesQuery($lastId: ID, $expiresAt: String, $expiresAtSec: String) {
query EstateSizesQuery(${variables}) {
nfts (
first: ${MAX_RESULTS},
where: {
Expand Down
Loading