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

Create collections top level entity in GraphQL #214

Merged
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
24 changes: 24 additions & 0 deletions schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,7 @@ input BooleanSearchOptions {
"""Collection of hypercerts for reference and display purposes"""
type Collection {
admins: [User!]!
blueprints: [Blueprint!]

"""Chain ID of the collection"""
chain_ids: [EthBigInt!]
Expand All @@ -342,12 +343,29 @@ type Collection {

"""Description of the collection"""
description: String!
hypercerts: [Hypercert!]
id: ID!

"""Name of the collection"""
name: String!
}

input CollectionFetchInput {
by: CollectionSortOptions
}

input CollectionSortOptions {
created_at: SortOrder
description: SortOrder
name: SortOrder
}

input CollectionWhereInput {
description: StringSearchOptions
id: IdSearchOptions
name: StringSearchOptions
}

"""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 @@ -469,6 +487,11 @@ type GetBlueprintResponse {
data: [Blueprint!]
}

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

"""Pointer to a contract deployed on a chain"""
type GetContractsResponse {
count: Int
Expand Down Expand Up @@ -889,6 +912,7 @@ type Query {
attestationSchemas(first: Int, offset: Int): GetAttestationsSchemaResponse!
attestations(first: Int, offset: Int, sort: AttestationFetchInput, where: AttestationWhereInput): GetAttestationsResponse!
blueprints(first: Int, offset: Int, sort: BlueprintFetchInput, where: BlueprintWhereInput): GetBlueprintResponse!
collections(first: Int, offset: Int, sort: CollectionFetchInput, where: CollectionWhereInput): GetCollectionsResponse!
contracts(first: Int, offset: Int, sort: ContractFetchInput, where: ContractWhereInput): GetContractsResponse!
fractions(first: Int, offset: Int, sort: FractionFetchInput, where: FractionWhereInput): GetFractionsResponse!
hyperboards(first: Int, offset: Int, sort: HyperboardFetchInput, where: HyperboardWhereInput): GetHyperboardsResponse!
Expand Down
28 changes: 28 additions & 0 deletions src/graphql/schemas/args/collectionArgs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { ArgsType, Field, InputType } from "type-graphql";

import { BasicCollectionWhereInput } from "../inputs/collectionInput.js";
import type { OrderOptions } from "../inputs/orderOptions.js";
import { Collection } from "../typeDefs/collectionTypeDefs.js";
import { CollectionSortOptions } from "../inputs/sortOptions.js";

import { withPagination } from "./baseArgs.js";

@InputType()
export class CollectionWhereInput extends BasicCollectionWhereInput {}

@InputType()
export class CollectionFetchInput implements OrderOptions<Collection> {
@Field(() => CollectionSortOptions, { nullable: true })
by?: CollectionSortOptions;
}

@ArgsType()
export class CollectionArgs {
@Field(() => CollectionWhereInput, { nullable: true })
where?: CollectionWhereInput;
@Field(() => CollectionFetchInput, { nullable: true })
sort?: CollectionFetchInput;
}

@ArgsType()
export class GetCollectionsArgs extends withPagination(CollectionArgs) {}
18 changes: 18 additions & 0 deletions src/graphql/schemas/inputs/collectionInput.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Field, InputType } from "type-graphql";

import { Collection } from "../typeDefs/collectionTypeDefs.js";

import { IdSearchOptions, StringSearchOptions } from "./searchOptions.js";
import type { WhereOptions } from "./whereOptions.js";

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

@Field(() => StringSearchOptions, { nullable: true })
name?: StringSearchOptions;

@Field(() => StringSearchOptions, { nullable: true })
description?: StringSearchOptions;
}
11 changes: 11 additions & 0 deletions src/graphql/schemas/inputs/sortOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { Sale } from "../typeDefs/salesTypeDefs.js";
import { Hyperboard } from "../typeDefs/hyperboardTypeDefs.js";
import { Blueprint } from "../typeDefs/blueprintTypeDefs.js";
import { SignatureRequest } from "../typeDefs/signatureRequestTypeDefs.js";
import { Collection } from "../typeDefs/collectionTypeDefs.js";

export type SortOptions<T extends object> = {
[P in keyof T]: SortOrder | null;
Expand Down Expand Up @@ -238,3 +239,13 @@ export class SignatureRequestSortOptions
@Field(() => SortOrder, { nullable: true })
purpose?: SortOrder;
}

@InputType()
export class CollectionSortOptions implements SortOptions<Collection> {
@Field(() => SortOrder, { nullable: true })
name?: SortOrder;
@Field(() => SortOrder, { nullable: true })
created_at?: SortOrder;
@Field(() => SortOrder, { nullable: true })
description?: SortOrder;
}
101 changes: 101 additions & 0 deletions src/graphql/schemas/resolvers/collectionResolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import {
Args,
FieldResolver,
ObjectType,
Query,
Resolver,
Root,
} from "type-graphql";

import { GetCollectionsArgs } from "../args/collectionArgs.js";
import { Collection } from "../typeDefs/collectionTypeDefs.js";
import { Blueprint } from "../typeDefs/blueprintTypeDefs.js";
import { User } from "../typeDefs/userTypeDefs.js";

import { createBaseResolver, DataResponse } from "./baseTypes.js";
import GetHypercertsResponse from "./hypercertResolver.js";

@ObjectType()
class GetCollectionsResponse extends DataResponse(Collection) {}

const CollectionBaseResolver = createBaseResolver("collection");

@Resolver(() => Collection)
class CollectionResolver extends CollectionBaseResolver {
@Query(() => GetCollectionsResponse)
async collections(@Args() args: GetCollectionsArgs) {
try {
const res = await this.supabaseDataService.getCollections(args);

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

@FieldResolver(() => GetHypercertsResponse)
async hypercerts(@Root() collection: Collection) {
if (!collection.id) {
console.error(
"[CollectionResolver::hypercerts] Collection ID is undefined",
);
return [];
}

const hypercerts = await this.supabaseDataService.getCollectionHypercerts(
collection.id,
);

if (!hypercerts?.length) {
return [];
}

const hypercertIds = hypercerts
.map((h) => h.hypercert_id)
.filter((id): id is string => id !== undefined);

if (hypercertIds.length === 0) {
return [];
}

const hypercertsData = await this.getHypercerts({
where: { hypercert_id: { in: hypercertIds } },
});

return hypercertsData.data || [];
}

@FieldResolver(() => [User])
async admins(@Root() collection: Collection) {
if (!collection.id) {
console.error("[CollectionResolver::admins] Collection ID is undefined");
return [];
}

const admins = await this.supabaseDataService.getCollectionAdmins(
collection.id,
);
return admins || [];
}

@FieldResolver(() => [Blueprint])
async blueprints(@Root() collection: Collection) {
pheuberger marked this conversation as resolved.
Show resolved Hide resolved
if (!collection.id) {
console.error(
"[CollectionResolver::blueprints] Collection ID is undefined",
);
return [];
}

const blueprints = await this.supabaseDataService.getCollectionBlueprints(
collection.id,
);
return blueprints || [];
}
}

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 @@ -11,6 +11,7 @@ import { SalesResolver } from "./salesResolver.js";
import { UserResolver } from "./userResolver.js";
import { BlueprintResolver } from "./blueprintResolver.js";
import { SignatureRequestResolver } from "./signatureRequestResolver.js";
import { CollectionResolver } from "./collectionResolver.js";

export const resolvers = [
ContractResolver,
Expand All @@ -26,4 +27,5 @@ export const resolvers = [
UserResolver,
BlueprintResolver,
SignatureRequestResolver,
CollectionResolver,
] as const;
34 changes: 34 additions & 0 deletions src/graphql/schemas/typeDefs/collectionTypeDefs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Field, ObjectType } from "type-graphql";

import { EthBigInt } from "../../scalars/ethBigInt.js";

import { BasicTypeDef } from "./baseTypes/basicTypeDef.js";
import { User } from "./userTypeDefs.js";
import { Hypercert } from "./hypercertTypeDefs.js";
import { Blueprint } from "./blueprintTypeDefs.js";

@ObjectType({
description: "Collection of hypercerts for reference and display purposes",
})
export class Collection extends BasicTypeDef {
@Field({ description: "Creation timestamp of the collection" })
created_at?: string;
@Field({ description: "Name of the collection" })
name?: string;
@Field({ description: "Description of the collection" })
description?: string;
@Field(() => [EthBigInt], {
nullable: true,
description: "Chain ID of the collection",
})
chain_ids?: (bigint | number | string)[];

@Field(() => [User])
admins?: User[];

@Field(() => [Hypercert], { nullable: true })
hypercerts?: Hypercert[];

@Field(() => [Blueprint], { nullable: true })
blueprints?: Blueprint[];
}
21 changes: 1 addition & 20 deletions src/graphql/schemas/typeDefs/hyperboardTypeDefs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { BasicTypeDef } from "./baseTypes/basicTypeDef.js";
import { EthBigInt } from "../../scalars/ethBigInt.js";
import { User } from "./userTypeDefs.js";
import { GraphQLBigInt } from "graphql-scalars";
import { Collection } from "./collectionTypeDefs.js";

@ObjectType({
description: "Hyperboard of hypercerts for reference and display purposes",
Expand Down Expand Up @@ -48,26 +49,6 @@ class SectionResponseType {
count?: number;
}

@ObjectType({
description: "Collection of hypercerts for reference and display purposes",
})
class Collection extends BasicTypeDef {
@Field({ description: "Creation timestamp of the collection" })
created_at?: string;
@Field({ description: "Name of the collection" })
name?: string;
@Field({ description: "Description of the collection" })
description?: string;
@Field(() => [EthBigInt], {
nullable: true,
description: "Chain ID of the collection",
})
chain_ids?: (bigint | number | string)[];

@Field(() => [User])
admins?: User[];
}

@ObjectType({
description: "Section representing a collection within a hyperboard",
})
Expand Down
Loading
Loading