Skip to content

Commit

Permalink
Merge pull request #260 from holaplex/main
Browse files Browse the repository at this point in the history
Release 10/18
  • Loading branch information
kespinola authored Oct 18, 2023
2 parents 69246eb + 7082d89 commit 39e84da
Show file tree
Hide file tree
Showing 18 changed files with 637 additions and 200 deletions.
8 changes: 4 additions & 4 deletions api/proto.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ sha512 = "d75800df0d4744c6b0f4d9a9952d3bfd0bb6b24a8babd19104cc11b54a525f85551b3c

[[schemas]]
subject = "nfts"
version = 30
sha512 = "bee70bd6f0f18a8049f93bceb9c4b500b49352f9d19d55d5a411da92cbd786c88bec47f73e1ef6946ceefc7de8e558f704bf8187be9d9f4e49bd102baec29327"
version = 31
sha512 = "449574f8551ab8c17824af9e08b1658ad1b26ac80340230ddf02e7a1e0979d8a47025913a6598799cf83dd1a9cda87697ee87a13f404ebb52c95ea0084205767"

[[schemas]]
subject = "organization"
Expand All @@ -20,8 +20,8 @@ sha512 = "c5ddf43d2958ec690ee2261d0ff9808b67ce810d2fc4b6077f96f561929a920f03509f

[[schemas]]
subject = "solana_nfts"
version = 11
sha512 = "967fefde938a0f6ce05194e4fca15673e681caac54d8aeec114c5d38418632b9696dbaf5362345a15114e5abb49de55d0af8b9edcc0f2c91f9ef1ccc4ff55d68"
version = 12
sha512 = "4f85496c50a82cb40faa097cf6d0cb23275b3b90cb561d01388f3e5a71282a8b8e1eea617b7d712b0e415d65af483209fac2db1591456fa814a1f41a1c457433"

[[schemas]]
subject = "timestamp"
Expand Down
4 changes: 2 additions & 2 deletions api/proto.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ endpoint = "https://schemas.holaplex.tools"

[schemas]
organization = 5
nfts = 30
nfts = 31
customer = 2
treasury = 23
solana_nfts = 11
solana_nfts = 12
polygon_nfts = 6
timestamp = 1
197 changes: 195 additions & 2 deletions api/src/dataloaders/collection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,17 @@ use std::collections::HashMap;

use async_graphql::{dataloader::Loader as DataLoader, FieldError, Result};
use poem::async_trait;
use sea_orm::prelude::*;
use redis::{AsyncCommands, Client as Redis};
use sea_orm::{prelude::*, FromQueryResult, QueryFilter, QuerySelect};

use crate::{db::Connection, entities::collections, objects::Collection};
use crate::{
db::Connection,
entities::{
collection_mints, collections, drops,
sea_orm_active_enums::{CreationStatus, DropType},
},
objects::Collection,
};

#[derive(Debug, Clone)]
pub struct Loader {
Expand Down Expand Up @@ -35,3 +43,188 @@ impl DataLoader<Uuid> for Loader {
.collect()
}
}

#[derive(FromQueryResult, Debug, Clone)]
struct CollectionTotalMintsCount {
id: Uuid,
count: i64,
}

#[derive(Debug, Clone)]
pub struct TotalMintsLoader {
pub db: Connection,
pub redis: Redis,
}

impl TotalMintsLoader {
#[must_use]
pub fn new(db: Connection, redis: Redis) -> Self {
Self { db, redis }
}
}

