Skip to content

Commit 19817cb

Browse files
committed
refactor(metadata): document metadata resolver and update metadata-related services
Restructured metadata-related components to improve code organization: - Removed MetadataResolver from graphql schemas - Updated HypercertsEntityService to include metadata retrieval methods - Modified MetadataQueryStrategy to simplify query generation - Updated import paths in composed resolver and other resolvers - Removed hypercert-specific filtering from metadata query strategy - Relocated metadata-related methods to HypercertsService - Updated test cases to reflect new metadata retrieval approach
1 parent 13a68ec commit 19817cb

File tree

17 files changed

+742
-144
lines changed

17 files changed

+742
-144
lines changed

src/graphql/schemas/args/metadataArgs.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,10 @@ import { ArgsType } from "type-graphql";
22
import { BaseQueryArgs } from "../../../lib/graphql/BaseQueryArgs.js";
33
import { createEntityArgs } from "../../../lib/graphql/createEntityArgs.js";
44
import { WhereFieldDefinitions } from "../../../lib/graphql/whereFieldDefinitions.js";
5-
import { EntityTypeDefs } from "../typeDefs/typeDefs.js";
65

76
const { WhereInput: MetadataWhereInput, SortOptions: MetadataSortOptions } =
87
createEntityArgs("Metadata", {
98
...WhereFieldDefinitions.Metadata.fields,
10-
hypercerts: {
11-
type: "id",
12-
references: {
13-
entity: EntityTypeDefs.Hypercert,
14-
fields: WhereFieldDefinitions.Hypercert.fields,
15-
},
16-
},
179
});
1810

1911
@ArgsType()

src/graphql/schemas/resolvers/composed.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { HypercertResolver } from "./hypercertResolver.js";
2-
import { MetadataResolver } from "./metadataResolver.js";
2+
import { MetadataResolver } from "../../../services/graphql/resolvers/metadataResolver.js";
33
import { ContractResolver } from "../../../services/graphql/resolvers/contractResolver.js";
44
import { FractionResolver } from "../../../services/graphql/resolvers/fractionResolver.js";
55
import { AttestationResolver } from "../../../services/graphql/resolvers/attestationResolver.js";

src/graphql/schemas/resolvers/hyperboardResolver.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,12 +122,12 @@ class HyperboardResolver {
122122
where: { hypercert_id: { in: hypercertIds } },
123123
})
124124
.then((res) => res.data),
125-
this.metadataService.getMetadata({
126-
where: { hypercerts: { hypercert_id: { in: hypercertIds } } },
125+
this.hypercertsService.getHypercertMetadataSets({
126+
hypercert_ids: hypercertIds,
127127
}),
128128
]);
129129

130-
const metadataByUri = _.keyBy(metadata.data, "uri");
130+
const metadataByUri = _.keyBy(metadata, "uri");
131131

132132
// get blueprints
133133
const collectionBlueprints =

src/graphql/schemas/resolvers/metadataResolver.ts

Lines changed: 0 additions & 38 deletions
This file was deleted.

src/graphql/schemas/resolvers/orderResolver.ts

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { Args, FieldResolver, Query, Resolver, Root } from "type-graphql";
44
import { getAddress } from "viem";
55
import { HypercertsService } from "../../../services/database/entities/HypercertsEntityService.js";
66
import { MarketplaceOrdersService } from "../../../services/database/entities/MarketplaceOrdersEntityService.js";
7-
import { MetadataService } from "../../../services/database/entities/MetadataEntityService.js";
87
import { Database } from "../../../types/supabaseData.js";
98
import { addPriceInUsdToOrder } from "../../../utils/addPriceInUSDToOrder.js";
109
import { getHypercertTokenId } from "../../../utils/tokenIds.js";
@@ -19,8 +18,6 @@ class OrderResolver {
1918
private marketplaceOrdersService: MarketplaceOrdersService,
2019
@inject(HypercertsService)
2120
private hypercertService: HypercertsService,
22-
@inject(MetadataService)
23-
private metadataService: MetadataService,
2421
) {}
2522

2623
@Query(() => GetOrdersResponse)
@@ -112,12 +109,8 @@ class OrderResolver {
112109
hypercert_id: { eq: formattedHypercertId },
113110
},
114111
}),
115-
this.metadataService.getMetadataSingle({
116-
where: {
117-
hypercerts: {
118-
hypercert_id: { eq: formattedHypercertId },
119-
},
120-
},
112+
this.hypercertService.getHypercertMetadata({
113+
hypercert_id: formattedHypercertId,
121114
}),
122115
]);
123116

