From cc59acd286ed8c3db6e6be00aa087f2fa7b7f94e Mon Sep 17 00:00:00 2001 From: Mohsin Riaz Date: Sat, 25 Dec 2021 20:59:41 +0500 Subject: [PATCH 1/7] get seriesdata for owner on getNFTs and getNFT --- src/api/services/gqlQueriesBuilder.ts | 6 +- src/api/services/nft.ts | 156 +++++++++++++++----------- src/api/validators/nftValidators.ts | 6 +- 3 files changed, 101 insertions(+), 67 deletions(-) diff --git a/src/api/services/gqlQueriesBuilder.ts b/src/api/services/gqlQueriesBuilder.ts index 1057eae..fde1fef 100644 --- a/src/api/services/gqlQueriesBuilder.ts +++ b/src/api/services/gqlQueriesBuilder.ts @@ -1,6 +1,7 @@ import { gql } from "graphql-request"; import { convertSortString, LIMIT_MAX_PAGINATION } from "../../utils"; import { getHistoryQuery, getSeriesStatusQuery, NFTBySeriesQuery, NFTQuery, NFTsQuery, statNFTsUserQuery } from "../validators/nftValidators"; +// import L from '../../common/logger'; const nodes = ` nodes { @@ -29,7 +30,6 @@ export class GQLQueriesBuilder { offset: ${query.pagination?.page && query.pagination?.limit ? (query.pagination.page - 1) * query.pagination.limit : 0} filter:{ and:[ - { serieId: { notEqualTo: "Ternoa Xmas 2021" } } ${query.filter?.ids ? `{id: { in: ${JSON.stringify(query.filter.ids.map(x => String(x)))} }}` : ""} ${query.filter?.idsToExclude ? `{id: { notIn: ${JSON.stringify(query.filter.idsToExclude.map(x => String(x)))} }}` : ""} ${query.filter?.idsCategories ? `{id: { in: ${JSON.stringify(query.filter.idsCategories.map(x => String(x)))} }}` : ""} @@ -73,7 +73,6 @@ export class GQLQueriesBuilder { nftEntities( filter: { and: [ - { serieId: { notEqualTo: "Ternoa Xmas 2021" } } { timestampBurn: { isNull: true } } { id: { equalTo: "${query.id}" } } ] @@ -85,6 +84,7 @@ export class GQLQueriesBuilder { `; NFTsForSeries = (query: NFTBySeriesQuery) => { + // L.info(`NFTsForSeries Query gql::${JSON.stringify(query)}`) const nodesSerieData = ` nodes { id @@ -105,9 +105,9 @@ export class GQLQueriesBuilder { ` : ""} filter: { and : [ - { serieId: { notEqualTo: "Ternoa Xmas 2021" } } { timestampBurn: { isNull: true } } { serieId:{ in:${JSON.stringify(query.seriesIds)} } } + ${query.owner ? `{ owner: { equalTo: "${query.owner}" } }` : ""} ] } ) diff --git a/src/api/services/nft.ts b/src/api/services/nft.ts index ab7b6fd..73ea25f 100644 --- a/src/api/services/nft.ts +++ b/src/api/services/nft.ts @@ -14,6 +14,7 @@ import { IUser } from "../../interfaces/IUser"; import CategoryModel from "../../models/category"; import { ICategory } from "../../interfaces/ICategory"; // import { INFTLike } from "../../interfaces/INFTLike"; +import L from "../../common/logger"; const indexerUrl = process.env.INDEXER_URL || "https://indexer.chaos.ternoa.com"; @@ -27,29 +28,29 @@ export class NFTService { try { // const likesData: AggregatePaginateResult<{_id: string, count: number}> | null = null // Categories - if (query.filter?.categories){ + if (query.filter?.categories) { const withNoCategories = query.filter.categories.includes("none") - const categoriesCode = query.filter.categories.filter(x => x!=="none") + const categoriesCode = query.filter.categories.filter(x => x !== "none") const allCategories = await CategoryService.getCategories({}) const categories = allCategories.map(x => x.code).filter(x => categoriesCode.includes(x)) - const mongoQuery = {categories: {$in: categories} } + const mongoQuery = { categories: { $in: categories } } const mongoNfts = await NftModel.find(mongoQuery as any) const nftIds = mongoNfts.map((nft) => nft.chainId) - if (withNoCategories){ + if (withNoCategories) { const categoriesToExclude = allCategories.map(x => x.code).filter(x => !categoriesCode.includes(x)) - const mongoQueryExclude = {$and: [{categories: {$in: categoriesToExclude}}, {chainId: {$nin: nftIds}}]} + const mongoQueryExclude = { $and: [{ categories: { $in: categoriesToExclude } }, { chainId: { $nin: nftIds } }] } const mongoNFTsToExclude = await NftModel.find(mongoQueryExclude as any) const nftIdsToExclude = mongoNFTsToExclude.map((nft) => nft.chainId) query.filter.idsToExcludeCategories = nftIdsToExclude - }else{ + } else { query.filter.idsCategories = nftIds } } // Liked only - if (query.filter?.liked){ + if (query.filter?.liked) { const data = await fetch(`${TERNOA_API_URL}/api/users/${query.filter.liked}?populateLikes=${true}`) const user = await data.json() as IUser - query.filter.series = user.likedNFTs.length > 0 ? user.likedNFTs.map(x=>x.serieId) : [] + query.filter.series = user.likedNFTs.length > 0 ? user.likedNFTs.map(x => x.serieId) : [] } // Sort mongo @@ -74,12 +75,14 @@ export class NFTService { const gqlQuery = QueriesBuilder.NFTs(query); const res: DistinctNFTListResponse = await request(indexerUrl, gqlQuery); const NFTs = res.distinctSerieNfts.nodes; + L.info(`'NFTs length:${NFTs.length}`) // Series Data - const seriesData = await this.getNFTsForSeries({seriesIds: NFTs.map(x => x.serieId)}) + const seriesData = await this.getNFTsForSeriesByOwner({ seriesIds: NFTs.map(x => x.serieId), owner: query?.filter?.owner }) + L.info(seriesData) // Populate res.distinctSerieNfts.nodes = await Promise.all(NFTs.map(async (NFT) => populateNFT(NFT, seriesData, query))) // Result formatting - const result: CustomResponse={ + const result: CustomResponse = { totalCount: res.distinctSerieNfts.totalCount, data: res.distinctSerieNfts.nodes, hasNextPage: res.distinctSerieNfts.pageInfo?.hasNextPage || undefined, @@ -104,26 +107,31 @@ export class NFTService { const result: NFTListResponse = await request(indexerUrl, gqlQuery); let NFT = result.nftEntities.nodes[0]; if (!NFT) throw new Error(); - const seriesData = await this.getNFTsForSeries({seriesIds: [NFT.serieId]}) + let seriesData; + if (query && query.filter && query.filter.owner) { + seriesData = await this.getNFTsForSeriesByOwner({ seriesIds: [NFT.serieId], owner: query.filter.owner }) + } else { + seriesData = await this.getNFTsForSeries({ seriesIds: [NFT.serieId] }) + } NFT = await populateNFT(NFT, seriesData, query); let viewsCount = 0 - if (query.incViews){ + if (query.incViews) { const date = +new Date() - const views = await NftViewModel.find(NFT.serieId!=="0" ? {viewedSerie: NFT.serieId} : {viewedId: query.id}) - if (query.viewerIp && - ( - views.length === 0 || - date - Math.max.apply(null, views.filter(x => x.viewerIp === query.viewerIp).map(x => x.date)) > TIME_BETWEEN_SAME_USER_VIEWS - ) + const views = await NftViewModel.find(NFT.serieId !== "0" ? { viewedSerie: NFT.serieId } : { viewedId: query.id }) + if (query.viewerIp && + ( + views.length === 0 || + date - Math.max.apply(null, views.filter(x => x.viewerIp === query.viewerIp).map(x => x.date)) > TIME_BETWEEN_SAME_USER_VIEWS + ) ) { - const newView = new NftViewModel({viewedSerie: NFT.serieId, viewedId: query.id, viewer: query.viewerWalletId, viewerIp: query.viewerIp, date}) + const newView = new NftViewModel({ viewedSerie: NFT.serieId, viewedId: query.id, viewer: query.viewerWalletId, viewerIp: query.viewerIp, date }) await newView.save(); viewsCount = views.length + 1 - }else{ + } else { viewsCount = views.length } } - return { ...NFT, viewsCount}; + return { ...NFT, viewsCount }; } catch (err) { throw new Error("Couldn't get NFT"); } @@ -134,14 +142,14 @@ export class NFTService { * @param query - query (see statNFTsUserQuery) * @throws Will throw an error if can't request indexer or db or user not find */ - async getStatNFTsUser(query: statNFTsUserQuery): Promise<{ - countOwned: number, - countOwnedListed: number, - countOwnedUnlisted: number, - countCreated: number, - countFollowers: number, + async getStatNFTsUser(query: statNFTsUserQuery): Promise<{ + countOwned: number, + countOwnedListed: number, + countOwnedUnlisted: number, + countCreated: number, + countFollowers: number, countFollowed: number - }> { + }> { try { const [owned, ownedListed, ownedUnlisted, created, followers, followed] = await Promise.all([ request(indexerUrl, QueriesBuilder.countOwnerOwned(query)), @@ -157,7 +165,7 @@ export class NFTService { const countCreated: number = created.nftEntities.totalCount; const countFollowers: number = followers.length const countFollowed: number = followed.length - return {countOwned, countOwnedListed, countOwnedUnlisted, countCreated, countFollowers, countFollowed} + return { countOwned, countOwnedListed, countOwnedUnlisted, countCreated, countFollowers, countFollowed } } catch (err) { throw new Error("Couldn't get users stat"); } @@ -176,13 +184,13 @@ export class NFTService { decryptedCreator === query.creator && decryptedChainIdsString === query.chainIds.join('-') && decryptedCategoriesString === query.categories.join('-') - ){ - const categories = await CategoryService.getCategories({filter: {codes: query.categories}}) + ) { + const categories = await CategoryService.getCategories({ filter: { codes: query.categories } }) const categoriesCodes = categories.map(x => x.code) - const data: {chainId: string, categories: string[]}[] = query.chainIds.map(x => { return {chainId: x, categories: categoriesCodes} }) + const data: { chainId: string, categories: string[] }[] = query.chainIds.map(x => { return { chainId: x, categories: categoriesCodes } }) await NftModel.insertMany(data) return true; - }else{ + } else { throw new Error("Invalid authentication") } } catch (err) { @@ -195,18 +203,40 @@ export class NFTService { * @param query - query (see NFTBySeriesQuery) * @throws Will throw an error if nft ID doesn't exist */ - async getNFTsForSeries(query: NFTBySeriesQuery): Promise>{ - try{ + async getNFTsForSeries(query: NFTBySeriesQuery): Promise> { + try { const gqlQuery = QueriesBuilder.NFTsForSeries(query) const res: NFTListResponse = await request(indexerUrl, gqlQuery); - const result: CustomResponse={ + const result: CustomResponse = { totalCount: res.nftEntities.totalCount, data: res.nftEntities.nodes, hasNextPage: res.nftEntities.pageInfo?.hasNextPage || undefined, hasPreviousPage: res.nftEntities.pageInfo?.hasPreviousPage || undefined } return result - }catch(err){ + } catch (err) { + throw new Error("Couldn't get NFTs for those series"); + } + } + /** + * Finds NFTs with series included in seriesIds array + * @param query - query (see NFTBySeriesQuery) + * @throws Will throw an error if nft ID doesn't exist + */ + async getNFTsForSeriesByOwner(query: NFTBySeriesQuery): Promise> { + try { + L.info(`query::${JSON.stringify(query)}`) + const gqlQuery = QueriesBuilder.NFTsForSeries(query) + const res: NFTListResponse = await request(indexerUrl, gqlQuery); + const result: CustomResponse = { + totalCount: res.nftEntities.totalCount, + data: res.nftEntities.nodes, + hasNextPage: res.nftEntities.pageInfo?.hasNextPage || undefined, + hasPreviousPage: res.nftEntities.pageInfo?.hasPreviousPage || undefined + } + return result + } catch (err) { + L.info(`series error:: ${JSON.stringify(err)}`) throw new Error("Couldn't get NFTs for those series"); } } @@ -220,7 +250,7 @@ export class NFTService { try { const nft = await NftModel.findOne({ chainId: nftId }); if (!nft) return null; - const categories = await CategoryModel.find({code: {$in: nft.categories}}) + const categories = await CategoryModel.find({ code: { $in: nft.categories } }) return categories as ICategory[]; } catch (err) { throw new Error("Couldn't get categories for this NFT"); @@ -232,13 +262,13 @@ export class NFTService { * @param query - query (see getSeriesStatusQuery) * @throws Will throw an error if seriesId is not found */ - async getSeriesStatus(query: getSeriesStatusQuery): Promise{ - try{ + async getSeriesStatus(query: getSeriesStatusQuery): Promise { + try { const gqlQuery = QueriesBuilder.getSeries(query) const res = await request(indexerUrl, gqlQuery); if (!res.serieEntities.nodes || res.serieEntities.nodes.length === 0) throw Error() return res.serieEntities.nodes[0] - }catch(err){ + } catch (err) { throw new Error("Couldn't get series status"); } } @@ -248,44 +278,44 @@ export class NFTService { * @param query - query (see canAddToSeriesQuery) * @throws Will throw an error if seriesId is not found */ - async canAddToSeries(query: canAddToSeriesQuery): Promise{ - try{ - const gqlQuery = QueriesBuilder.getSeries(query) - const res = await request(indexerUrl, gqlQuery); - if (!res.serieEntities.nodes || res.serieEntities.nodes.length === 0) return true - const series:ISeries = res.serieEntities.nodes[0] - if (series.locked || series.owner!==query.walletId) return false - return true - }catch(err){ - throw new Error("Couldn't get information about this series"); - } + async canAddToSeries(query: canAddToSeriesQuery): Promise { + try { + const gqlQuery = QueriesBuilder.getSeries(query) + const res = await request(indexerUrl, gqlQuery); + if (!res.serieEntities.nodes || res.serieEntities.nodes.length === 0) return true + const series: ISeries = res.serieEntities.nodes[0] + if (series.locked || series.owner !== query.walletId) return false + return true + } catch (err) { + throw new Error("Couldn't get information about this series"); } + } /** * Return the history of the serie specified * @param query - query (see getHistoryQuery) * @throws Will throw an error if indexer is not reachable */ - async getHistory(query: getHistoryQuery): Promise>{ - try{ + async getHistory(query: getHistoryQuery): Promise> { + try { const gqlQuery = QueriesBuilder.getHistory(query) const res = await request(indexerUrl, gqlQuery); const data: INFTTransfer[] = [] - if (query.filter?.grouped){ - let previousRow:INFTTransfer = null + if (query.filter?.grouped) { + let previousRow: INFTTransfer = null let tempQty = 1 res.nftTransferEntities.nodes.forEach((x: INFTTransfer) => { const currentRow = x - if (previousRow){ + if (previousRow) { if ( currentRow.from === previousRow.from && - currentRow.to === previousRow.to && + currentRow.to === previousRow.to && currentRow.amount === previousRow.amount && currentRow.seriesId === previousRow.seriesId && currentRow.typeOfTransaction === previousRow.typeOfTransaction - ){ + ) { tempQty += 1 - }else{ + } else { previousRow.quantity = tempQty data.push(previousRow) tempQty = 1 @@ -293,19 +323,19 @@ export class NFTService { } previousRow = currentRow }); - if (previousRow){ + if (previousRow) { previousRow.quantity = tempQty data.push(previousRow) } } - const result: CustomResponse={ + const result: CustomResponse = { totalCount: res.nftTransferEntities.totalCount, data: query.filter?.grouped ? data : res.nftTransferEntities.nodes, hasNextPage: res.nftTransferEntities.pageInfo?.hasNextPage || undefined, hasPreviousPage: res.nftTransferEntities.pageInfo?.hasPreviousPage || undefined } return result - }catch(err){ + } catch (err) { throw new Error("Couldn't get history information about this nft / series"); } } diff --git a/src/api/validators/nftValidators.ts b/src/api/validators/nftValidators.ts index c87d4d0..da97cd5 100644 --- a/src/api/validators/nftValidators.ts +++ b/src/api/validators/nftValidators.ts @@ -72,7 +72,8 @@ export type NFTQuery = { incViews?: boolean filter?: { marketplaceId?: number - noSeriesData?: boolean + noSeriesData?: boolean, + owner?:string, } } export const validationGetNFT = (query: any) => { @@ -84,6 +85,7 @@ export const validationGetNFT = (query: any) => { filter: Joi.object({ marketplaceId: Joi.number().integer().min(0), noSeriesData: Joi.boolean(), + owner:Joi.string(), }), incViews: Joi.boolean(), viewerWalletId: Joi.string(), @@ -119,7 +121,9 @@ export type NFTBySeriesQuery = { page?: number limit?: number } + owner?: string } + export const validationNFTsBySeries = (query: any) => { let { pagination, seriesIds } = query seriesIds = typeof seriesIds === "string" ? [seriesIds] : seriesIds From ba779179ffea38eb4d1e4cf79c0817dac7ae9b79 Mon Sep 17 00:00:00 2001 From: Mohsin Riaz Date: Sat, 25 Dec 2021 21:38:18 +0500 Subject: [PATCH 2/7] removed extra logs --- src/api/services/nft.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/api/services/nft.ts b/src/api/services/nft.ts index 73ea25f..0b22e3e 100644 --- a/src/api/services/nft.ts +++ b/src/api/services/nft.ts @@ -75,10 +75,8 @@ export class NFTService { const gqlQuery = QueriesBuilder.NFTs(query); const res: DistinctNFTListResponse = await request(indexerUrl, gqlQuery); const NFTs = res.distinctSerieNfts.nodes; - L.info(`'NFTs length:${NFTs.length}`) // Series Data const seriesData = await this.getNFTsForSeriesByOwner({ seriesIds: NFTs.map(x => x.serieId), owner: query?.filter?.owner }) - L.info(seriesData) // Populate res.distinctSerieNfts.nodes = await Promise.all(NFTs.map(async (NFT) => populateNFT(NFT, seriesData, query))) // Result formatting @@ -225,7 +223,6 @@ export class NFTService { */ async getNFTsForSeriesByOwner(query: NFTBySeriesQuery): Promise> { try { - L.info(`query::${JSON.stringify(query)}`) const gqlQuery = QueriesBuilder.NFTsForSeries(query) const res: NFTListResponse = await request(indexerUrl, gqlQuery); const result: CustomResponse = { @@ -236,7 +233,7 @@ export class NFTService { } return result } catch (err) { - L.info(`series error:: ${JSON.stringify(err)}`) + L.info(`getNFTsForSeriesByOwner error:: ${JSON.stringify(err)}`) throw new Error("Couldn't get NFTs for those series"); } } From 2ce918482b898ce002adfd3d57ead269b2c354bf Mon Sep 17 00:00:00 2001 From: "ghalielouarzazi@hotmail.com" Date: Tue, 28 Dec 2021 08:45:40 +0100 Subject: [PATCH 3/7] separate seriesData from NFTs request, count nfts stats directly from indexer --- src/api/helpers/nftHelpers.ts | 71 +++----------- src/api/services/gqlQueriesBuilder.ts | 127 ++++++++++++++++++++++++-- src/api/services/nft.ts | 88 +++++++++++------- src/api/validators/nftValidators.ts | 20 ++-- 4 files changed, 199 insertions(+), 107 deletions(-) diff --git a/src/api/helpers/nftHelpers.ts b/src/api/helpers/nftHelpers.ts index c159c18..55bcdc7 100644 --- a/src/api/helpers/nftHelpers.ts +++ b/src/api/helpers/nftHelpers.ts @@ -24,83 +24,38 @@ const ipfsGatewayUri = */ export async function populateNFT( NFT: INFT, - seriesData: CustomResponse, query: NFTsQuery ): Promise { - const [serieData, creatorData, ownerData, info, categories, seriesLocked] = + const [stat, creatorData, ownerData, info, categories, seriesLocked] = await Promise.all([ - populateSerieData(NFT, seriesData, query), + populateStat(NFT, query), populateNFTCreator(NFT), populateNFTOwner(NFT), populateNFTIpfs(NFT), populateNFTCategories(NFT), populateNFTSeriesObject(NFT.serieId) ]); - return { ...NFT, ...serieData, creatorData, ownerData, ...info, categories, seriesLocked}; + return { ...NFT, ...stat, creatorData, ownerData, ...info, categories, seriesLocked}; } -export async function populateSerieData( +export async function populateStat( NFT: INFT, - seriesData: CustomResponse, query: NFTsQuery ): Promise<{ - serieData: INFT[]; - totalNft: number; - totalListedNft: number; - totalListedInMarketplace: number; - totalOwnedByRequestingUser: number; - totalOwnedListedByRequestingUser: number; - smallestPrice: string; + totalNft: number, + totalListedNft: number, + totalListedInMarketplace: number, + totalOwnedByRequestingUser: number, + totalOwnedListedByRequestingUser: number, + smallestPrice: string }> { try { const marketplaceId = query.filter?.marketplaceId; const owner = query.filter?.owner; - if (NFT.serieId === "0") - return { - serieData: [ - { - id: NFT.id, - owner: NFT.owner, - listed: NFT.listed, - price: NFT.price, - marketplaceId: NFT.marketplaceId, - }, - ], - totalNft: 1, - totalListedNft: NFT.listed, - totalListedInMarketplace: NFT.listed, - totalOwnedByRequestingUser: 1, - totalOwnedListedByRequestingUser: NFT.listed, - smallestPrice: NFT.price, - }; - const result = seriesData.data.filter((x) => x.serieId === NFT.serieId).map(({serieId, ...rest}) => rest); - const serieData = result.sort( - (a, b) => - (a.isCapsule !== b.isCapsule && (a.isCapsule ? 1 : -1)) || // capsule last - b.listed - a.listed || // listed first - (marketplaceId && Number(a.marketplaceId) !== Number(b.marketplaceId) && (Number(a.marketplaceId) === marketplaceId || Number(b.marketplaceId) === marketplaceId) && - marketplaceId === Number(a.marketplaceId) ? -1 : 1) || // marketplace id corresponding to request first - Number(a.price) - Number(b.price) // smallest price first - ); - const listedNft = serieData.filter((x) => x.listed); - return { - serieData: !query.filter?.noSeriesData ? serieData : [], - totalNft: serieData.length, - totalListedNft: listedNft.length, - totalListedInMarketplace: marketplaceId - ? listedNft.filter((x) => Number(x.marketplaceId) === marketplaceId) - .length - : listedNft.length, - totalOwnedByRequestingUser: owner - ? serieData.filter((x) => x.owner === owner).length - : 0, - totalOwnedListedByRequestingUser: owner - ? listedNft.filter((x) => x.owner === owner).length - : 0, - smallestPrice: serieData.length > 0 ? serieData[0].price : NFT.price - }; + const stat = await NFTService.getStatNFT(NFT.serieId, marketplaceId, owner) + return stat } catch (err) { - L.error({ err }, "NFTs with same serie could not have been fetched"); + L.error({ err }, "NFTs stats could not have been fetched"); return null; } } diff --git a/src/api/services/gqlQueriesBuilder.ts b/src/api/services/gqlQueriesBuilder.ts index fde1fef..d334b2d 100644 --- a/src/api/services/gqlQueriesBuilder.ts +++ b/src/api/services/gqlQueriesBuilder.ts @@ -84,7 +84,6 @@ export class GQLQueriesBuilder { `; NFTsForSeries = (query: NFTBySeriesQuery) => { - // L.info(`NFTsForSeries Query gql::${JSON.stringify(query)}`) const nodesSerieData = ` nodes { id @@ -92,7 +91,6 @@ export class GQLQueriesBuilder { listed price marketplaceId - serieId isCapsule } `; @@ -107,9 +105,10 @@ export class GQLQueriesBuilder { and : [ { timestampBurn: { isNull: true } } { serieId:{ in:${JSON.stringify(query.seriesIds)} } } - ${query.owner ? `{ owner: { equalTo: "${query.owner}" } }` : ""} + ${query.filter?.owner ? `{ owner: { equalTo: "${query.filter.owner}" } }` : ""} ] } + orderBy: [IS_CAPSULE_ASC, LISTED_DESC] ) { totalCount @@ -128,7 +127,6 @@ export class GQLQueriesBuilder { nftEntities( filter: { and: [ - { serieId: { notEqualTo: "Ternoa Xmas 2021" } } { timestampBurn: { isNull: true } } { owner: { equalTo: "${query.id}" } } ] @@ -143,7 +141,6 @@ export class GQLQueriesBuilder { nftEntities( filter: { and: [ - { serieId: { notEqualTo: "Ternoa Xmas 2021" } } { timestampBurn: { isNull: true } } ${query.filter?.marketplaceId ? `{ marketplaceId: { equalTo: "${query.filter.marketplaceId}"} }` : ""} { owner: { equalTo: "${query.id}" } } @@ -161,7 +158,6 @@ export class GQLQueriesBuilder { nftEntities( filter: { and: [ - { serieId: { notEqualTo: "Ternoa Xmas 2021" } } { timestampBurn: { isNull: true } } { owner: { equalTo: "${query.id}" } } {listed: { equalTo: 0}} @@ -178,7 +174,6 @@ export class GQLQueriesBuilder { nftEntities( filter: { and: [ - { serieId: { notEqualTo: "Ternoa Xmas 2021" } } { timestampBurn: { isNull: true } } { creator: { equalTo: "${query.id}" } } ] @@ -276,6 +271,124 @@ export class GQLQueriesBuilder { } `; +countTotal = (seriesId: string) => gql` + { + nftEntities( + filter: { + and: [ + { timestampBurn: { isNull: true } } + { serieId: { equalTo: "${seriesId}" } } + ] + } + ) { + totalCount + } + } +`; + +countTotalListed = (seriesId: string) => gql` + { + nftEntities( + filter: { + and: [ + { timestampBurn: { isNull: true } } + { serieId: { equalTo: "${seriesId}" } } + { listed: { equalTo: 1} } + ] + } + ) { + totalCount + } + } +`; + +countTotalListedInMarketplace = (seriesId: string, marketplaceId: number) => gql` + { + nftEntities( + filter: { + and: [ + { timestampBurn: { isNull: true } } + { serieId: { equalTo: "${seriesId}" } } + { listed: { equalTo: 1} } + { marketplaceId: { equalTo: "${marketplaceId}" } } + ] + } + ) { + totalCount + } + } +`; + +countTotalOwned = (seriesId: string, owner: string) => gql` + { + nftEntities( + filter: { + and: [ + { timestampBurn: { isNull: true } } + { serieId: { equalTo: "${seriesId}" } } + { owner: { equalTo: "${owner}" } } + ] + } + ) { + totalCount + } + } +`; + +countTotalOwnedListed = (seriesId: string, owner: string) => gql` + { + nftEntities( + filter: { + and: [ + { timestampBurn: { isNull: true } } + { serieId: { equalTo: "${seriesId}" } } + { listed: { equalTo: 1} } + { owner: { equalTo: "${owner}" } } + ] + } + ) { + totalCount + } + } +`; + +countTotalOwnedListedInMarketplace = (seriesId: string, owner: string, marketplaceId: number) => gql` + { + nftEntities( + filter: { + and: [ + { timestampBurn: { isNull: true } } + { serieId: { equalTo: "${seriesId}" } } + { listed: { equalTo: 1} } + { owner: { equalTo: "${owner}" } } + { marketplaceId: { equalTo: "${marketplaceId}" } } + ] + } + ) { + totalCount + } + } +`; + +countSmallestPrice = (seriesId: string, marketplaceId: number=null) => gql` + { + nftEntities( + filter: { + and: [ + { timestampBurn: { isNull: true } } + { serieId: { equalTo: "${seriesId}" } } + { listed: { equalTo: 1} } + ${marketplaceId ? `{ marketplaceId: {equalTo: ${marketplaceId}} }` : ``} + ] + } + ) { + nodes{ + price + } + } + } +`; + } export default new GQLQueriesBuilder(); diff --git a/src/api/services/nft.ts b/src/api/services/nft.ts index 0b22e3e..5774058 100644 --- a/src/api/services/nft.ts +++ b/src/api/services/nft.ts @@ -26,7 +26,6 @@ export class NFTService { */ async getNFTs(query: NFTsQuery): Promise> { try { - // const likesData: AggregatePaginateResult<{_id: string, count: number}> | null = null // Categories if (query.filter?.categories) { const withNoCategories = query.filter.categories.includes("none") @@ -53,7 +52,8 @@ export class NFTService { query.filter.series = user.likedNFTs.length > 0 ? user.likedNFTs.map(x => x.serieId) : [] } - // Sort mongo + // Sort mongo -> BLOCKED CAUSE OF DATASTRUCTURE + // const likesData: AggregatePaginateResult<{_id: string, count: number}> | null = null /*if (query.sortMongo){ const sortArray = query.sortMongo.split(',') const sortByLikes = sortArray.find(x => x.split(':')[0] === "likes") @@ -75,10 +75,8 @@ export class NFTService { const gqlQuery = QueriesBuilder.NFTs(query); const res: DistinctNFTListResponse = await request(indexerUrl, gqlQuery); const NFTs = res.distinctSerieNfts.nodes; - // Series Data - const seriesData = await this.getNFTsForSeriesByOwner({ seriesIds: NFTs.map(x => x.serieId), owner: query?.filter?.owner }) // Populate - res.distinctSerieNfts.nodes = await Promise.all(NFTs.map(async (NFT) => populateNFT(NFT, seriesData, query))) + res.distinctSerieNfts.nodes = await Promise.all(NFTs.map(async (NFT) => populateNFT(NFT, query))) // Result formatting const result: CustomResponse = { totalCount: res.distinctSerieNfts.totalCount, @@ -105,13 +103,7 @@ export class NFTService { const result: NFTListResponse = await request(indexerUrl, gqlQuery); let NFT = result.nftEntities.nodes[0]; if (!NFT) throw new Error(); - let seriesData; - if (query && query.filter && query.filter.owner) { - seriesData = await this.getNFTsForSeriesByOwner({ seriesIds: [NFT.serieId], owner: query.filter.owner }) - } else { - seriesData = await this.getNFTsForSeries({ seriesIds: [NFT.serieId] }) - } - NFT = await populateNFT(NFT, seriesData, query); + NFT = await populateNFT(NFT, query); let viewsCount = 0 if (query.incViews) { const date = +new Date() @@ -169,6 +161,49 @@ export class NFTService { } } + /** + * Gets nft stat (total, total listed, listed, total listed on marketplace, owned by user, owned by user listed, smallest price) + * @param seriesId - Series Id of nft to get stat for + * @param marketplaceId - marketplace id (optional) + * @param owner - owner (optional) + * @throws Will throw an error if can't request indexer + */ + async getStatNFT(seriesId:string, marketplaceId:number=null, owner:string=null): Promise<{ + totalNft: number, + totalListedNft: number, + totalListedInMarketplace: number, + totalOwnedByRequestingUser: number, + totalOwnedListedByRequestingUser: number, + totalOwnedListedInMarketplaceByRequestingUser: number, + smallestPrice: string + }> { + try { + const [totalRequest, totalListedRequest, totalListedInMarketplaceRequest, totalOwnedByRequestingUserRequest, totalOwnedListedByRequestingUserRequest, totalOwnedListedInMarketplaceByRequestingUserRequest, smallestPriceRequest] = await Promise.all([ + request(indexerUrl, QueriesBuilder.countTotal(seriesId)), + request(indexerUrl, QueriesBuilder.countTotalListed(seriesId)), + marketplaceId!==null ? request(indexerUrl, QueriesBuilder.countTotalListedInMarketplace(seriesId, marketplaceId)) : 0, + owner ? request(indexerUrl, QueriesBuilder.countTotalOwned(seriesId, owner)) : null, + owner ? request(indexerUrl, QueriesBuilder.countTotalOwnedListed(seriesId, owner)) : null, + owner && marketplaceId!==null ? request(indexerUrl, QueriesBuilder.countTotalOwnedListedInMarketplace(seriesId, owner, marketplaceId)) : null, + request(indexerUrl, QueriesBuilder.countSmallestPrice(seriesId, marketplaceId)), + ]) + const totalNft: number = totalRequest.nftEntities.totalCount; + const totalListedNft: number = totalListedRequest.nftEntities.totalCount; + const totalListedInMarketplace: number = totalListedInMarketplaceRequest ? totalListedInMarketplaceRequest.nftEntities.totalCount : 0; + const totalOwnedByRequestingUser: number = totalOwnedByRequestingUserRequest ? totalOwnedByRequestingUserRequest.nftEntities.totalCount : 0; + const totalOwnedListedByRequestingUser: number = totalOwnedListedByRequestingUserRequest ? totalOwnedListedByRequestingUserRequest.nftEntities.totalCount : 0; + const totalOwnedListedInMarketplaceByRequestingUser: number = totalOwnedListedInMarketplaceByRequestingUserRequest ? totalOwnedListedInMarketplaceByRequestingUserRequest.nftEntities.totalCount : 0; + const smallestPrice: string = smallestPriceRequest.nftEntities.nodes.length > 0 ? + smallestPriceRequest.nftEntities.nodes.sort((a:INFT,b:INFT) => Number(a.price) - Number(b.price))[0].price + : + "0" + ; + return { totalNft, totalListedNft, totalListedInMarketplace, totalOwnedByRequestingUser, totalOwnedListedByRequestingUser, totalOwnedListedInMarketplaceByRequestingUser, smallestPrice } + } catch (err) { + throw new Error("Couldn't get NFT stat"); + } + } + /** * Creates a new nft document in DB (for offchain categories) * @param query - query (see addCategoriesNFTsQuery) @@ -203,37 +238,22 @@ export class NFTService { */ async getNFTsForSeries(query: NFTBySeriesQuery): Promise> { try { + const marketplaceId = query.filter?.marketplaceId; const gqlQuery = QueriesBuilder.NFTsForSeries(query) const res: NFTListResponse = await request(indexerUrl, gqlQuery); + const seriesData = res.nftEntities.nodes.sort((a, b) => + (marketplaceId && Number(a.marketplaceId) !== Number(b.marketplaceId) && (Number(a.marketplaceId) === marketplaceId || Number(b.marketplaceId) === marketplaceId) && + marketplaceId === Number(a.marketplaceId) ? -1 : 1) || // marketplace id corresponding to request first + Number(a.price) - Number(b.price) // smallest price first + ) const result: CustomResponse = { totalCount: res.nftEntities.totalCount, - data: res.nftEntities.nodes, - hasNextPage: res.nftEntities.pageInfo?.hasNextPage || undefined, - hasPreviousPage: res.nftEntities.pageInfo?.hasPreviousPage || undefined - } - return result - } catch (err) { - throw new Error("Couldn't get NFTs for those series"); - } - } - /** - * Finds NFTs with series included in seriesIds array - * @param query - query (see NFTBySeriesQuery) - * @throws Will throw an error if nft ID doesn't exist - */ - async getNFTsForSeriesByOwner(query: NFTBySeriesQuery): Promise> { - try { - const gqlQuery = QueriesBuilder.NFTsForSeries(query) - const res: NFTListResponse = await request(indexerUrl, gqlQuery); - const result: CustomResponse = { - totalCount: res.nftEntities.totalCount, - data: res.nftEntities.nodes, + data: seriesData, hasNextPage: res.nftEntities.pageInfo?.hasNextPage || undefined, hasPreviousPage: res.nftEntities.pageInfo?.hasPreviousPage || undefined } return result } catch (err) { - L.info(`getNFTsForSeriesByOwner error:: ${JSON.stringify(err)}`) throw new Error("Couldn't get NFTs for those series"); } } diff --git a/src/api/validators/nftValidators.ts b/src/api/validators/nftValidators.ts index da97cd5..fd6dff1 100644 --- a/src/api/validators/nftValidators.ts +++ b/src/api/validators/nftValidators.ts @@ -27,7 +27,6 @@ export type NFTsQuery = { priceTiimeFilter?: string seriesLocked?: boolean isCapsule?: boolean - noSeriesData?: boolean } } export const validationGetNFTs = (query: any) => { @@ -58,7 +57,6 @@ export const validationGetNFTs = (query: any) => { priceTiimeFilter: Joi.string(), seriesLocked: Joi.boolean(), isCapsule: Joi.boolean(), - noSeriesData: Joi.boolean(), }), }) return validateQuery(validationSchema, { pagination, sort, sortMongo, filter }) as NFTsQuery; @@ -72,7 +70,6 @@ export type NFTQuery = { incViews?: boolean filter?: { marketplaceId?: number - noSeriesData?: boolean, owner?:string, } } @@ -84,7 +81,6 @@ export const validationGetNFT = (query: any) => { id: Joi.number().required().integer().min(0), filter: Joi.object({ marketplaceId: Joi.number().integer().min(0), - noSeriesData: Joi.boolean(), owner:Joi.string(), }), incViews: Joi.boolean(), @@ -121,21 +117,29 @@ export type NFTBySeriesQuery = { page?: number limit?: number } - owner?: string + filter?:{ + owner?: string + marketplaceId?: number + } } export const validationNFTsBySeries = (query: any) => { - let { pagination, seriesIds } = query + let { pagination, seriesIds, filter } = query seriesIds = typeof seriesIds === "string" ? [seriesIds] : seriesIds if (pagination) pagination = JSON.parse(pagination); + if (filter) filter = JSON.parse(filter); const validationSchema = Joi.object({ seriesIds: Joi.array().required().items(Joi.string().required()), pagination: Joi.object({ page: Joi.number().integer().min(0), limit: Joi.number().integer().min(0).max(LIMIT_MAX_PAGINATION), - }) + }), + filter: Joi.object({ + owner: Joi.string(), + marketplaceId: Joi.number().integer(), + }), }) - return validateQuery(validationSchema, { pagination, seriesIds }) as NFTBySeriesQuery; + return validateQuery(validationSchema, { pagination, seriesIds, filter }) as NFTBySeriesQuery; } From 4fd04bb84a868a3e05732e87b564261c5f624905 Mon Sep 17 00:00:00 2001 From: Mohsin Riaz Date: Fri, 7 Jan 2022 00:16:19 +0500 Subject: [PATCH 4/7] Hide wrong PS2 NFTs --- src/api/services/gqlQueriesBuilder.ts | 56 +++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/src/api/services/gqlQueriesBuilder.ts b/src/api/services/gqlQueriesBuilder.ts index 1057eae..1de7faa 100644 --- a/src/api/services/gqlQueriesBuilder.ts +++ b/src/api/services/gqlQueriesBuilder.ts @@ -30,6 +30,14 @@ export class GQLQueriesBuilder { filter:{ and:[ { serieId: { notEqualTo: "Ternoa Xmas 2021" } } + { serieId: { notEqualTo: "Ternoa Private - 1" } } + { serieId: { notEqualTo: "Ternoa Private - 2" } } + { serieId: { notEqualTo: "Ternoa Private - 3" } } + { serieId: { notEqualTo: "Ternoa Private - 4" } } + { serieId: { notEqualTo: "Ternoa Private - 5" } } + { serieId: { notEqualTo: "Ternoa Private - 6" } } + { serieId: { notEqualTo: "Ternoa Private - 7" } } + { serieId: { notEqualTo: "Ternoa Private - 8" } } ${query.filter?.ids ? `{id: { in: ${JSON.stringify(query.filter.ids.map(x => String(x)))} }}` : ""} ${query.filter?.idsToExclude ? `{id: { notIn: ${JSON.stringify(query.filter.idsToExclude.map(x => String(x)))} }}` : ""} ${query.filter?.idsCategories ? `{id: { in: ${JSON.stringify(query.filter.idsCategories.map(x => String(x)))} }}` : ""} @@ -74,6 +82,14 @@ export class GQLQueriesBuilder { filter: { and: [ { serieId: { notEqualTo: "Ternoa Xmas 2021" } } + { serieId: { notEqualTo: "Ternoa Private - 1" } } + { serieId: { notEqualTo: "Ternoa Private - 2" } } + { serieId: { notEqualTo: "Ternoa Private - 3" } } + { serieId: { notEqualTo: "Ternoa Private - 4" } } + { serieId: { notEqualTo: "Ternoa Private - 5" } } + { serieId: { notEqualTo: "Ternoa Private - 6" } } + { serieId: { notEqualTo: "Ternoa Private - 7" } } + { serieId: { notEqualTo: "Ternoa Private - 8" } } { timestampBurn: { isNull: true } } { id: { equalTo: "${query.id}" } } ] @@ -106,6 +122,14 @@ export class GQLQueriesBuilder { filter: { and : [ { serieId: { notEqualTo: "Ternoa Xmas 2021" } } + { serieId: { notEqualTo: "Ternoa Private - 1" } } + { serieId: { notEqualTo: "Ternoa Private - 2" } } + { serieId: { notEqualTo: "Ternoa Private - 3" } } + { serieId: { notEqualTo: "Ternoa Private - 4" } } + { serieId: { notEqualTo: "Ternoa Private - 5" } } + { serieId: { notEqualTo: "Ternoa Private - 6" } } + { serieId: { notEqualTo: "Ternoa Private - 7" } } + { serieId: { notEqualTo: "Ternoa Private - 8" } } { timestampBurn: { isNull: true } } { serieId:{ in:${JSON.stringify(query.seriesIds)} } } ] @@ -129,6 +153,14 @@ export class GQLQueriesBuilder { filter: { and: [ { serieId: { notEqualTo: "Ternoa Xmas 2021" } } + { serieId: { notEqualTo: "Ternoa Private - 1" } } + { serieId: { notEqualTo: "Ternoa Private - 2" } } + { serieId: { notEqualTo: "Ternoa Private - 3" } } + { serieId: { notEqualTo: "Ternoa Private - 4" } } + { serieId: { notEqualTo: "Ternoa Private - 5" } } + { serieId: { notEqualTo: "Ternoa Private - 6" } } + { serieId: { notEqualTo: "Ternoa Private - 7" } } + { serieId: { notEqualTo: "Ternoa Private - 8" } } { timestampBurn: { isNull: true } } { owner: { equalTo: "${query.id}" } } ] @@ -144,6 +176,14 @@ export class GQLQueriesBuilder { filter: { and: [ { serieId: { notEqualTo: "Ternoa Xmas 2021" } } + { serieId: { notEqualTo: "Ternoa Private - 1" } } + { serieId: { notEqualTo: "Ternoa Private - 2" } } + { serieId: { notEqualTo: "Ternoa Private - 3" } } + { serieId: { notEqualTo: "Ternoa Private - 4" } } + { serieId: { notEqualTo: "Ternoa Private - 5" } } + { serieId: { notEqualTo: "Ternoa Private - 6" } } + { serieId: { notEqualTo: "Ternoa Private - 7" } } + { serieId: { notEqualTo: "Ternoa Private - 8" } } { timestampBurn: { isNull: true } } ${query.filter?.marketplaceId ? `{ marketplaceId: { equalTo: "${query.filter.marketplaceId}"} }` : ""} { owner: { equalTo: "${query.id}" } } @@ -162,6 +202,14 @@ export class GQLQueriesBuilder { filter: { and: [ { serieId: { notEqualTo: "Ternoa Xmas 2021" } } + { serieId: { notEqualTo: "Ternoa Private - 1" } } + { serieId: { notEqualTo: "Ternoa Private - 2" } } + { serieId: { notEqualTo: "Ternoa Private - 3" } } + { serieId: { notEqualTo: "Ternoa Private - 4" } } + { serieId: { notEqualTo: "Ternoa Private - 5" } } + { serieId: { notEqualTo: "Ternoa Private - 6" } } + { serieId: { notEqualTo: "Ternoa Private - 7" } } + { serieId: { notEqualTo: "Ternoa Private - 8" } } { timestampBurn: { isNull: true } } { owner: { equalTo: "${query.id}" } } {listed: { equalTo: 0}} @@ -179,6 +227,14 @@ export class GQLQueriesBuilder { filter: { and: [ { serieId: { notEqualTo: "Ternoa Xmas 2021" } } + { serieId: { notEqualTo: "Ternoa Private - 1" } } + { serieId: { notEqualTo: "Ternoa Private - 2" } } + { serieId: { notEqualTo: "Ternoa Private - 3" } } + { serieId: { notEqualTo: "Ternoa Private - 4" } } + { serieId: { notEqualTo: "Ternoa Private - 5" } } + { serieId: { notEqualTo: "Ternoa Private - 6" } } + { serieId: { notEqualTo: "Ternoa Private - 7" } } + { serieId: { notEqualTo: "Ternoa Private - 8" } } { timestampBurn: { isNull: true } } { creator: { equalTo: "${query.id}" } } ] From a0a1ca78b865bbf7a2b48dfce16abe25b869ac60 Mon Sep 17 00:00:00 2001 From: Mohsin Riaz Date: Fri, 7 Jan 2022 00:26:17 +0500 Subject: [PATCH 5/7] shown ps2 nfts again --- src/api/services/gqlQueriesBuilder.ts | 56 --------------------------- 1 file changed, 56 deletions(-) diff --git a/src/api/services/gqlQueriesBuilder.ts b/src/api/services/gqlQueriesBuilder.ts index 1de7faa..1057eae 100644 --- a/src/api/services/gqlQueriesBuilder.ts +++ b/src/api/services/gqlQueriesBuilder.ts @@ -30,14 +30,6 @@ export class GQLQueriesBuilder { filter:{ and:[ { serieId: { notEqualTo: "Ternoa Xmas 2021" } } - { serieId: { notEqualTo: "Ternoa Private - 1" } } - { serieId: { notEqualTo: "Ternoa Private - 2" } } - { serieId: { notEqualTo: "Ternoa Private - 3" } } - { serieId: { notEqualTo: "Ternoa Private - 4" } } - { serieId: { notEqualTo: "Ternoa Private - 5" } } - { serieId: { notEqualTo: "Ternoa Private - 6" } } - { serieId: { notEqualTo: "Ternoa Private - 7" } } - { serieId: { notEqualTo: "Ternoa Private - 8" } } ${query.filter?.ids ? `{id: { in: ${JSON.stringify(query.filter.ids.map(x => String(x)))} }}` : ""} ${query.filter?.idsToExclude ? `{id: { notIn: ${JSON.stringify(query.filter.idsToExclude.map(x => String(x)))} }}` : ""} ${query.filter?.idsCategories ? `{id: { in: ${JSON.stringify(query.filter.idsCategories.map(x => String(x)))} }}` : ""} @@ -82,14 +74,6 @@ export class GQLQueriesBuilder { filter: { and: [ { serieId: { notEqualTo: "Ternoa Xmas 2021" } } - { serieId: { notEqualTo: "Ternoa Private - 1" } } - { serieId: { notEqualTo: "Ternoa Private - 2" } } - { serieId: { notEqualTo: "Ternoa Private - 3" } } - { serieId: { notEqualTo: "Ternoa Private - 4" } } - { serieId: { notEqualTo: "Ternoa Private - 5" } } - { serieId: { notEqualTo: "Ternoa Private - 6" } } - { serieId: { notEqualTo: "Ternoa Private - 7" } } - { serieId: { notEqualTo: "Ternoa Private - 8" } } { timestampBurn: { isNull: true } } { id: { equalTo: "${query.id}" } } ] @@ -122,14 +106,6 @@ export class GQLQueriesBuilder { filter: { and : [ { serieId: { notEqualTo: "Ternoa Xmas 2021" } } - { serieId: { notEqualTo: "Ternoa Private - 1" } } - { serieId: { notEqualTo: "Ternoa Private - 2" } } - { serieId: { notEqualTo: "Ternoa Private - 3" } } - { serieId: { notEqualTo: "Ternoa Private - 4" } } - { serieId: { notEqualTo: "Ternoa Private - 5" } } - { serieId: { notEqualTo: "Ternoa Private - 6" } } - { serieId: { notEqualTo: "Ternoa Private - 7" } } - { serieId: { notEqualTo: "Ternoa Private - 8" } } { timestampBurn: { isNull: true } } { serieId:{ in:${JSON.stringify(query.seriesIds)} } } ] @@ -153,14 +129,6 @@ export class GQLQueriesBuilder { filter: { and: [ { serieId: { notEqualTo: "Ternoa Xmas 2021" } } - { serieId: { notEqualTo: "Ternoa Private - 1" } } - { serieId: { notEqualTo: "Ternoa Private - 2" } } - { serieId: { notEqualTo: "Ternoa Private - 3" } } - { serieId: { notEqualTo: "Ternoa Private - 4" } } - { serieId: { notEqualTo: "Ternoa Private - 5" } } - { serieId: { notEqualTo: "Ternoa Private - 6" } } - { serieId: { notEqualTo: "Ternoa Private - 7" } } - { serieId: { notEqualTo: "Ternoa Private - 8" } } { timestampBurn: { isNull: true } } { owner: { equalTo: "${query.id}" } } ] @@ -176,14 +144,6 @@ export class GQLQueriesBuilder { filter: { and: [ { serieId: { notEqualTo: "Ternoa Xmas 2021" } } - { serieId: { notEqualTo: "Ternoa Private - 1" } } - { serieId: { notEqualTo: "Ternoa Private - 2" } } - { serieId: { notEqualTo: "Ternoa Private - 3" } } - { serieId: { notEqualTo: "Ternoa Private - 4" } } - { serieId: { notEqualTo: "Ternoa Private - 5" } } - { serieId: { notEqualTo: "Ternoa Private - 6" } } - { serieId: { notEqualTo: "Ternoa Private - 7" } } - { serieId: { notEqualTo: "Ternoa Private - 8" } } { timestampBurn: { isNull: true } } ${query.filter?.marketplaceId ? `{ marketplaceId: { equalTo: "${query.filter.marketplaceId}"} }` : ""} { owner: { equalTo: "${query.id}" } } @@ -202,14 +162,6 @@ export class GQLQueriesBuilder { filter: { and: [ { serieId: { notEqualTo: "Ternoa Xmas 2021" } } - { serieId: { notEqualTo: "Ternoa Private - 1" } } - { serieId: { notEqualTo: "Ternoa Private - 2" } } - { serieId: { notEqualTo: "Ternoa Private - 3" } } - { serieId: { notEqualTo: "Ternoa Private - 4" } } - { serieId: { notEqualTo: "Ternoa Private - 5" } } - { serieId: { notEqualTo: "Ternoa Private - 6" } } - { serieId: { notEqualTo: "Ternoa Private - 7" } } - { serieId: { notEqualTo: "Ternoa Private - 8" } } { timestampBurn: { isNull: true } } { owner: { equalTo: "${query.id}" } } {listed: { equalTo: 0}} @@ -227,14 +179,6 @@ export class GQLQueriesBuilder { filter: { and: [ { serieId: { notEqualTo: "Ternoa Xmas 2021" } } - { serieId: { notEqualTo: "Ternoa Private - 1" } } - { serieId: { notEqualTo: "Ternoa Private - 2" } } - { serieId: { notEqualTo: "Ternoa Private - 3" } } - { serieId: { notEqualTo: "Ternoa Private - 4" } } - { serieId: { notEqualTo: "Ternoa Private - 5" } } - { serieId: { notEqualTo: "Ternoa Private - 6" } } - { serieId: { notEqualTo: "Ternoa Private - 7" } } - { serieId: { notEqualTo: "Ternoa Private - 8" } } { timestampBurn: { isNull: true } } { creator: { equalTo: "${query.id}" } } ] From 865377d9aa07d8f75155f641395e19c920665349 Mon Sep 17 00:00:00 2001 From: "ghalielouarzazi@hotmail.com" Date: Mon, 10 Jan 2022 10:04:44 +0100 Subject: [PATCH 6/7] add total on sale route --- src/api/controllers/nfts/controller.ts | 17 +++++++++++++++-- src/api/controllers/nfts/router.ts | 1 + src/api/services/gqlQueriesBuilder.ts | 16 ++++++++++++++++ src/api/services/nft.ts | 18 +++++++++++++++++- src/api/validators/nftValidators.ts | 12 +++++++++++- 5 files changed, 60 insertions(+), 4 deletions(-) diff --git a/src/api/controllers/nfts/controller.ts b/src/api/controllers/nfts/controller.ts index 35df1dc..3ad819d 100644 --- a/src/api/controllers/nfts/controller.ts +++ b/src/api/controllers/nfts/controller.ts @@ -1,6 +1,6 @@ import NFTService from "../../services/nft"; import { NextFunction, Request, Response } from "express"; -import { validationGetNFTs, validationGetNFT, validationGetStatNFTsUser, validationNFTsBySeries, validationGetSeries, validationCanAddToSeries, validationGetHistory, validationAddCategoriesNFTs } from "../../validators/nftValidators"; +import { validationGetNFTs, validationGetNFT, validationGetStatNFTsUser, validationNFTsBySeries, validationGetSeries, validationCanAddToSeries, validationGetHistory, validationAddCategoriesNFTs, validationGetTotalOnSale } from "../../validators/nftValidators"; export class Controller { async getNFTs( @@ -96,12 +96,25 @@ export class Controller { next: NextFunction ): Promise{ try { - const queryValues = validationGetHistory({ ...req.query }) + const queryValues = validationGetHistory(req.query) res.json(await NFTService.getHistory(queryValues)); } catch (err) { next(err); } } + + async getTotalOnSale( + req: Request, + res: Response, + next: NextFunction + ): Promise{ + try { + const queryValues = validationGetTotalOnSale(req.query) + res.json(await NFTService.getTotalOnSale(queryValues)); + } catch (err) { + next(err); + } + } } export default new Controller(); diff --git a/src/api/controllers/nfts/router.ts b/src/api/controllers/nfts/router.ts index dd77fee..4401c71 100644 --- a/src/api/controllers/nfts/router.ts +++ b/src/api/controllers/nfts/router.ts @@ -4,6 +4,7 @@ export default express .Router() .get("/", controller.getNFTs) .get("/history", controller.getHistory) + .get("/total-on-sale", controller.getTotalOnSale) .get("/:id", controller.getNFT) .get("/stat/:id", controller.getStatNFTsUser) .get("/series/data", controller.getNFTsBySeries) diff --git a/src/api/services/gqlQueriesBuilder.ts b/src/api/services/gqlQueriesBuilder.ts index d334b2d..a270a88 100644 --- a/src/api/services/gqlQueriesBuilder.ts +++ b/src/api/services/gqlQueriesBuilder.ts @@ -389,6 +389,22 @@ countSmallestPrice = (seriesId: string, marketplaceId: number=null) => gql` } `; +countAllListedInMarketplace = (marketplaceId: number) => gql` + { + nftEntities( + filter: { + and: [ + { timestampBurn: { isNull: true } } + { listed: { equalTo: 1} } + { marketplaceId: { equalTo: "${marketplaceId}" } } + ] + } + ) { + totalCount + } + } +`; + } export default new GQLQueriesBuilder(); diff --git a/src/api/services/nft.ts b/src/api/services/nft.ts index 5774058..6be76a2 100644 --- a/src/api/services/nft.ts +++ b/src/api/services/nft.ts @@ -9,7 +9,7 @@ import CategoryService from "./category" import { populateNFT } from "../helpers/nftHelpers"; import QueriesBuilder from "./gqlQueriesBuilder"; import { decryptCookie, TERNOA_API_URL, TIME_BETWEEN_SAME_USER_VIEWS } from "../../utils"; -import { canAddToSeriesQuery, addCategoriesNFTsQuery, getHistoryQuery, getSeriesStatusQuery, NFTBySeriesQuery, NFTQuery, NFTsQuery, statNFTsUserQuery } from "../validators/nftValidators"; +import { canAddToSeriesQuery, addCategoriesNFTsQuery, getHistoryQuery, getSeriesStatusQuery, NFTBySeriesQuery, NFTQuery, NFTsQuery, statNFTsUserQuery, getTotalOnSaleQuery } from "../validators/nftValidators"; import { IUser } from "../../interfaces/IUser"; import CategoryModel from "../../models/category"; import { ICategory } from "../../interfaces/ICategory"; @@ -356,6 +356,22 @@ export class NFTService { throw new Error("Couldn't get history information about this nft / series"); } } + + /** + * Returns the + * @param query - query (see getTotalOnSaleQuery) + * @throws Will throw an error if seriesId is not found + */ + async getTotalOnSale(query: getTotalOnSaleQuery): Promise { + try { + const gqlQuery = QueriesBuilder.countAllListedInMarketplace(query.marketplaceId) + const res = await request(indexerUrl, gqlQuery); + if (!res.nftEntities.totalCount) throw new Error() + return res.nftEntities.totalCount + } catch (err) { + throw new Error("Count could not have been fetched"); + } + } } export default new NFTService(); diff --git a/src/api/validators/nftValidators.ts b/src/api/validators/nftValidators.ts index fd6dff1..df22fc4 100644 --- a/src/api/validators/nftValidators.ts +++ b/src/api/validators/nftValidators.ts @@ -227,4 +227,14 @@ export const validationGetHistory = (query: any) => { }).required(), }) return validateQuery(validationSchema, {pagination, sort, filter}) as getHistoryQuery; -} \ No newline at end of file +} + +export type getTotalOnSaleQuery = { + marketplaceId: number +} +export const validationGetTotalOnSale = (query: any) => { + const validationSchema = Joi.object({ + marketplaceId: Joi.number().required(), + }) + return validateQuery(validationSchema, query) as getTotalOnSaleQuery; +} From 10556a8a3d5dc227a8574025aefb7fcc16e38fdf Mon Sep 17 00:00:00 2001 From: "ghalielouarzazi@hotmail.com" Date: Mon, 10 Jan 2022 15:25:11 +0100 Subject: [PATCH 7/7] upgrade version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 045d74c..144f444 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nft-marketplace-api", - "version": "1.2.1", + "version": "1.3.0", "description": "ternoa nft marketplace server", "main": "dist/index.js", "scripts": {