#[async_trait]
impl DataLoader<Uuid> for TotalMintsLoader {
type Error = FieldError;
type Value = i64;

async fn load(&self, keys: &[Uuid]) -> Result<HashMap<Uuid, Self::Value>, Self::Error> {
let mut results: HashMap<Uuid, Self::Value> = HashMap::new();
let mut missing_keys: Vec<Uuid> = Vec::new();

let mut redis_connection = self.redis.get_async_connection().await?;

for key in keys {
let redis_key = format!("collection:{key}:total_mints");
match redis_connection.get::<_, i64>(&redis_key).await {
Ok(value) => {
results.insert(*key, value);
},
Err(_) => {
missing_keys.push(*key);
},
}
}

if missing_keys.is_empty() {
return Ok(results);
}

let conn = self.db.get();
let count_results = collection_mints::Entity::find()
.select_only()
.column_as(collection_mints::Column::Id.count(), "count")
.column_as(collection_mints::Column::CollectionId, "id")
.filter(
collection_mints::Column::CollectionId
.is_in(missing_keys.iter().map(ToOwned::to_owned))
.and(collection_mints::Column::CreationStatus.ne(CreationStatus::Queued)),
)
.group_by(collection_mints::Column::CollectionId)
.into_model::<CollectionTotalMintsCount>()
.all(conn)
.await?;
let count_results = count_results
.into_iter()
.map(|result| (result.id, result.count))
.collect::<HashMap<_, _>>();

for key in missing_keys {
let count = count_results.get(&key).copied().unwrap_or_default();
let redis_key = format!("collection:{key}:total_mints");

redis_connection
.set::<_, i64, Option<i64>>(&redis_key, count)
.await?;

results.insert(key, count);
}

Ok(results)
}
}

#[derive(FromQueryResult)]
struct CollectionSupplyCount {
id: Uuid,
count: i64,
}

#[derive(Debug, Clone)]
pub struct SupplyLoader {
pub db: Connection,
pub redis: Redis,
}

impl SupplyLoader {
#[must_use]
pub fn new(db: Connection, redis: Redis) -> Self {
Self { db, redis }
}
}

#[async_trait]
impl DataLoader<Uuid> for SupplyLoader {
type Error = FieldError;
type Value = Option<i64>;

async fn load(&self, keys: &[Uuid]) -> Result<HashMap<Uuid, Self::Value>, Self::Error> {
let mut results: HashMap<Uuid, Self::Value> = HashMap::new();
let mut missing_keys: Vec<Uuid> = Vec::new();

let mut redis_connection = self.redis.get_async_connection().await?;

for key in keys {
let redis_key = format!("collection:{key}:supply");
match redis_connection.get::<_, Option<i64>>(&redis_key).await {
Ok(value) => {
results.insert(*key, value);
},
Err(_) => {
missing_keys.push(*key);
},
}
}

if missing_keys.is_empty() {
return Ok(results);
}

let conn = self.db.get();
let mut computed_supplies: Vec<Uuid> = Vec::new();

let collection_with_drops = collections::Entity::find()
.filter(collections::Column::Id.is_in(missing_keys.iter().map(ToOwned::to_owned)))
.inner_join(drops::Entity)
.select_also(drops::Entity)
.all(conn)
.await?;

for (collection, drop) in collection_with_drops {
if let Some(drop) = drop {
if drop.drop_type == DropType::Open {
computed_supplies.push(collection.id);
continue;
}
continue;
}

let redis_key = format!("collection:{}:supply", collection.id);

let supply = redis_connection
.set::<_, Option<i64>, Option<i64>>(&redis_key, collection.supply)
.await?;

results.insert(collection.id, supply);
}

let count_results = collection_mints::Entity::find()
.select_only()
.column_as(collection_mints::Column::Id.count(), "count")
.column_as(collection_mints::Column::CollectionId, "id")
.filter(
collection_mints::Column::CollectionId
.is_in(computed_supplies.iter().map(ToOwned::to_owned)),
)
.group_by(collection_mints::Column::CollectionId)
.into_model::<CollectionSupplyCount>()
.all(conn)
.await?
.into_iter()
.map(|result| (result.id, result.count))
.collect::<HashMap<_, _>>();

for key in computed_supplies {
let count = count_results.get(&key).copied();
let redis_key = format!("collection:{key}:supply");

redis_connection
.set::<_, Option<i64>, Option<i64>>(&redis_key, count)
.await?;

results.insert(key, count);
}

Ok(results)
}
}
27 changes: 5 additions & 22 deletions api/src/dataloaders/collection_drop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,9 @@ use std::collections::HashMap;

use async_graphql::{dataloader::Loader as DataLoader, FieldError, Result};
use poem::async_trait;
use sea_orm::{prelude::*, JoinType, QuerySelect};
use sea_orm::prelude::*;

use crate::{
db::Connection,
entities::{collections, drops},
objects::Drop,
};
use crate::{db::Connection, entities::drops, objects::Drop};

