Skip to content

Commit

Permalink
(feat): add support for collection entity in graphql api (#84)
Browse files Browse the repository at this point in the history
* (feat): add support for collection entity in graphql api

* (chore): update logging

* (fix): update naming and chain id type
  • Loading branch information
Jipperism authored Jun 18, 2024
1 parent ea028a3 commit db34134
Show file tree
Hide file tree
Showing 9 changed files with 395 additions and 0 deletions.
38 changes: 38 additions & 0 deletions schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ input BasicAttestationWhereInput {
uid: StringSearchOptions
}

input BasicCollectionWhereInput {
admin_id: StringSearchOptions
chain_id: NumberSearchOptions
id: IdSearchOptions
}

input BasicContractWhereInput {
chain_id: NumberSearchOptions
contract_address: StringSearchOptions
Expand Down Expand Up @@ -157,6 +163,20 @@ input BooleanSearchOptions {
eq: Boolean
}

type Collection {
admin_address: String!
background_image: String!
chain_id: BigInt
grayscale_image: Boolean!
id: ID!
name: String!
tile_border_color: String!
}

input CollectionFetchInput {
by: ContractSortOptions
}

"""Pointer to a contract deployed on a chain"""
type Contract {
"""The ID of the chain on which the contract is deployed"""
Expand Down Expand Up @@ -196,12 +216,24 @@ scalar EthBigInt

type Fraction {
creation_block_timestamp: BigInt

"""
The ID of the fraction concatenated from the chain ID, contract address, and token ID
"""
fraction_id: ID
id: ID!
last_block_update_timestamp: BigInt

"""The metadata for the fraction"""
metadata: Metadata

"""Marketplace orders related to this fraction"""
orders: GetOrdersResponse

"""Address of the owner of the fractions"""
owner_address: String

"""Units held by the fraction"""
units: EthBigInt
}

Expand Down Expand Up @@ -238,6 +270,11 @@ type GetAttestationsSchemaResponse {
data: [AttestationSchema!]!
}

type GetCollectionsResponse {
count: Int
data: [Collection!]
}

type GetContractsResponse {
count: Int
data: [Contract!]
Expand Down Expand Up @@ -429,6 +466,7 @@ input OrderFetchInput {
type Query {
attestationSchemas(count: CountKeys, first: Int, offset: Int, sort: AttestationSchemaFetchInput, where: AttestationSchemaWhereInput): GetAttestationsSchemaResponse!
attestations(count: CountKeys, first: Int, offset: Int, sort: AttestationFetchInput, where: AttestationWhereInput): GetAttestationsResponse!
collections(count: CountKeys, first: Int, offset: Int, sort: CollectionFetchInput, where: BasicCollectionWhereInput): GetCollectionsResponse!
contracts(count: CountKeys, first: Int, offset: Int, sort: ContractFetchInput, where: BasicContractWhereInput): GetContractsResponse!
fractions(count: CountKeys, first: Int, offset: Int, sort: FractionFetchInput, where: FractionWhereInput): GetFractionsResponse!
hypercerts(count: CountKeys, first: Int, offset: Int, sort: HypercertFetchInput, where: HypercertsWhereInput): GetHypercertsResponse!
Expand Down
17 changes: 17 additions & 0 deletions src/graphql/schemas/args/collectionArgs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { ArgsType, Field } from "type-graphql";
import { PaginationArgs } from "./paginationArgs.js";
import {BasicCollectionWhereInput, CollectionFetchInput} from "../inputs/collectionInput.js";

@ArgsType()
export class GetCollectionArgs extends PaginationArgs {
@Field({ nullable: true })
where?: BasicCollectionWhereInput;
@Field({ nullable: true })
sort?: CollectionFetchInput;
}

@ArgsType()
export class GetCollectionByIdArgs {
@Field({ nullable: true })
id?: string;
}
22 changes: 22 additions & 0 deletions src/graphql/schemas/inputs/collectionInput.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Field, InputType } from "type-graphql";
import type { WhereOptions } from "./whereOptions.js";
import {IdSearchOptions, NumberSearchOptions, StringSearchOptions} from "./searchOptions.js";
import { ContractSortOptions } from "./sortOptions.js";
import {Collection} from "../typeDefs/collectionTypeDefs.js";
import {CollectionOptions} from "./collectionOptions.js";

@InputType()
export class BasicCollectionWhereInput implements WhereOptions<Collection> {
@Field((_) => IdSearchOptions, { nullable: true })
id?: IdSearchOptions | null;
@Field((_) => NumberSearchOptions, { nullable: true })
chain_id?: NumberSearchOptions | null;
@Field((_) => StringSearchOptions, { nullable: true })
admin_id?: StringSearchOptions | null;
}

@InputType()
export class CollectionFetchInput implements CollectionOptions<Collection> {
@Field((_) => ContractSortOptions, { nullable: true })
by?: ContractSortOptions;
}
13 changes: 13 additions & 0 deletions src/graphql/schemas/inputs/collectionOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import {
AttestationSchemaSortOptions,
AttestationSortOptions,
ContractSortOptions, FractionSortOptions,
HypercertSortOptions,
MetadataSortOptions
} from "./sortOptions.js";


export type CollectionOptions<T extends object> = {
by?: HypercertSortOptions | ContractSortOptions | MetadataSortOptions | AttestationSortOptions | AttestationSchemaSortOptions | FractionSortOptions | null;
}

50 changes: 50 additions & 0 deletions src/graphql/schemas/resolvers/collectionResolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Args, Field, Int, ObjectType, Query, Resolver } from "type-graphql";
import { inject, injectable } from "tsyringe";
import { Order } from "../typeDefs/orderTypeDefs.js";
import { SupabaseDataService } from "../../../services/SupabaseDataService.js";
import {Collection} from "../typeDefs/collectionTypeDefs.js";
import {GetCollectionArgs} from "../args/collectionArgs.js";

@ObjectType()
export default class GetCollectionsResponse {
@Field(() => [Collection], { nullable: true })
data?: Collection[];

@Field(() => Int, { nullable: true })
count?: number;
}

@injectable()
@Resolver((_) => Order)
class CollectionResolver {
constructor(
@inject(SupabaseDataService)
private readonly supabaseService: SupabaseDataService,
) {}

@Query((_) => GetCollectionsResponse)
async collections(@Args() args: GetCollectionArgs) {
try {
const res = await this.supabaseService.getCollections(args)

const { data, error, count } = res;

if (error) {
console.warn(
`[CollectionResolver::collections] Error fetching collections: `,
error,
);
return { data };
}

return { data, count: count ? count : data?.length };
} catch (e) {
throw new Error(
`[CollectionResolver::collections] Error fetching collections: ${(e as Error).message}`,
);
}
}

}

export { CollectionResolver };
2 changes: 2 additions & 0 deletions src/graphql/schemas/resolvers/composed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { FractionResolver } from "./fractionResolver.js";
import { AttestationResolver } from "./attestationResolver.js";
import { AttestationSchemaResolver } from "./attestationSchemaResolver.js";
import { OrderResolver } from "./orderResolver.js";
import { CollectionResolver } from "./collectionResolver.js";

export const resolvers = [
ContractResolver,
Expand All @@ -14,4 +15,5 @@ export const resolvers = [
AttestationResolver,
AttestationSchemaResolver,
OrderResolver,
CollectionResolver
] as const;
21 changes: 21 additions & 0 deletions src/graphql/schemas/typeDefs/collectionTypeDefs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Field, ObjectType } from "type-graphql";
import { BasicTypeDef } from "./basicTypeDef.js";
import {GraphQLBigInt} from "graphql-scalars";

@ObjectType()
class Collection extends BasicTypeDef {
@Field()
name?: string;
@Field({name: "admin_address"})
admin_id?: string;
@Field(() => GraphQLBigInt, {nullable: true})
chain_id?: number;
@Field()
background_image?: string;
@Field()
grayscale_image?: boolean;
@Field()
tile_border_color?: string;
}

export { Collection };
16 changes: 16 additions & 0 deletions src/services/SupabaseDataService.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import type { SupabaseClient } from "@supabase/supabase-js";
import type { Database as DataDatabase } from "../types/supabaseData.js";
import { supabaseData } from "../client/supabase.js";
import {GetCollectionArgs} from "../graphql/schemas/args/collectionArgs.js";
import {applyFilters} from "../graphql/schemas/utils/filters.js";
import {applySorting} from "../graphql/schemas/utils/sorting.js";
import {applyPagination} from "../graphql/schemas/utils/pagination.js";

export class SupabaseDataService {
private supabaseData: SupabaseClient<DataDatabase>;
Expand Down Expand Up @@ -70,4 +74,16 @@ export class SupabaseDataService {
.order("createdAt", { ascending: false })
.throwOnError();
}

getCollections(args: GetCollectionArgs) {
let query = this.supabaseData.from("hyperboards").select("*");

const {where, sort, offset, first} = args;

query = applyFilters({query, where});
query = applySorting({query, sort});
query = applyPagination({query, pagination: {first, offset}});

return query;
}
}
Loading

0 comments on commit db34134

Please sign in to comment.