From 12038fc687276c4062da851d08405cadc1fcc944 Mon Sep 17 00:00:00 2001 From: bitbeckers Date: Tue, 11 Feb 2025 11:41:02 +0100 Subject: [PATCH] feat(service): introduce image fetching service Refactors the SupabaseCachingService to get all hypercert columns except for the image. Renamed the getMetadata method to getMetadataWithoutImage to make this explicit. Introduces MetadataImageService for fetching the image when the field is selected in a metadata query. --- src/graphql/schemas/resolvers/baseTypes.ts | 5 ++- .../schemas/resolvers/fractionResolver.ts | 2 +- .../schemas/resolvers/hyperboardResolver.ts | 2 +- .../schemas/resolvers/hypercertResolver.ts | 2 +- .../schemas/resolvers/metadataResolver.ts | 29 +++++++++++++- .../schemas/resolvers/orderResolver.ts | 2 +- .../schemas/resolvers/salesResolver.ts | 2 +- .../schemas/typeDefs/metadataTypeDefs.ts | 5 --- src/services/MetadataImageService.ts | 30 ++++++++++++++ src/services/SupabaseCachingService.ts | 39 ++++++++++++++++++- 10 files changed, 102 insertions(+), 16 deletions(-) create mode 100644 src/services/MetadataImageService.ts diff --git a/src/graphql/schemas/resolvers/baseTypes.ts b/src/graphql/schemas/resolvers/baseTypes.ts index 1bafa13..e02e7d8 100644 --- a/src/graphql/schemas/resolvers/baseTypes.ts +++ b/src/graphql/schemas/resolvers/baseTypes.ts @@ -38,13 +38,14 @@ export function createBaseResolver( readonly supabaseCachingService = container.resolve(SupabaseCachingService); readonly supabaseDataService = container.resolve(SupabaseDataService); - getMetadata(args: GetMetadataArgs, single: boolean = false) { + getMetadataWithoutImage(args: GetMetadataArgs, single: boolean = false) { console.debug( `[${entityFieldName}Resolver::getMetadata] Fetching metadata`, ); try { - const queries = this.supabaseCachingService.getMetadata(args); + const queries = + this.supabaseCachingService.getMetadataWithoutImage(args); if (single) { return queries.data.executeTakeFirst(); } diff --git a/src/graphql/schemas/resolvers/fractionResolver.ts b/src/graphql/schemas/resolvers/fractionResolver.ts index d3653c1..b2c1cf0 100644 --- a/src/graphql/schemas/resolvers/fractionResolver.ts +++ b/src/graphql/schemas/resolvers/fractionResolver.ts @@ -29,7 +29,7 @@ class FractionResolver extends FractionBaseResolver { return; } - return await this.getMetadata( + return await this.getMetadataWithoutImage( { where: { hypercerts: { id: { eq: fraction.claims_id } } }, }, diff --git a/src/graphql/schemas/resolvers/hyperboardResolver.ts b/src/graphql/schemas/resolvers/hyperboardResolver.ts index 1ed4086..b29dc85 100644 --- a/src/graphql/schemas/resolvers/hyperboardResolver.ts +++ b/src/graphql/schemas/resolvers/hyperboardResolver.ts @@ -42,7 +42,7 @@ class HyperboardResolver extends HyperboardBaseResolver { }).then((res) => res.data), ]); - const metadata = await this.getMetadata({ + const metadata = await this.getMetadataWithoutImage({ where: { hypercerts: { hypercert_id: { in: hypercertIds } } }, }) .then((res) => res.data) diff --git a/src/graphql/schemas/resolvers/hypercertResolver.ts b/src/graphql/schemas/resolvers/hypercertResolver.ts index a3df1a6..a97ed4c 100644 --- a/src/graphql/schemas/resolvers/hypercertResolver.ts +++ b/src/graphql/schemas/resolvers/hypercertResolver.ts @@ -38,7 +38,7 @@ class HypercertResolver extends HypercertBaseResolver { return; } - return await this.getMetadata( + return await this.getMetadataWithoutImage( { where: { uri: { eq: hypercert.uri } } }, true, ); diff --git a/src/graphql/schemas/resolvers/metadataResolver.ts b/src/graphql/schemas/resolvers/metadataResolver.ts index 20885b0..1f56ded 100644 --- a/src/graphql/schemas/resolvers/metadataResolver.ts +++ b/src/graphql/schemas/resolvers/metadataResolver.ts @@ -1,18 +1,43 @@ -import { Args, ObjectType, Query, Resolver } from "type-graphql"; +import { + Args, + FieldResolver, + ObjectType, + Query, + Resolver, + Root, +} from "type-graphql"; +import { inject, singleton } from "tsyringe"; import { Metadata } from "../typeDefs/metadataTypeDefs.js"; import { GetMetadataArgs } from "../args/metadataArgs.js"; import { createBaseResolver, DataResponse } from "./baseTypes.js"; +import { MetadataImageService } from "../../../services/MetadataImageService.js"; @ObjectType() export class GetMetadataResponse extends DataResponse(Metadata) {} const MetadataBaseResolver = createBaseResolver("metadata"); +@singleton() @Resolver(() => Metadata) class MetadataResolver extends MetadataBaseResolver { + constructor( + @inject(MetadataImageService) private imageService: MetadataImageService, + ) { + super(); + } + @Query(() => GetMetadataResponse) async metadata(@Args() args: GetMetadataArgs) { - return await this.getMetadata(args); + return await this.getMetadataWithoutImage(args); + } + + @FieldResolver(() => String, { + nullable: true, + description: "Base64 encoded representation of the image of the hypercert", + }) + async image(@Root() metadata: Metadata) { + if (!metadata.uri) return null; + return await this.imageService.getImageByUri(metadata.uri); } } diff --git a/src/graphql/schemas/resolvers/orderResolver.ts b/src/graphql/schemas/resolvers/orderResolver.ts index e780ac0..0e7947b 100644 --- a/src/graphql/schemas/resolvers/orderResolver.ts +++ b/src/graphql/schemas/resolvers/orderResolver.ts @@ -107,7 +107,7 @@ class OrderResolver extends OrderBaseResolver { true, ); - const metadata = await this.getMetadata( + const metadata = await this.getMetadataWithoutImage( { where: { hypercerts: { diff --git a/src/graphql/schemas/resolvers/salesResolver.ts b/src/graphql/schemas/resolvers/salesResolver.ts index 76fadd3..760079c 100644 --- a/src/graphql/schemas/resolvers/salesResolver.ts +++ b/src/graphql/schemas/resolvers/salesResolver.ts @@ -48,7 +48,7 @@ class SalesResolver extends SalesBaseResolver { return null; } - const metadata = await this.getMetadata( + const metadata = await this.getMetadataWithoutImage( { where: { hypercerts: { diff --git a/src/graphql/schemas/typeDefs/metadataTypeDefs.ts b/src/graphql/schemas/typeDefs/metadataTypeDefs.ts index 71a40d7..99c325e 100644 --- a/src/graphql/schemas/typeDefs/metadataTypeDefs.ts +++ b/src/graphql/schemas/typeDefs/metadataTypeDefs.ts @@ -14,11 +14,6 @@ class Metadata extends BasicTypeDef { name?: string; @Field({ nullable: true, description: "Description of the hypercert" }) description?: string; - @Field({ - nullable: true, - description: "Base64 encoded representation of the image of the hypercert", - }) - image?: string; @Field({ nullable: true, description: "URI of the hypercert metadata" }) uri?: string; @Field({ diff --git a/src/services/MetadataImageService.ts b/src/services/MetadataImageService.ts new file mode 100644 index 0000000..7f14e69 --- /dev/null +++ b/src/services/MetadataImageService.ts @@ -0,0 +1,30 @@ +import { singleton } from "tsyringe"; +import { kyselyCaching } from "../client/kysely.js"; +import { CachingDatabase } from "../types/kyselySupabaseCaching.js"; +import { BaseSupabaseService } from "./BaseSupabaseService.js"; + +@singleton() +export class MetadataImageService extends BaseSupabaseService { + constructor() { + super(kyselyCaching); + } + + // TODO: remove these when we more refactor the services to improve typing and performance + getDataQuery() { + throw new Error("Method not implemented - not needed for image service"); + } + + getCountQuery() { + throw new Error("Method not implemented - not needed for image service"); + } + + async getImageByUri(uri: string): Promise { + const result = await this.db + .selectFrom("metadata") + .select(["image"]) + .where("uri", "=", uri) + .executeTakeFirst(); + + return result?.image ?? null; + } +} diff --git a/src/services/SupabaseCachingService.ts b/src/services/SupabaseCachingService.ts index 89670b5..f12e238 100644 --- a/src/services/SupabaseCachingService.ts +++ b/src/services/SupabaseCachingService.ts @@ -56,7 +56,7 @@ export class SupabaseCachingService extends BaseSupabaseService }; } - getMetadata(args: GetMetadataArgs) { + getMetadataWithoutImage(args: GetMetadataArgs) { return { data: this.handleGetData("metadata", args), count: this.handleGetCount("metadata", args), @@ -138,9 +138,44 @@ export class SupabaseCachingService extends BaseSupabaseService case "fractions_view": return this.db.selectFrom("fractions_view").selectAll(); case "metadata": + // Skip the image column + // 1. id + // 2. name + // 3. description + // 4. image + // 5. external_url + // 6. work_scope + // 7. work_timeframe_from + // 8. work_timeframe_to + // 9. impact_scope + // 10. impact_timeframe_from + // 11. impact_timeframe_to + // 12. contributors + // 13. rights + // 14. uri + // 15. properties + // 16. allow_list_uri + // 17. parsed return this.db .selectFrom("metadata") - .selectAll("metadata") + .select([ + "id", + "name", + "description", + "external_url", + "work_scope", + "work_timeframe_from", + "work_timeframe_to", + "impact_scope", + "impact_timeframe_from", + "impact_timeframe_to", + "contributors", + "rights", + "uri", + "properties", + "allow_list_uri", + "parsed", + ]) .$if(args.where?.hypercerts, (qb) => qb.innerJoin("claims", "claims.uri", "metadata.uri"), );