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

Add listing query for collection #814

Open
wants to merge 6 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
155 changes: 155 additions & 0 deletions crates/core/src/db/queries/metadatas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,19 @@ pub struct CollectionNftOptions {
pub offset: u64,
}

/// Input parameters for the [`collection_nfts`] query.
#[derive(Debug)]
pub struct CollectionListedNftOptions {
/// Collection address
pub collection: String,
/// Auction house of the collection
pub auction_house: Option<String>,
/// Limit the number of returned rows
pub limit: u64,
/// Skip the first `n` resulting rows
pub offset: u64,
}

/// Input parameters for the [`wallet_nfts`] query.
#[derive(Debug)]
pub struct WalletNftOptions {
Expand Down Expand Up @@ -670,6 +683,148 @@ pub fn collection_nfts(conn: &Connection, options: CollectionNftOptions) -> Resu
.context("Failed to load nft(s)")
}

/// Handles queries for a Collection Listed Nfts
///
/// # Errors
/// returns an error when the underlying queries throw an error
#[allow(clippy::too_many_lines)]
pub fn collection_listed_nfts(
conn: &Connection,
options: CollectionListedNftOptions,
) -> Result<Vec<Nft>> {
let CollectionListedNftOptions {
collection,
auction_house,
limit,
offset,
} = options;

let current_time = Utc::now().naive_utc();

let uuid = Uuid::parse_str(&collection);
let is_me_collection = match uuid {
Err(_error) => false,
Ok(_result) => true,
};
let query = Query::select()
.columns(vec![
(Metadatas::Table, Metadatas::Address),
(Metadatas::Table, Metadatas::Name),
(Metadatas::Table, Metadatas::SellerFeeBasisPoints),
(Metadatas::Table, Metadatas::UpdateAuthorityAddress),
(Metadatas::Table, Metadatas::MintAddress),
(Metadatas::Table, Metadatas::PrimarySaleHappened),
(Metadatas::Table, Metadatas::Uri),
(Metadatas::Table, Metadatas::Slot),
])
.columns(vec![
(MetadataJsons::Table, MetadataJsons::Description),
(MetadataJsons::Table, MetadataJsons::Image),
(MetadataJsons::Table, MetadataJsons::AnimationUrl),
(MetadataJsons::Table, MetadataJsons::ExternalUrl),
(MetadataJsons::Table, MetadataJsons::Category),
(MetadataJsons::Table, MetadataJsons::Model),
])
.columns(vec![(
CurrentMetadataOwners::Table,
CurrentMetadataOwners::TokenAccountAddress,
)])
.from(MetadataJsons::Table)
.inner_join(
Metadatas::Table,
Expr::tbl(MetadataJsons::Table, MetadataJsons::MetadataAddress)
.equals(Metadatas::Table, Metadatas::Address),
)
.inner_join(
CurrentMetadataOwners::Table,
Expr::tbl(Metadatas::Table, Metadatas::MintAddress).equals(
CurrentMetadataOwners::Table,
CurrentMetadataOwners::MintAddress,
),
)
.conditions(
is_me_collection,
|query| {
query.inner_join(
MeMetadataCollections::Table,
Expr::tbl(
MeMetadataCollections::Table,
MeMetadataCollections::MetadataAddress,
)
.equals(Metadatas::Table, Metadatas::Address),
);
},
|query| {
query.inner_join(
MetadataCollectionKeys::Table,
Expr::tbl(
MetadataCollectionKeys::Table,
MetadataCollectionKeys::MetadataAddress,
)
.equals(Metadatas::Table, Metadatas::Address),
);
},
)
.left_join(
Listings::Table,
Condition::all()
.add(
Expr::tbl(Listings::Table, Listings::Metadata)
.equals(Metadatas::Table, Metadatas::Address),
)
.add(Expr::tbl(Listings::Table, Listings::Seller).equals(
CurrentMetadataOwners::Table,
CurrentMetadataOwners::OwnerAddress,
))
.add(Expr::tbl(Listings::Table, Listings::PurchaseId).is_null())
.add(Expr::tbl(Listings::Table, Listings::CanceledAt).is_null())
.add(
Expr::tbl(Listings::Table, Listings::AuctionHouse)
.ne(pubkeys::OPENSEA_AUCTION_HOUSE.to_string()),
)
.add(
Expr::tbl(Listings::Table, Listings::Expiry)
.is_null()
.or(Expr::tbl(Listings::Table, Listings::Expiry).gt(current_time)),
)
.add_option(auction_house.map(|auction_house| {
Expr::col((Listings::Table, Listings::AuctionHouse)).eq(auction_house)
})),
)
.and_where(Expr::col(Metadatas::BurnedAt).is_null())
.conditions(
is_me_collection,
|query| {
query.and_where(
Expr::col((
MeMetadataCollections::Table,
MeMetadataCollections::CollectionId,
))
.eq(collection.clone()),
);
},
|query| {
query.and_where(
Expr::col((
MetadataCollectionKeys::Table,
MetadataCollectionKeys::CollectionAddress,
))
.eq(collection.clone()),
);
},
)
.limit(limit)
.offset(offset)
.order_by((Listings::Table, Listings::Price), Order::Asc)
.take();

let query = query.to_string(PostgresQueryBuilder);

diesel::sql_query(query)
.load(conn)
.context("Failed to load nft(s)")
kespinola marked this conversation as resolved.
Show resolved Hide resolved
}

/// Handles queries for a wallet Nfts
///
/// # Errors
Expand Down
27 changes: 26 additions & 1 deletion crates/graphql/src/schema/objects/nft.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ use indexer_core::{
assets::{proxy_url, AssetIdentifier, ImageSize},
bigdecimal::ToPrimitive,
db::{
queries::{self, metadatas::CollectionNftOptions},
queries::{
self,
metadatas::{CollectionListedNftOptions, CollectionNftOptions},
},
sql_query,
sql_types::Text,
tables::{
Expand Down Expand Up @@ -1147,6 +1150,28 @@ impl CollectionTrend {
self.one_day_marketcap_change
}

pub async fn listings(
&self,
ctx: &AppContext,
limit: i32,
offset: i32,
auction_house: Option<String>,
) -> FieldResult<Vec<Nft>> {
let conn = ctx.shared.db.get()?;

let nfts = queries::metadatas::collection_listed_nfts(&conn, CollectionListedNftOptions {
kespinola marked this conversation as resolved.
Show resolved Hide resolved
collection: self.collection.clone(),
auction_house,
limit: limit.try_into()?,
offset: offset.try_into()?,
})?;

nfts.into_iter()
.map(TryInto::try_into)
.collect::<Result<_, _>>()
.map_err(Into::into)
}

pub async fn collection(&self, ctx: &AppContext) -> FieldResult<Option<Collection>> {
ctx.generic_collection_loader
.load(self.collection.clone())
Expand Down