From 869a3e320c209be8dd49178c820262360c3495e5 Mon Sep 17 00:00:00 2001 From: Carson Full Date: Thu, 15 May 2025 23:00:57 -0500 Subject: [PATCH 1/3] Fix inheritance on ID/ResourceName types I think this must've changed when we removed the static Props declarations. Previously comparing static types worked, probably because of the static Prop list. Now comparing instance types appears to work, which seems more correct anyway. --- src/core/resources/resource-name.types.ts | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/core/resources/resource-name.types.ts b/src/core/resources/resource-name.types.ts index 82aa3b3f17..e88202656e 100644 --- a/src/core/resources/resource-name.types.ts +++ b/src/core/resources/resource-name.types.ts @@ -65,15 +65,21 @@ type ResourceNameFromStatic< IncludeSubclasses extends boolean = false, > = ResourceShape extends TResourceStatic ? string // short-circuit non-specific types - : { - [Name in keyof ResourceMap]: ResourceMap[Name] extends TResourceStatic // Only self or subclasses - ? IncludeSubclasses extends true - ? Name - : TResourceStatic extends ResourceMap[Name] // Exclude subclasses - ? Name + : InstanceType extends infer TResource + ? { + [Name in keyof ResourceMap]: InstanceType< + ResourceMap[Name] + > extends infer Other + ? Other extends TResource // Only self or subclasses + ? IncludeSubclasses extends true + ? Name + : TResource extends Other // Exclude subclasses + ? Name + : never : never : never; - }[keyof ResourceMap]; + }[keyof ResourceMap] + : never; type ResourceNameFromDBName = // eslint-disable-next-line @typescript-eslint/naming-convention From 750a2d03aa28859cc707817a2d38a2a8c4a4a84a Mon Sep 17 00:00:00 2001 From: Carson Full Date: Fri, 16 May 2025 15:34:41 -0500 Subject: [PATCH 2/3] Declare typename for Actors to help distinguish against other resources --- src/components/user/dto/actor.dto.ts | 4 +++- src/components/user/dto/user.dto.ts | 2 ++ .../user/system-agent.gel.repository.ts | 22 +++++++++---------- .../user/system-agent.neo4j.repository.ts | 8 ++++--- 4 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/components/user/dto/actor.dto.ts b/src/components/user/dto/actor.dto.ts index 8fe1559ffc..c42c3874c7 100644 --- a/src/components/user/dto/actor.dto.ts +++ b/src/components/user/dto/actor.dto.ts @@ -19,6 +19,8 @@ import { e } from '~/core/gel'; resolveType: resolveByTypename(Actor.name), }) export class Actor extends DataObject { + declare readonly __typename: 'User' | 'SystemAgent'; + @IdField() readonly id: ID; } @@ -31,7 +33,7 @@ export class Actor extends DataObject { implements: [Actor], }) export abstract class SystemAgent extends Actor { - __typename?: 'SystemAgent'; + declare readonly __typename: 'SystemAgent'; @NameField() readonly name: string; diff --git a/src/components/user/dto/user.dto.ts b/src/components/user/dto/user.dto.ts index 39f5d2805e..fe5e6fdd48 100644 --- a/src/components/user/dto/user.dto.ts +++ b/src/components/user/dto/user.dto.ts @@ -45,6 +45,8 @@ export class User extends Interfaces { ...Commentable.Relations, } satisfies ResourceRelationsShape); + declare readonly __typename: 'User'; + @Field() @DbUnique('EmailAddress') email: SecuredStringNullable; diff --git a/src/components/user/system-agent.gel.repository.ts b/src/components/user/system-agent.gel.repository.ts index 96276b7e65..9625363151 100644 --- a/src/components/user/system-agent.gel.repository.ts +++ b/src/components/user/system-agent.gel.repository.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; import { type Role } from '~/common'; -import { disableAccessPolicies, edgeql, Gel } from '~/core/gel'; +import { disableAccessPolicies, e, Gel } from '~/core/gel'; import { SystemAgentRepository } from './system-agent.repository'; @Injectable() @@ -12,15 +12,15 @@ export class SystemAgentGelRepository extends SystemAgentRepository { } protected async upsertAgent(name: string, roles?: readonly Role[]) { - const query = edgeql(` - select ( - (select SystemAgent filter .name = $name) ?? - (insert SystemAgent { - name := $name, - roles := array_unpack(>$roles) - }) - ) {*} - `); - return await this.db.run(query, { name, roles }); + const upserted = e.op( + e.select(e.SystemAgent, () => ({ filter_single: { name } })), + '??', + e.insert(e.SystemAgent, { name, roles }), + ); + const query = e.select(upserted, (agent) => ({ + __typename: e.str('SystemAgent' as const), + ...agent['*'], + })); + return await this.db.run(query); } } diff --git a/src/components/user/system-agent.neo4j.repository.ts b/src/components/user/system-agent.neo4j.repository.ts index d4cef2365e..9151d83eaf 100644 --- a/src/components/user/system-agent.neo4j.repository.ts +++ b/src/components/user/system-agent.neo4j.repository.ts @@ -1,7 +1,9 @@ import { Injectable } from '@nestjs/common'; import { node } from 'cypher-query-builder'; -import { type ID, type Role } from '~/common'; +import { type Role } from '~/common'; import { DatabaseService } from '~/core/database'; +import { merge } from '~/core/database/query'; +import { type SystemAgent } from './dto'; import { SystemAgentRepository } from './system-agent.repository'; @Injectable() @@ -22,8 +24,8 @@ export class SystemAgentNeo4jRepository extends SystemAgentRepository { 'agent.roles': roles ?? [], }, }) - .return<{ agent: { id: ID; name: string; roles: readonly Role[] } }>( - 'apoc.convert.toMap(agent) AS agent', + .return<{ agent: SystemAgent }>( + merge('agent', { __typename: '"SystemAgent"' }).as('agent'), ) .first(); return res!.agent; From dd5eb2281027d44effc524055480ca3652059d96 Mon Sep 17 00:00:00 2001 From: Carson Full Date: Fri, 16 May 2025 15:36:55 -0500 Subject: [PATCH 3/3] Declare narrower `type` for TranslationProject --- src/components/project/dto/project.dto.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/project/dto/project.dto.ts b/src/components/project/dto/project.dto.ts index d6fa3a86f2..1e212f3d99 100644 --- a/src/components/project/dto/project.dto.ts +++ b/src/components/project/dto/project.dto.ts @@ -220,7 +220,9 @@ export { Project as IProject, type AnyProject as Project }; resolveType: resolveProjectType, implements: [Project], }) -export class TranslationProject extends Project {} +export class TranslationProject extends Project { + declare readonly type: 'MultiplicationTranslation' | 'MomentumTranslation'; +} @RegisterResource({ db: e.MomentumTranslationProject }) @ObjectType({