Skip to content

Commit 68e8532

Browse files
committed
feat(attestation): implement comprehensive attestation resolver and services
Introduced a robust implementation for attestation-related functionality: - Migrated attestation resolver to services directory for better code organization - Enhanced HypercertPointer schema validation with improved type handling - Added comprehensive test coverage for AttestationResolver, AttestationEntityService, and AttestationQueryStrategy - Implemented advanced parsing and validation for attestation data - Improved hypercert ID generation with flexible input handling - Added detailed documentation for resolver methods and services
1 parent f4a3d97 commit 68e8532

10 files changed

Lines changed: 1271 additions & 111 deletions

File tree

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"commitlint": "commitlint --config commitlintrc.ts --edit"
2727
},
2828
"dependencies": {
29+
"@faker-js/faker": "^9.6.0",
2930
"@graphql-tools/merge": "^9.0.19",
3031
"@graphql-yoga/plugin-response-cache": "^3.13.0",
3132
"@hypercerts-org/contracts": "2.0.0-alpha.12",

pnpm-lock.yaml

Lines changed: 19 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/graphql/schemas/resolvers/attestationResolver.ts

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

src/graphql/schemas/resolvers/composed.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { HypercertResolver } from "./hypercertResolver.js";
22
import { MetadataResolver } from "./metadataResolver.js";
33
import { ContractResolver } from "./contractResolver.js";
44
import { FractionResolver } from "./fractionResolver.js";
5-
import { AttestationResolver } from "./attestationResolver.js";
5+
import { AttestationResolver } from "../../../services/graphql/resolvers/attestationResolver.js";
66
import { AttestationSchemaResolver } from "./attestationSchemaResolver.js";
77
import { OrderResolver } from "./orderResolver.js";
88
import { HyperboardResolver } from "./hyperboardResolver.js";

src/services/database/entities/AttestationEntityService.ts

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,18 @@ import {
1111

1212
export type AttestationSelect = Selectable<CachingDatabase["attestations"]>;
1313

14+
/**
15+
* Service for managing attestation entities in the database.
16+
* Handles CRUD operations for attestations, including data parsing and validation.
17+
*
18+
* This service:
19+
* - Provides methods for retrieving single or multiple attestations
20+
* - Handles parsing of attestation data, particularly bigint conversions
21+
* - Uses an EntityService for database operations
22+
* - Supports filtering by attestation fields and related entities
23+
*
24+
* @injectable Marks the class as injectable for dependency injection
25+
*/
1426
@injectable()
1527
export class AttestationService {
1628
private entityService: EntityService<
@@ -26,6 +38,29 @@ export class AttestationService {
2638
>("attestations", "AttestationEntityService", kyselyCaching);
2739
}
2840

41+
/**
42+
* Retrieves multiple attestations based on provided arguments.
43+
* Handles filtering and parsing of attestation data.
44+
*
45+
* @param args - Query arguments for filtering attestations
46+
* @returns Promise resolving to:
47+
* - data: Array of attestations with parsed data
48+
* - count: Total number of matching attestations
49+
* @throws {Error} If the database query fails
50+
*
51+
* @example
52+
* ```typescript
53+
* // Get attestations by ID
54+
* const result = await attestationService.getAttestations({
55+
* where: { id: { eq: "123" } }
56+
* });
57+
*
58+
* // Get attestations by related schema
59+
* const result = await attestationService.getAttestations({
60+
* where: { eas_schema: { id: { eq: "schema-id" } } }
61+
* });
62+
* ```
63+
*/
2964
async getAttestations(args: GetAttestationsArgs) {
3065
const respone = await this.entityService.getMany(args);
3166
return {
@@ -37,15 +72,42 @@ export class AttestationService {
3772
};
3873
}
3974

75+
/**
76+
* Retrieves a single attestation based on provided arguments.
77+
*
78+
* @param args - Query arguments for filtering attestations
79+
* @returns Promise resolving to:
80+
* - The found attestation if it exists
81+
* - undefined if no attestation matches the query
82+
* @throws {Error} If the database query fails
83+
*
84+
* @example
85+
* ```typescript
86+
* const attestation = await attestationService.getAttestation({
87+
* where: { id: { eq: "123" } }
88+
* });
89+
* ```
90+
*/
4091
async getAttestation(args: GetAttestationsArgs) {
41-
const attestation = await this.entityService.getSingle(args);
42-
if (!attestation) {
43-
throw new Error("Attestation not found");
44-
}
45-
return attestation;
92+
return await this.entityService.getSingle(args);
4693
}
4794

48-
// Parses the attestation.data field to ensure bigints are converted to strings
95+
/**
96+
* Parses attestation data, converting bigint values to strings.
97+
* This is necessary because GraphQL cannot handle bigint values directly.
98+
*
99+
* @param data - Raw attestation data from the database
100+
* @returns Parsed data with bigint values converted to strings
101+
*
102+
* @example
103+
* ```typescript
104+
* const parsed = attestationService.parseAttestation({
105+
* token_id: 123456789n,
106+
* other_field: "value"
107+
* });
108+
* // parsed = { token_id: "123456789", other_field: "value" }
109+
* ```
110+
*/
49111
parseAttestation(data: Json) {
50112
// TODO cleaner handling of bigints in created attestations
51113
if (

src/services/database/strategies/AttestationQueryStrategy.ts

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,13 @@ import { QueryStrategy } from "./QueryStrategy.js";
55
import { isWhereEmpty } from "../../../lib/strategies/isWhereEmpty.js";
66

77
/**
8-
* Strategy for querying attestations
9-
* Handles joins with claims, metadata, and supported schemas tables
8+
* Strategy for building database queries for attestations.
9+
* Implements complex query logic for attestation retrieval, including:
10+
* - Joins with related tables (claims, supported_schemas)
11+
* - Filtering based on related entities
12+
* - Count queries for total matching records
13+
*
14+
* This strategy extends the base QueryStrategy to provide attestation-specific query building.
1015
*/
1116
export class AttestationsQueryStrategy extends QueryStrategy<
1217
CachingDatabase,
@@ -15,6 +20,30 @@ export class AttestationsQueryStrategy extends QueryStrategy<
1520
> {
1621
protected readonly tableName = "attestations" as const;
1722

23+
/**
24+
* Builds a query to retrieve attestation data with optional filtering.
25+
* Handles complex joins and relationships with other tables.
26+
*
27+
* @param db - Kysely database instance
28+
* @param args - Optional query arguments for filtering
29+
* @returns A query builder for retrieving attestation data
30+
*
31+
* Key features:
32+
* - Joins with supported_schemas when eas_schema filter is present
33+
* - Joins with claims when hypercert filter is present
34+
* - Returns all columns from the attestations table
35+
*
36+
* @example
37+
* ```typescript
38+
* // Basic query without filters
39+
* buildDataQuery(db);
40+
* // SELECT * FROM attestations
41+
*
42+
* // Query with schema filter
43+
* buildDataQuery(db, { where: { eas_schema: { id: { eq: 'schema-id' } } } });
44+
* // SELECT * FROM attestations WHERE EXISTS (SELECT * FROM supported_schemas ...)
45+
* ```
46+
*/
1847
buildDataQuery(db: Kysely<CachingDatabase>, args?: GetAttestationsArgs) {
1948
if (!args) {
2049
return db.selectFrom(this.tableName).selectAll();
@@ -46,6 +75,19 @@ export class AttestationsQueryStrategy extends QueryStrategy<
4675
.selectAll();
4776
}
4877

78+
/**
79+
* Builds a query to count attestations with optional filtering.
80+
* Uses the same filtering logic as buildDataQuery but returns a count.
81+
*
82+
* @param db - Kysely database instance
83+
* @param args - Optional query arguments for filtering
84+
* @returns A query builder for counting attestations
85+
*
86+
* Key features:
87+
* - Applies the same joins and filters as buildDataQuery
88+
* - Returns a count of matching attestations
89+
* - Optimized for counting by selecting only the count
90+
*/
4991
buildCountQuery(db: Kysely<CachingDatabase>, args?: GetAttestationsArgs) {
5092
if (!args) {
5193
return db.selectFrom(this.tableName).select((eb) => {

0 commit comments

Comments
 (0)