Skip to content

Commit 8f958df

Browse files
committed
handle nested relations
1 parent 3e433d4 commit 8f958df

File tree

8 files changed

+122
-174
lines changed

8 files changed

+122
-174
lines changed

apps/events/src/components/playground.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Event } from '../schema';
44
import { Button } from './ui/button';
55

66
export const Playground = ({ spaceId }: { spaceId: string }) => {
7-
const { data, isLoading, isError } = useQuery(Event, {
7+
const { data, isLoading, isError, invalidEntities } = useQuery(Event, {
88
mode: 'public',
99
include: {
1010
sponsors: {
@@ -24,7 +24,7 @@ export const Playground = ({ spaceId }: { spaceId: string }) => {
2424

2525
const deleteEntity = _useDeleteEntityPublic(Event, { space: spaceId });
2626

27-
console.log({ isLoading, isError, data });
27+
console.log({ isLoading, isError, data, invalidEntities });
2828

2929
return (
3030
<div>

apps/events/src/mapping.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,29 +33,29 @@ export const mapping: Mapping.Mapping = {
3333
},
3434
},
3535
JobOffer: {
36-
typeIds: [Id('f60585af-71b6-4674-9a26-b74ca6c1cceb')],
36+
typeIds: [Id('a4c1b288-756e-477b-aab2-007decf01c61')],
3737
properties: {
3838
name: Id('a126ca53-0c8e-48d5-b888-82c734c38935'),
39-
salary: Id('baa36ac9-78ac-4cf7-8394-6b2d3006bebe'),
39+
salary: Id('86ff5361-b820-4ba8-b689-b48e815e07d2'),
4040
},
4141
},
4242
Company: {
43-
typeIds: [Id('6c504df5-1a8f-43d1-bf2d-1ef9fa5b08b5')],
43+
typeIds: [Id('bcf56f59-c532-47d5-a005-2d802f512c85')],
4444
properties: {
4545
name: Id('a126ca53-0c8e-48d5-b888-82c734c38935'),
4646
},
4747
relations: {
48-
jobOffers: Id('1203064e-9741-4235-89d4-97f4b22eddfb'),
48+
jobOffers: Id('54190b30-1c68-499c-9ed8-5c6190810e31'),
4949
},
5050
},
5151
Event: {
52-
typeIds: [Id('7f9562d4-034d-4385-bf5c-f02cdebba47a')],
52+
typeIds: [Id('239bc639-938e-427c-bebb-d562d82ae272')],
5353
properties: {
5454
name: Id('a126ca53-0c8e-48d5-b888-82c734c38935'),
5555
description: Id('9b1f76ff-9711-404c-861e-59dc3fa7d037'),
5656
},
5757
relations: {
58-
sponsors: Id('6860bfac-f703-4289-b789-972d0aaf3abe'),
58+
sponsors: Id('926b00ee-68b5-4462-a27f-3806af705118'),
5959
},
6060
},
6161
Todo3: {

apps/events/src/routes/playground.lazy.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { CreateEvents } from '@/components/create-events';
22
import { CreatePropertiesAndTypesEvent } from '@/components/create-properties-and-types-event';
33
import { Playground } from '@/components/playground';
4-
import { Projects } from '@/components/projects';
54
import { HypergraphSpaceProvider } from '@graphprotocol/hypergraph-react';
65
import { createLazyFileRoute } from '@tanstack/react-router';
76

@@ -10,12 +9,12 @@ export const Route = createLazyFileRoute('/playground')({
109
});
1110

1211
function RouteComponent() {
13-
const space = 'a393e509-ae56-4d99-987c-bed71d9db631';
12+
const space = '282aee96-48b0-4c6e-b020-736430a82a87';
1413
return (
1514
<>
1615
{/* <Event spaceId={space} entityId="cf7c620b-d724-498f-b134-8280dc8249ae" /> */}
1716
<Playground spaceId={space} />
18-
<Projects spaceId="3f32353d-3b27-4a13-b71a-746f06e1f7db" />
17+
{/* <Projects spaceId="3f32353d-3b27-4a13-b71a-746f06e1f7db" /> */}
1918
<HypergraphSpaceProvider space={space}>
2019
<div className="flex flex-col gap-4 max-w-(--breakpoint-sm) mx-auto py-8">
2120
<h1 className="text-2xl font-bold">Playground</h1>

apps/events/src/schema.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,10 @@ export const JobOffer = EntitySchema(
5656
salary: Type.Number,
5757
},
5858
{
59-
types: [Id('bffa181e-a333-495b-949c-57f2831d7eca')],
59+
types: [Id('a4c1b288-756e-477b-aab2-007decf01c61')],
6060
properties: {
6161
name: Id('a126ca53-0c8e-48d5-b888-82c734c38935'),
62-
salary: Id('baa36ac9-78ac-4cf7-8394-6b2d3006bebe'),
62+
salary: Id('86ff5361-b820-4ba8-b689-b48e815e07d2'),
6363
},
6464
},
6565
);
@@ -70,10 +70,10 @@ export const Company = EntitySchema(
7070
jobOffers: Type.Relation(JobOffer),
7171
},
7272
{
73-
types: [Id('6c504df5-1a8f-43d1-bf2d-1ef9fa5b08b5')],
73+
types: [Id('bcf56f59-c532-47d5-a005-2d802f512c85')],
7474
properties: {
7575
name: Id('a126ca53-0c8e-48d5-b888-82c734c38935'),
76-
jobOffers: Id('1203064e-9741-4235-89d4-97f4b22eddfb'),
76+
jobOffers: Id('54190b30-1c68-499c-9ed8-5c6190810e31'),
7777
},
7878
},
7979
);
@@ -85,11 +85,11 @@ export const Event = EntitySchema(
8585
sponsors: Type.Relation(Company),
8686
},
8787
{
88-
types: [Id('7f9562d4-034d-4385-bf5c-f02cdebba47a')],
88+
types: [Id('239bc639-938e-427c-bebb-d562d82ae272')],
8989
properties: {
9090
name: Id('a126ca53-0c8e-48d5-b888-82c734c38935'),
9191
description: Id('9b1f76ff-9711-404c-861e-59dc3fa7d037'),
92-
sponsors: Id('6860bfac-f703-4289-b789-972d0aaf3abe'),
92+
sponsors: Id('926b00ee-68b5-4462-a27f-3806af705118'),
9393
},
9494
},
9595
);

apps/privy-login-example/src/routes/playground.lazy.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
import { HypergraphSpaceProvider } from '@graphprotocol/hypergraph-react';
2-
import { createLazyFileRoute } from '@tanstack/react-router';
31
import { CreateEvents } from '@/components/create-events';
42
import { CreatePropertiesAndTypesEvent } from '@/components/create-properties-and-types-event';
53
import { Event } from '@/components/event';
64
import { Playground } from '@/components/playground';
5+
import { HypergraphSpaceProvider } from '@graphprotocol/hypergraph-react';
6+
import { createLazyFileRoute } from '@tanstack/react-router';
77

88
export const Route = createLazyFileRoute('/playground')({
99
component: RouteComponent,
1010
});
1111

1212
function RouteComponent() {
13-
const space = 'a393e509-ae56-4d99-987c-bed71d9db631';
13+
const space = '282aee96-48b0-4c6e-b020-736430a82a87';
1414
return (
1515
<>
1616
<Event spaceId={space} entityId="cf7c620b-d724-498f-b134-8280dc8249ae" />

apps/privy-login-example/src/schema.ts

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,10 @@ export const JobOffer = EntitySchema(
5656
salary: Type.Number,
5757
},
5858
{
59-
types: [Id('bffa181e-a333-495b-949c-57f2831d7eca')],
59+
types: [Id('a4c1b288-756e-477b-aab2-007decf01c61')],
6060
properties: {
6161
name: Id('a126ca53-0c8e-48d5-b888-82c734c38935'),
62-
salary: Id('baa36ac9-78ac-4cf7-8394-6b2d3006bebe'),
62+
salary: Id('86ff5361-b820-4ba8-b689-b48e815e07d2'),
6363
},
6464
},
6565
);
@@ -70,10 +70,10 @@ export const Company = EntitySchema(
7070
jobOffers: Type.Relation(JobOffer),
7171
},
7272
{
73-
types: [Id('6c504df5-1a8f-43d1-bf2d-1ef9fa5b08b5')],
73+
types: [Id('bcf56f59-c532-47d5-a005-2d802f512c85')],
7474
properties: {
7575
name: Id('a126ca53-0c8e-48d5-b888-82c734c38935'),
76-
jobOffers: Id('1203064e-9741-4235-89d4-97f4b22eddfb'),
76+
jobOffers: Id('54190b30-1c68-499c-9ed8-5c6190810e31'),
7777
},
7878
},
7979
);
@@ -85,11 +85,41 @@ export const Event = EntitySchema(
8585
sponsors: Type.Relation(Company),
8686
},
8787
{
88-
types: [Id('7f9562d4-034d-4385-bf5c-f02cdebba47a')],
88+
types: [Id('239bc639-938e-427c-bebb-d562d82ae272')],
89+
properties: {
90+
name: Id('a126ca53-0c8e-48d5-b888-82c734c38935'),
91+
description: Id('9b1f76ff-9711-404c-861e-59dc3fa7d037'),
92+
sponsors: Id('926b00ee-68b5-4462-a27f-3806af705118'),
93+
},
94+
},
95+
);
96+
97+
export const Image = EntitySchema(
98+
{
99+
url: Type.String,
100+
},
101+
{
102+
types: [Id('ba4e4146-0010-499d-a0a3-caaa7f579d0e')],
103+
properties: {
104+
url: Id('8a743832-c094-4a62-b665-0c3cc2f9c7bc'),
105+
},
106+
},
107+
);
108+
109+
export const Project = EntitySchema(
110+
{
111+
name: Type.String,
112+
description: Type.optional(Type.String),
113+
x: Type.optional(Type.String),
114+
avatar: Type.Relation(Image),
115+
},
116+
{
117+
types: [Id('484a18c5-030a-499c-b0f2-ef588ff16d50')],
89118
properties: {
90119
name: Id('a126ca53-0c8e-48d5-b888-82c734c38935'),
91120
description: Id('9b1f76ff-9711-404c-861e-59dc3fa7d037'),
92-
sponsors: Id('6860bfac-f703-4289-b789-972d0aaf3abe'),
121+
x: Id('0d625978-4b3c-4b57-a86f-de45c997c73c'),
122+
avatar: Id('1155beff-fad5-49b7-a2e0-da4777b8792c'),
93123
},
94124
},
95125
);

packages/hypergraph-react/src/internal/convert-relations.ts

Lines changed: 44 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -21,140 +21,72 @@ type RecursiveQueryEntity = {
2121
point: string;
2222
}[];
2323
relationsList?: {
24+
id: string;
2425
toEntity: RecursiveQueryEntity;
2526
typeId: string;
2627
}[];
2728
};
2829

29-
export const convertRelations = <S extends Schema.Schema.AnyNoContext>(queryEntity: RecursiveQueryEntity, type: S) => {
30+
export const convertRelations = <S extends Schema.Schema.AnyNoContext>(
31+
queryEntity: RecursiveQueryEntity,
32+
ast: SchemaAST.TypeLiteral,
33+
) => {
3034
const rawEntity: Record<string, string | boolean | number | unknown[] | Date> = {};
3135

32-
const ast = type.ast as SchemaAST.TypeLiteral;
33-
34-
// console.log('queryEntity', queryEntity);
35-
3636
for (const prop of ast.propertySignatures) {
3737
const result = SchemaAST.getAnnotation<string>(PropertyIdSymbol)(prop.type);
3838

39-
if (isRelation(prop.type)) {
39+
if (isRelation(prop.type) && Option.isSome(result)) {
4040
rawEntity[String(prop.name)] = [];
4141

42-
if (!queryEntity.valuesList) {
42+
const relationTransformation = prop.type.rest?.[0]?.type;
43+
const typeIds: string[] = SchemaAST.getAnnotation<string[]>(TypeIdsSymbol)(relationTransformation).pipe(
44+
Option.getOrElse(() => []),
45+
);
46+
if (typeIds.length === 0) {
4347
continue;
4448
}
4549

46-
if (Option.isSome(result)) {
47-
const relationTransformation = prop.type.rest?.[0]?.type;
48-
const typeIds: string[] = SchemaAST.getAnnotation<string[]>(TypeIdsSymbol)(relationTransformation).pipe(
49-
Option.getOrElse(() => []),
50-
);
51-
if (typeIds.length === 0) {
52-
continue;
53-
}
54-
55-
const allRelationsWithTheCorrectPropertyTypeId = queryEntity.relationsList?.filter(
56-
(a) => a.typeId === result.value,
57-
);
58-
if (allRelationsWithTheCorrectPropertyTypeId) {
59-
for (const relationEntry of allRelationsWithTheCorrectPropertyTypeId) {
60-
const nestedRawEntity:
61-
| Record<string, string | boolean | number | unknown[] | Date>
62-
| { _relation: { id: string } } = {
63-
id: relationEntry.toEntity.id,
64-
_relation: {
65-
id: 'TODO: relation id',
66-
},
67-
};
68-
69-
for (const nestedProp of relationTransformation.propertySignatures) {
70-
const nestedResult = SchemaAST.getAnnotation<string>(PropertyIdSymbol)(nestedProp.type);
71-
if (Option.isSome(nestedResult)) {
72-
const value = relationEntry.toEntity.valuesList?.find((a) => a.propertyId === nestedResult.value);
73-
if (!value) {
74-
continue;
75-
}
76-
const rawValue = convertPropertyValue(value, nestedProp.type);
77-
if (rawValue) {
78-
nestedRawEntity[String(nestedProp.name)] = rawValue;
79-
}
50+
const allRelationsWithTheCorrectPropertyTypeId = queryEntity.relationsList?.filter(
51+
(a) => a.typeId === result.value,
52+
);
53+
if (allRelationsWithTheCorrectPropertyTypeId) {
54+
for (const relationEntry of allRelationsWithTheCorrectPropertyTypeId) {
55+
let nestedRawEntity:
56+
| Record<string, string | boolean | number | unknown[] | Date>
57+
| { _relation: { id: string } } = {
58+
id: relationEntry.toEntity.id,
59+
_relation: {
60+
id: relationEntry.id,
61+
},
62+
};
63+
64+
const relationsForRawNestedEntity = convertRelations(relationEntry.toEntity, relationTransformation);
65+
66+
nestedRawEntity = {
67+
...nestedRawEntity,
68+
...relationsForRawNestedEntity,
69+
};
70+
71+
for (const nestedProp of relationTransformation.propertySignatures) {
72+
const nestedResult = SchemaAST.getAnnotation<string>(PropertyIdSymbol)(nestedProp.type);
73+
if (Option.isSome(nestedResult)) {
74+
const value = relationEntry.toEntity.valuesList?.find((a) => a.propertyId === nestedResult.value);
75+
if (!value) {
76+
continue;
77+
}
78+
const rawValue = convertPropertyValue(value, nestedProp.type);
79+
if (rawValue) {
80+
nestedRawEntity[String(nestedProp.name)] = rawValue;
8081
}
81-
// TODO: in the end every entry should be validated using the Schema?!?
82-
rawEntity[String(prop.name)] = [...(rawEntity[String(prop.name)] as unknown[]), nestedRawEntity];
8382
}
8483
}
84+
// TODO: in the end every entry should be validated using the Schema?!?
85+
rawEntity[String(prop.name)] = [...(rawEntity[String(prop.name)] as unknown[]), nestedRawEntity];
8586
}
8687
}
8788
}
8889
}
8990

90-
// for (const [key, relationId] of Object.entries(mappingEntry?.relations ?? {})) {
91-
// const properties = (queryEntity.relationsList ?? []).filter((a) => a.typeId === relationId);
92-
// if (properties.length === 0) {
93-
// rawEntity[key] = [] as unknown[];
94-
// continue;
95-
// }
96-
97-
// const field = type.fields[key];
98-
// if (!field) {
99-
// // @ts-expect-error TODO: properly access the type.name
100-
// console.error(`Field ${key} not found in ${type.name}`);
101-
// continue;
102-
// }
103-
// const relationTransformation = field.ast.rest?.[0];
104-
// if (!relationTransformation) {
105-
// console.error(`Relation transformation for ${key} not found`);
106-
// continue;
107-
// }
108-
109-
// const identifierAnnotation = SchemaAST.getIdentifierAnnotation(relationTransformation.type.to);
110-
// if (Option.isNone(identifierAnnotation)) {
111-
// console.error(`Relation identifier for ${key} not found`);
112-
// continue;
113-
// }
114-
115-
// const relationTypeName = identifierAnnotation.value;
116-
117-
// const relationMappingEntry = mapping[relationTypeName];
118-
// if (!relationMappingEntry) {
119-
// console.error(`Relation mapping entry for ${relationTypeName} not found`);
120-
// continue;
121-
// }
122-
123-
// const newRelationEntities = properties.map((propertyEntry) => {
124-
// // @ts-expect-error TODO: properly access the type.name
125-
// const type = field.value;
126-
127-
// let rawEntity: Record<string, string | boolean | number | unknown[] | Date> = {
128-
// id: propertyEntry.toEntity.id,
129-
// name: propertyEntry.toEntity.name,
130-
// // TODO: should be determined by the actual value
131-
// __deleted: false,
132-
// // TODO: should be determined by the actual value
133-
// __version: '',
134-
// };
135-
136-
// // take the mappingEntry and assign the attributes to the rawEntity
137-
// for (const [key, value] of Object.entries(relationMappingEntry?.properties ?? {})) {
138-
// const property = propertyEntry.toEntity.valuesList?.find((a) => a.propertyId === value);
139-
// if (property) {
140-
// rawEntity[key] = convertPropertyValue(property, type);
141-
// }
142-
// }
143-
144-
// rawEntity = {
145-
// ...rawEntity,
146-
// ...convertRelations(propertyEntry.toEntity, type, relationMappingEntry, mapping),
147-
// };
148-
149-
// return rawEntity;
150-
// });
151-
152-
// if (rawEntity[key]) {
153-
// rawEntity[key] = [...(rawEntity[key] as unknown[]), ...newRelationEntities];
154-
// } else {
155-
// rawEntity[key] = newRelationEntities;
156-
// }
157-
// }
158-
15991
return rawEntity;
16092
};

0 commit comments

Comments
 (0)