Skip to content
13 changes: 12 additions & 1 deletion apps/ensapi/src/graphql-api/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,14 @@ import type {
DomainId,
InterpretedName,
Node,
PermissionsId,
PermissionsResourceId,
PermissionsUserId,
RegistrationId,
RegistryId,
RenewalId,
ResolverId,
ResolverRecordsId,
} from "@ensnode/ensnode-sdk";

import type { context } from "@/graphql-api/context";
Expand All @@ -28,7 +34,12 @@ export const builder = new SchemaBuilder<{
DomainId: { Input: DomainId; Output: DomainId };
RegistryId: { Input: RegistryId; Output: RegistryId };
ResolverId: { Input: ResolverId; Output: ResolverId };
// PermissionsId: { Input: PermissionsId; Output: PermissionsId };
PermissionsId: { Input: PermissionsId; Output: PermissionsId };
PermissionsResourceId: { Input: PermissionsResourceId; Output: PermissionsResourceId };
PermissionsUserId: { Input: PermissionsUserId; Output: PermissionsUserId };
RegistrationId: { Input: RegistrationId; Output: RegistrationId };
RenewalId: { Input: RenewalId; Output: RenewalId };
ResolverRecordsId: { Input: ResolverRecordsId; Output: ResolverRecordsId };
};

