Skip to content

Commit b943386

Browse files
committed
generate schema and mapping entries
1 parent 328e284 commit b943386

4 files changed

Lines changed: 307 additions & 0 deletions

File tree

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { useState } from 'react';
2+
import { generateMappingFile, generateSchemaFile } from '../utils/convertTypesData';
3+
import { Button } from './ui/button';
4+
import { Card, CardContent, CardHeader, CardTitle } from './ui/card';
5+
6+
export const ConvertTypesData = () => {
7+
const [schemaOutput, setSchemaOutput] = useState<string>('');
8+
const [mappingOutput, setMappingOutput] = useState<string>('');
9+
10+
const handleGenerateSchema = () => {
11+
const schema = generateSchemaFile();
12+
setSchemaOutput(schema);
13+
};
14+
15+
const handleGenerateMapping = () => {
16+
const mapping = generateMappingFile();
17+
setMappingOutput(mapping);
18+
};
19+
20+
const handleGenerateBoth = () => {
21+
handleGenerateSchema();
22+
handleGenerateMapping();
23+
};
24+
25+
return (
26+
<div className="space-y-4">
27+
<div className="flex gap-2">
28+
<Button onClick={handleGenerateSchema}>Generate Schema</Button>
29+
<Button onClick={handleGenerateMapping}>Generate Mapping</Button>
30+
<Button onClick={handleGenerateBoth}>Generate Both</Button>
31+
</div>
32+
33+
{schemaOutput && (
34+
<Card>
35+
<CardHeader>
36+
<CardTitle>Generated Schema</CardTitle>
37+
</CardHeader>
38+
<CardContent>
39+
<pre className="bg-gray-100 p-4 rounded text-sm overflow-auto max-h-96">{schemaOutput}</pre>
40+
</CardContent>
41+
</Card>
42+
)}
43+
44+
{mappingOutput && (
45+
<Card>
46+
<CardHeader>
47+
<CardTitle>Generated Mapping</CardTitle>
48+
</CardHeader>
49+
<CardContent>
50+
<pre className="bg-gray-100 p-4 rounded text-sm overflow-auto max-h-96">{mappingOutput}</pre>
51+
</CardContent>
52+
</Card>
53+
)}
54+
</div>
55+
);
56+
};

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { ConvertTypesData } from '@/components/ConvertTypesData';
12
import { CreateEvents } from '@/components/create-events';
23
import { CreatePropertiesAndTypesEvent } from '@/components/create-properties-and-types-event';
34
import { Playground } from '@/components/playground';
@@ -17,6 +18,7 @@ function RouteComponent() {
1718
<Playground />
1819
<CreatePropertiesAndTypesEvent space={space} />
1920
<CreateEvents space={space} />
21+
<ConvertTypesData />
2022
</div>
2123
</HypergraphSpaceProvider>
2224
);
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { describe, expect, it } from 'vitest';
2+
import { convertTypesDataToSchemaAndMapping, generateMappingFile, generateSchemaFile } from './convertTypesData';
3+
4+
describe('convertTypesData', () => {
5+
it('should convert typesData to schema and mapping', () => {
6+
const result = convertTypesDataToSchemaAndMapping();
7+
8+
expect(result).toHaveProperty('schema');
9+
expect(result).toHaveProperty('mapping');
10+
expect(typeof result.schema).toBe('string');
11+
expect(typeof result.mapping).toBe('object');
12+
});
13+
14+
it('should generate schema file with proper imports', () => {
15+
const schema = generateSchemaFile();
16+
17+
expect(schema).toContain("import { Entity, Type } from '@graphprotocol/hypergraph';");
18+
expect(schema).toContain('export class');
19+
});
20+
21+
it('should generate mapping file with proper imports', () => {
22+
const mapping = generateMappingFile();
23+
24+
expect(mapping).toContain("import { Id } from '@graphprotocol/grc-20';");
25+
expect(mapping).toContain("import type { Mapping } from '@graphprotocol/hypergraph';");
26+
expect(mapping).toContain('export const mapping: Mapping = {');
27+
});
28+
29+
it('should handle relation properties correctly', () => {
30+
const result = convertTypesDataToSchemaAndMapping();
31+
32+
// Check if any mapping entries have relations
33+
const hasRelations = Object.values(result.mapping).some((entry) => entry.relations);
34+
expect(hasRelations).toBe(true);
35+
});
36+
37+
it('should generate valid class names', () => {
38+
const result = convertTypesDataToSchemaAndMapping();
39+
40+
// Check if schema contains valid class definitions
41+
expect(result.schema).toMatch(/export class \w+ extends Entity\.Class/);
42+
});
43+
});
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
import { Type } from '@graphprotocol/hypergraph';
2+
import { typesData } from '../data/typesData';
3+
4+
// Type definitions for the data structure
5+
interface Property {
6+
id: string;
7+
dataType: 'TEXT' | 'NUMBER' | 'RELATION' | 'CHECKBOX' | 'DATE' | 'POINT' | 'URL';
8+
relationValueTypes: Array<{
9+
id: string;
10+
name: string;
11+
description: string | null;
12+
properties: Array<{
13+
id: string;
14+
dataType: string;
15+
entity: {
16+
id: string;
17+
name: string;
18+
};
19+
}>;
20+
}>;
21+
entity: {
22+
id: string;
23+
name: string;
24+
};
25+
}
26+
27+
interface TypeData {
28+
id: string;
29+
name: string;
30+
properties: Property[];
31+
}
32+
33+
interface TypesData {
34+
types: TypeData[];
35+
}
36+
37+
interface MappingEntry {
38+
typeIds: string[];
39+
properties: Record<string, string>;
40+
relations?: Record<string, string>;
41+
}
42+
43+
// Helper function to convert dataType to Type
44+
function dataTypeToType(
45+
dataType: string,
46+
):
47+
| typeof Type.Text
48+
| typeof Type.Number
49+
| typeof Type.Relation
50+
| typeof Type.Checkbox
51+
| typeof Type.Date
52+
| typeof Type.Point
53+
| typeof Type.Url {
54+
switch (dataType) {
55+
case 'TEXT':
56+
return Type.Text;
57+
case 'NUMBER':
58+
return Type.Number;
59+
case 'RELATION':
60+
return Type.Relation; // This will need to be handled specially
61+
case 'CHECKBOX':
62+
return Type.Checkbox;
63+
case 'DATE':
64+
return Type.Date;
65+
case 'POINT':
66+
return Type.Point;
67+
case 'URL':
68+
return Type.Url;
69+
default:
70+
return Type.Text; // fallback
71+
}
72+
}
73+
74+
// Helper function to get relation target class name
75+
function getRelationTargetClassName(
76+
relationValueTypes: Array<{
77+
id: string;
78+
name: string;
79+
description: string | null;
80+
properties: Array<{ id: string; dataType: string; entity: { id: string; name: string } }>;
81+
}>,
82+
): string | null {
83+
if (relationValueTypes.length === 0) return null;
84+
return relationValueTypes[0].name;
85+
}
86+
87+
// Helper function to create a class name from type name
88+
function createClassName(typeName: string): string {
89+
// Convert to PascalCase and handle special cases
90+
return typeName
91+
.split(' ')
92+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
93+
.join('');
94+
}
95+
96+
export function convertTypesDataToSchemaAndMapping() {
97+
const schemaEntries: string[] = [];
98+
const mappingEntries: Record<string, MappingEntry> = {};
99+
100+
// Create a map of type names to their IDs for relation handling
101+
const typeNameToId = new Map<string, string>();
102+
const typeIdToName = new Map<string, string>();
103+
104+
// First pass: collect all type names and IDs
105+
for (const type of typesData.types) {
106+
typeNameToId.set(type.name, type.id);
107+
typeIdToName.set(type.id, type.name);
108+
}
109+
110+
// Second pass: generate schema and mapping
111+
for (const type of typesData.types) {
112+
const className = createClassName(type.name);
113+
114+
// Generate schema entry
115+
const properties: string[] = [];
116+
const mappingProperties: Record<string, string> = {};
117+
const mappingRelations: Record<string, string> = {};
118+
119+
for (const property of type.properties) {
120+
const propertyName = property.entity.name.toLowerCase().replace(/\s+/g, '');
121+
122+
if (property.dataType === 'RELATION') {
123+
const targetClassName = getRelationTargetClassName(property.relationValueTypes);
124+
if (targetClassName) {
125+
const targetClass = createClassName(targetClassName);
126+
properties.push(` ${propertyName}: Type.Relation(${targetClass})`);
127+
mappingRelations[propertyName] = `Id.Id('${property.id}')`;
128+
}
129+
} else {
130+
const typeInstance = dataTypeToType(property.dataType);
131+
const typeName = typeInstance.name.endsWith('$') ? typeInstance.name.slice(0, -1) : typeInstance.name;
132+
properties.push(` ${propertyName}: Type.${typeName}`);
133+
mappingProperties[propertyName] = `Id.Id('${property.id}')`;
134+
}
135+
}
136+
137+
// Generate schema class
138+
const schemaClass = `export class ${className} extends Entity.Class<${className}>('${className}')({
139+
${properties.join(',\n')}
140+
}) {}`;
141+
142+
schemaEntries.push(schemaClass);
143+
144+
// Generate mapping entry
145+
mappingEntries[className] = {
146+
typeIds: [`Id.Id('${type.id}')`],
147+
properties: mappingProperties,
148+
...(Object.keys(mappingRelations).length > 0 && { relations: mappingRelations }),
149+
};
150+
}
151+
152+
return {
153+
schema: schemaEntries.join('\n\n'),
154+
mapping: mappingEntries,
155+
};
156+
}
157+
158+
// Function to generate the complete schema file content
159+
export function generateSchemaFile(): string {
160+
const { schema } = convertTypesDataToSchemaAndMapping();
161+
return `import { Entity, Type } from '@graphprotocol/hypergraph';
162+
163+
${schema}
164+
`;
165+
}
166+
167+
// Function to generate the complete mapping file content
168+
export function generateMappingFile(): string {
169+
const { mapping } = convertTypesDataToSchemaAndMapping();
170+
171+
const mappingEntries = Object.entries(mapping)
172+
.map(([className, mappingData]) => {
173+
const properties = Object.entries(mappingData.properties || {})
174+
.map(([key, value]) => ` ${key}: ${value}`)
175+
.join(',\n');
176+
177+
const relations = mappingData.relations
178+
? Object.entries(mappingData.relations)
179+
.map(([key, value]) => ` ${key}: ${value}`)
180+
.join(',\n')
181+
: '';
182+
183+
return ` ${className}: {
184+
typeIds: [${mappingData.typeIds.join(', ')}],
185+
properties: {
186+
${properties}
187+
},
188+
${
189+
relations
190+
? ` relations: {
191+
${relations}
192+
},`
193+
: ''
194+
}
195+
}`;
196+
})
197+
.join(',\n\n');
198+
199+
return `import { Id } from '@graphprotocol/grc-20';
200+
import type { Mapping } from '@graphprotocol/hypergraph';
201+
202+
export const mapping: Mapping = {
203+
${mappingEntries}
204+
};
205+
`;
206+
}

0 commit comments

Comments
 (0)