Skip to content

Commit 5aa3efa

Browse files
committed
extract convertRelations
1 parent 7197e78 commit 5aa3efa

File tree

3 files changed

+100
-196
lines changed

3 files changed

+100
-196
lines changed
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import type { Entity, Mapping } from '@graphprotocol/hypergraph';
2+
import { convertPropertyValue } from './convert-property-value.js';
3+
4+
// A recursive representation of the entity structure returned by the public GraphQL
5+
// endpoint. `values` and `relations` are optional because the nested `to` selections
6+
// get slimmer the deeper we traverse in the query. This type intentionally mirrors
7+
// only the fields we actually consume inside `convertRelations`.
8+
type RecursiveQueryEntity = {
9+
id: string;
10+
name: string;
11+
valuesList?: {
12+
propertyId: string;
13+
string: string;
14+
boolean: boolean;
15+
number: number;
16+
time: string;
17+
point: string;
18+
}[];
19+
relationsList?: {
20+
toEntity: RecursiveQueryEntity;
21+
typeId: string;
22+
}[];
23+
};
24+
25+
export const convertRelations = <S extends Entity.AnyNoContext>(
26+
queryEntity: RecursiveQueryEntity,
27+
type: S,
28+
mappingEntry: Mapping.MappingEntry,
29+
mapping: Mapping.Mapping,
30+
) => {
31+
const rawEntity: Record<string, string | boolean | number | unknown[] | Date> = {};
32+
33+
for (const [key, relationId] of Object.entries(mappingEntry?.relations ?? {})) {
34+
const properties = (queryEntity.relationsList ?? []).filter((a) => a.typeId === relationId);
35+
if (properties.length === 0) {
36+
rawEntity[key] = [] as unknown[];
37+
continue;
38+
}
39+
40+
const field = type.fields[key];
41+
if (!field) {
42+
// @ts-expect-error TODO: properly access the type.name
43+
console.error(`Field ${key} not found in ${type.name}`);
44+
continue;
45+
}
46+
// @ts-expect-error TODO: properly access the type.name
47+
const annotations = field.ast.rest[0].type.to.annotations;
48+
49+
// TODO: fix this access using proper effect types
50+
const relationTypeName =
51+
annotations[
52+
Object.getOwnPropertySymbols(annotations).find((sym) => sym.description === 'effect/annotation/Identifier')
53+
];
54+
55+
const relationMappingEntry = mapping[relationTypeName];
56+
if (!relationMappingEntry) {
57+
console.error(`Relation mapping entry for ${relationTypeName} not found`);
58+
continue;
59+
}
60+
61+
const newRelationEntities = properties.map((propertyEntry) => {
62+
// @ts-expect-error TODO: properly access the type.name
63+
const type = field.value;
64+
65+
let rawEntity: Record<string, string | boolean | number | unknown[] | Date> = {
66+
id: propertyEntry.toEntity.id,
67+
name: propertyEntry.toEntity.name,
68+
// TODO: should be determined by the actual value
69+
__deleted: false,
70+
// TODO: should be determined by the actual value
71+
__version: '',
72+
};
73+
74+
// take the mappingEntry and assign the attributes to the rawEntity
75+
for (const [key, value] of Object.entries(relationMappingEntry?.properties ?? {})) {
76+
const property = propertyEntry.toEntity.valuesList?.find((a) => a.propertyId === value);
77+
if (property) {
78+
rawEntity[key] = convertPropertyValue(property, key, type);
79+
}
80+
}
81+
82+
rawEntity = {
83+
...rawEntity,
84+
...convertRelations(propertyEntry.toEntity, type, relationMappingEntry, mapping),
85+
};
86+
87+
return rawEntity;
88+
});
89+
90+
if (rawEntity[key]) {
91+
rawEntity[key] = [...(rawEntity[key] as unknown[]), ...newRelationEntities];
92+
} else {
93+
rawEntity[key] = newRelationEntities;
94+
}
95+
}
96+
97+
return rawEntity;
98+
};

packages/hypergraph-react/src/internal/use-entity-public.tsx

Lines changed: 1 addition & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import * as Schema from 'effect/Schema';
77
import { gql, request } from 'graphql-request';
88
import { useMemo } from 'react';
99
import { convertPropertyValue } from './convert-property-value.js';
10+
import { convertRelations } from './convert-relations.js';
1011
import { useHypergraphSpaceInternal } from './use-hypergraph-space-internal.js';
1112