// the following ensures via typechecker that every t.connection returns a totalCount field
Expand Down
3 changes: 1 addition & 2 deletions apps/ensapi/src/graphql-api/lib/lazy-connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ export const lazyConnection = <Edges, PageInfo>({
}) => {
let _conn: ReturnType<typeof connection> | null = null;
const memoizedConnection = () => {
if (_conn !== null) return _conn;
_conn = connection();
if (_conn === null) _conn = connection();
return _conn;
};

Expand Down
2 changes: 0 additions & 2 deletions apps/ensapi/src/graphql-api/schema.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { builder } from "@/graphql-api/builder";

import "./schema/account-id";
import "./schema/account-registries-permissions";
import "./schema/account-resolver-permissions";
import "./schema/connection";
import "./schema/domain";
import "./schema/event";
Expand Down

This file was deleted.

This file was deleted.

30 changes: 11 additions & 19 deletions apps/ensapi/src/graphql-api/schema/account.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { type ResolveCursorConnectionArgs, resolveCursorConnection } from "@pothos/plugin-relay";
import { and, count, eq } from "drizzle-orm";
import { and, count, eq, getTableColumns } from "drizzle-orm";
import type { Address } from "viem";

import * as schema from "@ensnode/ensnode-schema";
Expand All @@ -17,15 +17,15 @@ import {
import { getModelId } from "@/graphql-api/lib/get-model-id";
import { lazyConnection } from "@/graphql-api/lib/lazy-connection";
import { AccountIdInput } from "@/graphql-api/schema/account-id";
import { AccountRegistryPermissionsRef } from "@/graphql-api/schema/account-registries-permissions";
import { AccountResolverPermissionsRef } from "@/graphql-api/schema/account-resolver-permissions";
import { ID_PAGINATED_CONNECTION_ARGS } from "@/graphql-api/schema/constants";
import {
AccountDomainsWhereInput,
DomainInterfaceRef,
DomainsOrderInput,
} from "@/graphql-api/schema/domain";
import { PermissionsUserRef } from "@/graphql-api/schema/permissions";
import { RegistryPermissionsUserRef } from "@/graphql-api/schema/registry-permissions-user";
import { ResolverPermissionsUserRef } from "@/graphql-api/schema/resolver-permissions-user";
import { db } from "@/lib/db";

export const AccountRef = builder.loadableObjectRef("Account", {
Expand Down Expand Up @@ -90,7 +90,8 @@ AccountRef.implement({
// Account.permissions
///////////////////////
permissions: t.connection({
description: "The Permissions granted to this Account.",
description:
"The Permissions granted to this Account, optionally filtered to Permissions in a specific contract.",
type: PermissionsUserRef,
args: {
in: t.arg({ type: AccountIdInput }),
Expand Down Expand Up @@ -128,10 +129,9 @@ AccountRef.implement({
///////////////////////////////
// Account.registryPermissions
///////////////////////////////
// TODO: this returns all permissions in a registry, perhaps can provide api for non-token resources...
registryPermissions: t.connection({
description: "The Permissions on Registries granted to this Account.",
type: AccountRegistryPermissionsRef,
type: RegistryPermissionsUserRef,
resolve: (parent, args) => {
Comment thread
shrugs marked this conversation as resolved.
const scope = eq(schema.permissionsUser.user, parent.id);
const join = and(
Expand All @@ -152,16 +152,12 @@ AccountRef.implement({
{ ...ID_PAGINATED_CONNECTION_ARGS, args },
({ before, after, limit, inverted }: ResolveCursorConnectionArgs) =>
db
.select({
permissionsUser: schema.permissionsUser,
registry: schema.registry,
})
.select(getTableColumns(schema.permissionsUser))
.from(schema.permissionsUser)
.innerJoin(schema.registry, join)
.where(and(scope, paginateBy(schema.permissionsUser.id, before, after)))
.orderBy(orderPaginationBy(schema.permissionsUser.id, inverted))
.limit(limit)
.then((rows) => rows.map((r) => ({ id: r.permissionsUser.id, ...r }))),
.limit(limit),
),
});
},
Comment thread
shrugs marked this conversation as resolved.
Expand All @@ -172,7 +168,7 @@ AccountRef.implement({
///////////////////////////////
resolverPermissions: t.connection({
description: "The Permissions on Resolvers granted to this Account.",
type: AccountResolverPermissionsRef,
type: ResolverPermissionsUserRef,
resolve: (parent, args) => {
Comment thread
shrugs marked this conversation as resolved.
Comment thread
shrugs marked this conversation as resolved.
const scope = eq(schema.permissionsUser.user, parent.id);
const join = and(
Expand All @@ -193,16 +189,12 @@ AccountRef.implement({
{ ...ID_PAGINATED_CONNECTION_ARGS, args },
({ before, after, limit, inverted }: ResolveCursorConnectionArgs) =>
db
.select({
permissionsUser: schema.permissionsUser,
resolver: schema.resolver,
})
.select(getTableColumns(schema.permissionsUser))
.from(schema.permissionsUser)
.innerJoin(schema.resolver, join)
.where(and(scope, paginateBy(schema.permissionsUser.id, before, after)))
.orderBy(orderPaginationBy(schema.permissionsUser.id, inverted))
.limit(limit)
.then((rows) => rows.map((r) => ({ id: r.permissionsUser.id, ...r }))),
.limit(limit),
),
});
Comment thread
shrugs marked this conversation as resolved.
},
Expand Down
66 changes: 63 additions & 3 deletions apps/ensapi/src/graphql-api/schema/domain.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { type ResolveCursorConnectionArgs, resolveCursorConnection } from "@pothos/plugin-relay";
import { and, eq } from "drizzle-orm";
import { and, count, eq, getTableColumns } from "drizzle-orm";

import * as schema from "@ensnode/ensnode-schema";
import {
Expand All @@ -11,7 +11,7 @@ import {
} from "@ensnode/ensnode-sdk";

import { builder } from "@/graphql-api/builder";
import { orderPaginationBy, paginateByInt } from "@/graphql-api/lib/connection-helpers";
import { orderPaginationBy, paginateBy, paginateByInt } from "@/graphql-api/lib/connection-helpers";
import { resolveFindDomains } from "@/graphql-api/lib/find-domains/find-domains-resolver";
import {
domainsBase,
Expand All @@ -25,9 +25,13 @@ import { getModelId } from "@/graphql-api/lib/get-model-id";
import { lazyConnection } from "@/graphql-api/lib/lazy-connection";
import { rejectAnyErrors } from "@/graphql-api/lib/reject-any-errors";
import { AccountRef } from "@/graphql-api/schema/account";
import { INDEX_PAGINATED_CONNECTION_ARGS } from "@/graphql-api/schema/constants";
import {
ID_PAGINATED_CONNECTION_ARGS,
INDEX_PAGINATED_CONNECTION_ARGS,
} from "@/graphql-api/schema/constants";
import { LabelRef } from "@/graphql-api/schema/label";
import { OrderDirection } from "@/graphql-api/schema/order-direction";
import { PermissionsUserRef } from "@/graphql-api/schema/permissions";
import { RegistrationInterfaceRef } from "@/graphql-api/schema/registration";
import { RegistryRef } from "@/graphql-api/schema/registry";
import { ResolverRef } from "@/graphql-api/schema/resolver";
Expand Down Expand Up @@ -323,13 +327,69 @@ ENSv2DomainRef.implement({
nullable: true,
resolve: (parent) => parent.subregistryId,
}),

///////////////////////////
// ENSv2Domain.permissions
///////////////////////////
permissions: t.connection({
description:
"Permissions for this Domain within its Registry, representing the roles granted to users for this Domain's token.",
type: PermissionsUserRef,
args: {
where: t.arg({ type: DomainPermissionsWhereInput }),
},
resolve: (parent, args) => {
Comment thread
shrugs marked this conversation as resolved.
const scope = and(
// filter by resource === tokenId
eq(schema.permissionsUser.resource, parent.tokenId),
// optionally filter by user
args.where?.user ? eq(schema.permissionsUser.user, args.where.user) : undefined,
);

// inner join against this Domain's registry to filter Permissions by those in said registry
const join = and(
eq(schema.permissionsUser.chainId, schema.registry.chainId),
eq(schema.permissionsUser.address, schema.registry.address),
eq(schema.registry.id, parent.registryId),
);

return lazyConnection({
totalCount: () =>
db
.select({ count: count() })
.from(schema.permissionsUser)
.innerJoin(schema.registry, join)
.where(scope)
.then((r) => r[0].count),
connection: () =>
resolveCursorConnection(
{ ...ID_PAGINATED_CONNECTION_ARGS, args },
({ before, after, limit, inverted }: ResolveCursorConnectionArgs) =>
db
.select(getTableColumns(schema.permissionsUser))
.from(schema.permissionsUser)
.innerJoin(schema.registry, join)
.where(and(scope, paginateBy(schema.permissionsUser.id, before, after)))
.orderBy(orderPaginationBy(schema.permissionsUser.id, inverted))
.limit(limit),
),
});
},
Comment thread
shrugs marked this conversation as resolved.
}),
}),
});

//////////////////////
// Inputs
//////////////////////

export const DomainPermissionsWhereInput = builder.inputType("DomainPermissionsWhereInput", {
description: "Filter Permissions over this Domain by a specific User address.",
fields: (t) => ({
user: t.field({ type: "Address" }),
Comment thread
shrugs marked this conversation as resolved.
}),
});

export const DomainIdInput = builder.inputType("DomainIdInput", {
description: "Reference a specific Domain.",
isOneOf: true,
Expand Down
Loading
Loading