#[derive(Debug, Clone)]
pub struct Loader {
Expand All @@ -29,26 +25,13 @@ impl DataLoader<Uuid> for Loader {

async fn load(&self, keys: &[Uuid]) -> Result<HashMap<Uuid, Self::Value>, Self::Error> {
let drops = drops::Entity::find()
.join(JoinType::InnerJoin, drops::Relation::Collections.def())
.select_also(collections::Entity)
.filter(drops::Column::CollectionId.is_in(keys.iter().map(ToOwned::to_owned)))
.all(self.db.get())
.await?;

drops
Ok(drops
.into_iter()
.map(|(drop, collection)| {
Ok((
drop.collection_id,
Drop::new(
drop.clone(),
collection.ok_or(FieldError::new(format!(
"no collection for the drop {}",
drop.id
)))?,
),
))
})
.collect::<Result<HashMap<Uuid, Self::Value>>>()
.map(|drop| (drop.collection_id, drop.into()))
.collect::<HashMap<_, _>>())
}
}
34 changes: 9 additions & 25 deletions api/src/dataloaders/drop.rs
Original file line number Diff line number Diff line change
@@ -1,53 +1,37 @@
use std::collections::HashMap;

use async_graphql::{dataloader::Loader as DataLoader, FieldError, Result};
use async_graphql::{dataloader::Loader, FieldError, Result};
use poem::async_trait;
use sea_orm::{prelude::*, JoinType, QuerySelect};
use sea_orm::prelude::*;

use crate::{
db::Connection,
entities::{collections, drops},
objects::Drop,
};
use crate::{db::Connection, entities::drops, objects::Drop};

#[derive(Debug, Clone)]
pub struct Loader {
pub struct DropLoader {
pub db: Connection,
}

impl Loader {
impl DropLoader {
#[must_use]
pub fn new(db: Connection) -> Self {
Self { db }
}
}

#[async_trait]
impl DataLoader<Uuid> for Loader {
impl Loader<Uuid> for DropLoader {
type Error = FieldError;
type Value = Drop;

async fn load(&self, keys: &[Uuid]) -> Result<HashMap<Uuid, Self::Value>, Self::Error> {
let drops = drops::Entity::find()
.join(JoinType::InnerJoin, drops::Relation::Collections.def())
.select_also(collections::Entity)
.filter(drops::Column::Id.is_in(keys.iter().map(ToOwned::to_owned)))
.all(self.db.get())
.await?;

drops
Ok(drops
.into_iter()
.map(|(drop, collection)| {
Ok((
drop.id,
Drop::new(
drop.clone(),
collection.ok_or_else(|| {
FieldError::new(format!("no collection for the drop {}", drop.id))
})?,
),
))
})
.collect::<Result<HashMap<Uuid, Self::Value>>>()
.map(|drop| (drop.id, drop.into()))
.collect())
}
}
14 changes: 3 additions & 11 deletions api/src/dataloaders/drops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,9 @@ use std::collections::HashMap;

use async_graphql::{dataloader::Loader as DataLoader, FieldError, Result};
use poem::async_trait;
use sea_orm::{prelude::*, JoinType, QuerySelect};
use sea_orm::prelude::*;

use crate::{
db::Connection,
entities::{collections, drops},
objects::Drop,
};
use crate::{db::Connection, entities::drops, objects::Drop};

#[derive(Debug, Clone)]
pub struct ProjectLoader {
Expand All @@ -29,17 +25,13 @@ impl DataLoader<Uuid> for ProjectLoader {

async fn load(&self, keys: &[Uuid]) -> Result<HashMap<Uuid, Self::Value>, Self::Error> {
let drops = drops::Entity::find()
.join(JoinType::InnerJoin, drops::Relation::Collections.def())
.select_also(collections::Entity)
.filter(drops::Column::ProjectId.is_in(keys.iter().map(ToOwned::to_owned)))
.all(self.db.get())
.await?;

Ok(drops
.into_iter()
.filter_map(|(drop, collection)| {
collection.map(|collection| (drop.project_id, Drop::new(drop, collection)))
})
.map(|drop| (drop.project_id, drop.into()))
.fold(HashMap::new(), |mut acc, (project, drop)| {
acc.entry(project).or_insert_with(Vec::new);

Expand Down
Loading

0 comments on commit 39e84da

Please sign in to comment.