1213
const entityQueryDocumentLevel0 = gql`
@@ -162,106 +163,6 @@ type EntityQueryResult = {
162163
} | null;
163164
};
164165

165-
// A recursive representation of the entity structure returned by the public GraphQL
166-
// endpoint. `values` and `relations` are optional because the nested `to` selections
167-
// get slimmer the deeper we traverse in the query. This type intentionally mirrors
168-
// only the fields we actually consume inside `convertRelations`.
169-
type RecursiveQueryEntity = {
170-
id: string;
171-
name: string;
172-
valuesList?: {
173-
propertyId: string;
174-
string: string;
175-
boolean: boolean;
176-
number: number;
177-
time: string;
178-
point: string;
179-
}[];
180-
relationsList?: {
181-
toEntity: RecursiveQueryEntity;
182-
typeId: string;
183-
}[];
184-
};
185-
186-
const convertRelations = <S extends Entity.AnyNoContext>(
187-
queryEntity: RecursiveQueryEntity,
188-
type: S,
189-
mappingEntry: Mapping.MappingEntry,
190-
mapping: Mapping.Mapping,
191-
) => {
192-
const rawEntity: Record<string, string | boolean | number | unknown[] | Date> = {};
193-
194-
for (const [key, relationId] of Object.entries(mappingEntry?.relations ?? {})) {
195-
const properties = (queryEntity.relationsList ?? []).filter((a) => a.typeId === relationId);
196-
if (properties.length === 0) {
197-
rawEntity[key] = [] as unknown[];
198-
continue;
199-
}
200-
201-
const field = type.fields[key];
202-
if (!field) {
203-
// @ts-expect-error TODO: properly access the type.name
204-
console.error(`Field ${key} not found in ${type.name}`);
205-
continue;
206-
}
207-
// @ts-expect-error TODO: properly access the type.name
208-
const annotations = field.ast.rest[0].type.to.annotations;
209-
210-
// TODO: fix this access using proper effect types
211-
const relationTypeName =
212-
annotations[
213-
Object.getOwnPropertySymbols(annotations).find((sym) => sym.description === 'effect/annotation/Identifier')
214-
];
215-
216-
const relationMappingEntry = mapping[relationTypeName];
217-
if (!relationMappingEntry) {
218-
console.error(`Relation mapping entry for ${relationTypeName} not found`);
219-
continue;
220-
}
221-
222-
const newRelationEntities = properties.map((propertyEntry) => {
223-
// @ts-expect-error TODO: properly access the type.name
224-
const type = field.value;
225-
226-
let rawEntity: Record<string, string | boolean | number | unknown[] | Date> = {
227-
id: propertyEntry.toEntity.id,
228-
name: propertyEntry.toEntity.name,
229-
// TODO: should be determined by the actual value
230-
__deleted: false,
231-
// TODO: should be determined by the actual value
232-
__version: '',
233-
};
234-
235-
// take the mappingEntry and assign the attributes to the rawEntity
236-
for (const [key, value] of Object.entries(relationMappingEntry?.properties ?? {})) {
237-
const property = propertyEntry.toEntity.valuesList?.find((a) => a.propertyId === value);
238-
if (property) {
239-
rawEntity[key] = convertPropertyValue(property, key, type);
240-
}
241-
}
242-
243-
rawEntity = {
244-
...rawEntity,
245-
...convertRelations(propertyEntry.toEntity, type, relationMappingEntry, mapping),
246-
};
247-
248-
return rawEntity;
249-
});
250-
251-
if (rawEntity[key]) {
252-
rawEntity[key] = [
253-
// @ts-expect-error TODO: properly access the type.name
254-
...rawEntity[key],
255-
...newRelationEntities,
256-
];
257-
} else {
258-
rawEntity[key] = newRelationEntities;
259-
}
260-
}
261-
262-
return rawEntity;
263-
};
264-
265166
export const parseResult = <S extends Entity.AnyNoContext>(
266167
queryData: EntityQueryResult,
267168
type: S,

packages/hypergraph-react/src/internal/use-query-public.tsx

Lines changed: 1 addition & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import * as Schema from 'effect/Schema';
77
import { gql, request } from 'graphql-request';
88
import { useMemo } from 'react';
99
import { convertPropertyValue } from './convert-property-value.js';
10+
import { convertRelations } from './convert-relations.js';
1011
import { translateFilterToGraphql } from './translate-filter-to-graphql.js';
1112
import type { QueryPublicParams } from './types.js';
1213
import { useHypergraphSpaceInternal } from './use-hypergraph-space-internal.js';
@@ -177,102 +178,6 @@ type EntityQueryResult = {
177178
}[];
178179
};
179180

180-
// A recursive representation of the entity structure returned by the public GraphQL
181-
// endpoint. `values` and `relations` are optional because the nested `to` selections
182-
// get slimmer the deeper we traverse in the query. This type intentionally mirrors
183-
// only the fields we actually consume inside `convertRelations`.
184-
type RecursiveQueryEntity = {
185-
id: string;
186-
name: string;
187-
valuesList?: {
188-
propertyId: string;
189-
string: string;
190-
boolean: boolean;
191-
number: number;
192-
time: string;
193-
point: string;
194-
}[];
195-
relationsList?: {
196-
toEntity: RecursiveQueryEntity;
197-
typeId: string;
198-
}[];
199-
};
200-
201-
const convertRelations = <S extends Entity.AnyNoContext>(
202-
queryEntity: RecursiveQueryEntity,
203-
type: S,
204-
mappingEntry: Mapping.MappingEntry,
205-
mapping: Mapping.Mapping,
206-
) => {
207-
const rawEntity: Record<string, string | boolean | number | unknown[] | Date> = {};
208-
209-
for (const [key, relationId] of Object.entries(mappingEntry?.relations ?? {})) {
210-
const properties = (queryEntity.relationsList ?? []).filter((a) => a.typeId === relationId);
211-
if (properties.length === 0) {
212-
rawEntity[key] = [] as unknown[];
213-
continue;
214-
}
215-
216-
const field = type.fields[key];
217-
if (!field) {
218-
// @ts-expect-error TODO: properly access the type.name
219-
console.error(`Field ${key} not found in ${type.name}`);
220-
continue;
221-
}
222-
// @ts-expect-error TODO: properly access the type.name
223-
const annotations = field.ast.rest[0].type.to.annotations;
224-
225-
// TODO: fix this access using proper effect types
226-
const relationTypeName =
227-
annotations[
228-
Object.getOwnPropertySymbols(annotations).find((sym) => sym.description === 'effect/annotation/Identifier')
229-
];
230-
231-
const relationMappingEntry = mapping[relationTypeName];
232-
if (!relationMappingEntry) {
233-
console.error(`Relation mapping entry for ${relationTypeName} not found`);
234-
continue;
235-
}
236-
237-
const newRelationEntities = properties.map((propertyEntry) => {
238-
// @ts-expect-error TODO: properly access the type.name
239-
const type = field.value;
240-
241-
let rawEntity: Record<string, string | boolean | number | unknown[] | Date> = {
242-
id: propertyEntry.toEntity.id,
243-
name: propertyEntry.toEntity.name,
244-
// TODO: should be determined by the actual value
245-
__deleted: false,
246-
// TODO: should be determined by the actual value
247-
__version: '',
248-
};
249-
250-
// take the mappingEntry and assign the attributes to the rawEntity
251-
for (const [key, value] of Object.entries(relationMappingEntry?.properties ?? {})) {
252-
const property = propertyEntry.toEntity.valuesList?.find((a) => a.propertyId === value);
253-
if (property) {
254-
rawEntity[key] = convertPropertyValue(property, key, type);
255-
}
256-
}
257-
258-
rawEntity = {
259-
...rawEntity,
260-
...convertRelations(propertyEntry.toEntity, type, relationMappingEntry, mapping),
261-
};
262-
263-
return rawEntity;
264-
});
265-
266-
if (rawEntity[key]) {
267-
rawEntity[key] = [...rawEntity[key], ...newRelationEntities];
268-
} else {
269-
rawEntity[key] = newRelationEntities;
270-
}
271-
}
272-
273-
return rawEntity;
274-
};
275-
276181
export const parseResult = <S extends Entity.AnyNoContext>(
277182
queryData: EntityQueryResult,
278183
type: S,

0 commit comments

Comments
 (0)