src/services/database/entities/HypercertsEntityService.ts

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { Selectable } from "kysely";
2-
import { injectable } from "tsyringe";
3-
import { kyselyCaching } from "../../../client/kysely.js";
1+
import { Expression, Selectable, SqlBool } from "kysely";
2+
import { inject, injectable } from "tsyringe";
3+
import { CachingKyselyService, kyselyCaching } from "../../../client/kysely.js";
44
import { GetHypercertsArgs } from "../../../graphql/schemas/args/hypercertsArgs.js";
55
import { CachingDatabase } from "../../../types/kyselySupabaseCaching.js";
66
import {
@@ -17,7 +17,10 @@ export class HypercertsService {
1717
GetHypercertsArgs
1818
>;
1919

20-
constructor() {
20+
constructor(
21+
@inject(CachingKyselyService)
22+
private cachingKyselyService: CachingKyselyService,
23+
) {
2124
this.entityService = createEntityService<
2225
CachingDatabase,
2326
"claims",
@@ -32,4 +35,56 @@ export class HypercertsService {
3235
async getHypercert(args: GetHypercertsArgs) {
3336
return this.entityService.getSingle(args);
3437
}
38+
39+
async getHypercertMetadata(args: {
40+
claims_id?: string;
41+
hypercert_id?: string;
42+
}) {
43+
const result = this.cachingKyselyService
44+
.getConnection()
45+
.selectFrom("metadata")
46+
.leftJoin("claims", "metadata.uri", "claims.uri")
47+
.selectAll("metadata")
48+
.where((eb) => {
49+
const ors: Expression<SqlBool>[] = [];
50+
51+
if (args.claims_id) {
52+
ors.push(eb("claims.id", "=", args.claims_id));
53+
}
54+
55+
if (args.hypercert_id) {
56+
ors.push(eb("claims.hypercert_id", "=", args.hypercert_id));
57+
}
58+
59+
return eb.or(ors);
60+
})
61+
.executeTakeFirst();
62+
63+
return result;
64+
}
65+
66+
async getHypercertMetadataSets(args: {
67+
claims_ids?: string[];
68+
hypercert_ids?: string[];
69+
}) {
70+
return this.cachingKyselyService
71+
.getConnection()
72+
.selectFrom("metadata")
73+
.leftJoin("claims", "metadata.uri", "claims.uri")
74+
.selectAll("metadata")
75+
.where((eb) => {
76+
const ors: Expression<SqlBool>[] = [];
77+
78+
if (args.claims_ids) {
79+
ors.push(eb("claims.id", "in", args.claims_ids));
80+
}
81+
82+
if (args.hypercert_ids) {
83+
ors.push(eb("claims.hypercert_id", "in", args.hypercert_ids));
84+
}
85+
86+
return eb.or(ors);
87+
})
88+
.execute();
89+
}
3590
}

src/services/database/entities/MetadataEntityService.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,22 @@ import {
1010

1111
export type MetadataSelect = Selectable<CachingDatabase["metadata"]>;
1212

13+
/**
14+
* Service for handling metadata operations in the system.
15+
* Provides methods for retrieving metadata records with support for filtering and relationships.
16+
*
17+
* Metadata represents descriptive information about hypercerts, including:
18+
* - Basic information (name, description)
19+
* - Work and impact timeframes
20+
* - Contributors and rights
21+
* - External references (URLs, URIs)
22+
* - Custom properties
23+
*
24+
* This service uses an EntityService for database operations, providing:
25+
* - Consistent error handling
26+
* - Type safety through Kysely
27+
* - Caching support
28+
*/
1329
@injectable()
1430
export class MetadataService {
1531
private entityService: EntityService<
@@ -25,10 +41,47 @@ export class MetadataService {
2541
>("metadata", "MetadataEntityService", kyselyCaching);
2642
}
2743

44+
/**
45+
* Retrieves multiple metadata records based on provided arguments.
46+
*
47+
* @param args - Query arguments for filtering metadata records
48+
* @returns A promise resolving to:
49+
* - data: Array of metadata records matching the criteria
50+
* - count: Total number of matching records
51+
*
52+
* @example
53+
* ```typescript
54+
* const result = await metadataService.getMetadata({
55+
* where: {
56+
* hypercerts: {
57+
* id: { eq: "some-hypercert-id" }
58+
* }
59+
* }
60+
* });
61+
* ```
62+
*/
2863
async getMetadata(args: GetMetadataArgs) {
2964
return this.entityService.getMany(args);
3065
}
3166

67+
/**
68+
* Retrieves a single metadata record based on provided arguments.
69+
* Useful when you expect exactly one matching record.
70+
*
71+
* @param args - Query arguments for filtering metadata records
72+
* @returns A promise resolving to:
73+
* - The matching metadata record if found
74+
* - undefined if no matching record exists
75+
*
76+
* @example
77+
* ```typescript
78+
* const metadata = await metadataService.getMetadataSingle({
79+
* where: {
80+
* uri: { eq: "ipfs://..." }
81+
* }
82+
* });
83+
* ```
84+
*/
3285
async getMetadataSingle(args: GetMetadataArgs) {
3386
return this.entityService.getSingle(args);
3487
}
Lines changed: 50 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { Kysely } from "kysely";
22
import { GetMetadataArgs } from "../../../graphql/schemas/args/metadataArgs.js";
3-
import { isWhereEmpty } from "../../../lib/strategies/isWhereEmpty.js";
43
import { CachingDatabase } from "../../../types/kyselySupabaseCaching.js";
54
import { MetadataSelect } from "../entities/MetadataEntityService.js";
65
import { QueryStrategy } from "./QueryStrategy.js";
@@ -27,8 +26,24 @@ const supportedColumns = [
2726
type MetadataSelection = Omit<MetadataSelect, "image">;
2827

2928
/**
30-
* Strategy for querying metadata
31-
* Handles joins with claims table and selects all columns except for the image column
29+
* Strategy for building database queries for metadata records.
30+
* Implements query logic for metadata retrieval and counting.
31+
*
32+
* This strategy handles:
33+
* - Basic metadata queries without filtering
34+
* - Selective column fetching (excludes large fields like 'image' by default)
35+
*
36+
* The strategy is designed to work with the metadata table schema:
37+
* - id: Unique identifier
38+
* - name: Hypercert name
39+
* - description: Detailed description
40+
* - work_scope, impact_scope: Scope definitions
41+
* - timeframe fields: Work and impact time ranges
42+
* - uri: IPFS or other content identifier
43+
* - properties: Additional custom properties
44+
*
45+
* Note: This strategy provides direct table access only. Any relationship
46+
* filtering (e.g., hypercert relationships) should be handled at the service level.
3247
*/
3348
export class MetadataQueryStrategy extends QueryStrategy<
3449
CachingDatabase,
@@ -38,33 +53,39 @@ export class MetadataQueryStrategy extends QueryStrategy<
3853
> {
3954
protected readonly tableName = "metadata" as const;
4055

41-
buildDataQuery(db: Kysely<CachingDatabase>, args?: GetMetadataArgs) {
42-
if (!args) {
43-
return db.selectFrom(this.tableName).select(supportedColumns);
44-
}
45-
46-
return db
47-
.selectFrom(this.tableName)
48-
.$if(!isWhereEmpty(args.where?.hypercerts), (qb) =>
49-
qb.innerJoin("claims", "claims.uri", "metadata.uri"),
50-
)
51-
.select(supportedColumns);
56+
/**
57+
* Builds a query to retrieve metadata records.
58+
* Returns all records with supported columns.
59+
*
60+
* @param db - Kysely database instance
61+
* @returns A query builder for retrieving metadata data
62+
*
63+
* @example
64+
* ```typescript
65+
* buildDataQuery(db);
66+
* // SELECT supported_columns FROM metadata
67+
* ```
68+
*/
69+
buildDataQuery(db: Kysely<CachingDatabase>) {
70+
return db.selectFrom(this.tableName).select(supportedColumns);
5271
}
5372

54-
buildCountQuery(db: Kysely<CachingDatabase>, args?: GetMetadataArgs) {
55-
if (!args) {
56-
return db.selectFrom(this.tableName).select((eb) => {
57-
return eb.fn.countAll().as("count");
58-
});
59-
}
60-
61-
return db
62-
.selectFrom(this.tableName)
63-
.$if(!isWhereEmpty(args.where?.hypercerts), (qb) =>
64-
qb.innerJoin("claims", "claims.uri", "metadata.uri"),
65-
)
66-
.select((eb) => {
67-
return eb.fn.countAll().as("count");
68-
});
73+
/**
74+
* Builds a query to count metadata records.
75+
* Returns total count of all records.
76+
*
77+
* @param db - Kysely database instance
78+
* @returns A query builder for counting metadata records
79+
*
80+
* @example
81+
* ```typescript
82+
* buildCountQuery(db);
83+
* // SELECT COUNT(*) as count FROM metadata
84+
* ```
85+
*/
86+
buildCountQuery(db: Kysely<CachingDatabase>) {
87+
return db.selectFrom(this.tableName).select((eb) => {
88+
return eb.fn.countAll().as("count");
89+
});
6990
}
7091
}

src/services/graphql/resolvers/attestationResolver.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -290,8 +290,8 @@ class AttestationResolver {
290290

291291
if (!attested_hypercert_id) return null;
292292

293-
return await this.metadataService.getMetadataSingle({
294-
where: { hypercerts: { hypercert_id: { eq: attested_hypercert_id } } },
293+
return await this.hypercertService.getHypercertMetadata({
294+
hypercert_id: attested_hypercert_id,
295295
});
296296
} catch (e) {
297297
console.error(

0 commit comments

Comments
 (0)