1- import type { Id as Grc20Id } from '@graphprotocol/grc-20' ;
1+ import { type CreatePropertyParams , Graph , Id as Grc20Id , type Op } from '@graphprotocol/grc-20' ;
2+ import { Array as EffectArray , Schema as EffectSchema , pipe } from 'effect' ;
23
34/**
45 * Mappings for a schema type and its properties/relations
@@ -18,17 +19,21 @@ export type MappingEntry = {
1819 *
1920 * @since 0.0.1
2021 */
21- properties ?: {
22- [ key : string ] : Grc20Id . Id ;
23- } ;
22+ properties ?:
23+ | {
24+ [ key : string ] : Grc20Id . Id ;
25+ }
26+ | undefined ;
2427 /**
2528 * Record of schema type relation names to the `Id.Id` of the relation in the Knowledge Graph
2629 *
2730 * @since 0.0.1
2831 */
29- relations ?: {
30- [ key : string ] : Grc20Id . Id ;
31- } ;
32+ relations ?:
33+ | {
34+ [ key : string ] : Grc20Id . Id ;
35+ }
36+ | undefined ;
3237} ;
3338
3439/**
@@ -63,3 +68,154 @@ export type MappingEntry = {
6368export type Mapping = {
6469 [ key : string ] : MappingEntry ;
6570} ;
71+
72+ export type DataTypeRelation = `Relation(${string } )`;
73+ export function isDataTypeRelation ( val : string ) : val is DataTypeRelation {
74+ return / ^ R e l a t i o n \( ( .+ ) \) $ / . test ( val ) ;
75+ }
76+ export const SchemaDataTypeRelation = EffectSchema . NonEmptyTrimmedString . pipe (
77+ EffectSchema . filter ( ( val ) => isDataTypeRelation ( val ) ) ,
78+ ) ;
79+ export type SchemaDataTypeRelation = typeof SchemaDataTypeRelation . Type ;
80+
81+ export const SchemaDataType = EffectSchema . Union (
82+ EffectSchema . Literal ( 'Text' , 'Number' , 'Boolean' , 'Date' , 'Point' , 'Url' ) ,
83+ SchemaDataTypeRelation ,
84+ ) ;
85+ export type SchemaDataType = typeof SchemaDataType . Type ;
86+
87+ export const Schema = EffectSchema . Struct ( {
88+ types : EffectSchema . Array (
89+ EffectSchema . Struct ( {
90+ name : EffectSchema . NonEmptyTrimmedString ,
91+ knowledgeGraphId : EffectSchema . NullOr ( EffectSchema . UUID ) ,
92+ properties : EffectSchema . Array (
93+ EffectSchema . Struct ( {
94+ name : EffectSchema . NonEmptyTrimmedString ,
95+ knowledgeGraphId : EffectSchema . NullOr ( EffectSchema . UUID ) ,
96+ dataType : SchemaDataType ,
97+ } ) ,
98+ ) . pipe ( EffectSchema . minItems ( 1 ) ) ,
99+ } ) ,
100+ ) . pipe ( EffectSchema . minItems ( 1 ) ) ,
101+ } ) . annotations ( {
102+ identifier : 'typesync/Schema' ,
103+ title : 'TypeSync app Schema' ,
104+ description : 'An array of types in the schema defined by the user to generate a Mapping object for' ,
105+ examples : [
106+ {
107+ types : [
108+ {
109+ name : 'Account' ,
110+ knowledgeGraphId : null ,
111+ properties : [ { name : 'username' , knowledgeGraphId : null , dataType : 'Text' } ] ,
112+ } ,
113+ ] ,
114+ } ,
115+ {
116+ types : [
117+ {
118+ name : 'Account' ,
119+ knowledgeGraphId : 'a5fd07b1-120f-46c6-b46f-387ef98396a6' ,
120+ properties : [ { name : 'name' , knowledgeGraphId : 'a126ca53-0c8e-48d5-b888-82c734c38935' , dataType : 'Text' } ] ,
121+ } ,
122+ ] ,
123+ } ,
124+ ] ,
125+ } ) ;
126+ export type Schema = typeof Schema . Type ;
127+
128+ export async function generateMapping ( schema : Schema ) : Promise < Mapping > {
129+ const entries : Array < MappingEntry & { typeName : string } > = [ ] ;
130+ const ops : Array < Op > = [ ] ;
131+
132+ for ( const type of schema . types ) {
133+ const typePropertyIds : Array < { propName : string ; id : Grc20Id . Id } > = [ ] ;
134+ for ( const property of type . properties ) {
135+ if ( property . knowledgeGraphId ) {
136+ typePropertyIds . push ( { propName : property . name , id : Grc20Id . Id ( property . knowledgeGraphId ) } ) ;
137+ continue ;
138+ }
139+ // create op for creating type property
140+ const { id, ops : createTypePropOp } = Graph . createProperty ( {
141+ name : property . name ,
142+ dataType : mapSchemaDataTypeToGRC20PropDataType ( property . dataType ) ,
143+ } ) ;
144+ typePropertyIds . push ( { propName : property . name , id } ) ;
145+ // add createProperty ops to array to submit in batch to KG
146+ ops . push ( ...createTypePropOp ) ;
147+ }
148+
149+ const properties : MappingEntry [ 'properties' ] = pipe (
150+ typePropertyIds ,
151+ EffectArray . reduce ( { } as NonNullable < MappingEntry [ 'properties' ] > , ( props , { propName, id } ) => {
152+ props [ propName ] = id ;
153+
154+ return props ;
155+ } ) ,
156+ ) ;
157+
158+ const relations : MappingEntry [ 'relations' ] = undefined ;
159+
160+ if ( type . knowledgeGraphId ) {
161+ entries . push ( {
162+ typeName : type . name ,
163+ typeIds : [ Grc20Id . Id ( type . knowledgeGraphId ) ] ,
164+ properties,
165+ relations,
166+ } ) ;
167+ continue ;
168+ }
169+ // create the type op, with its properties
170+ const { id, ops : createTypeOp } = Graph . createType ( {
171+ name : type . name ,
172+ properties : EffectArray . map ( typePropertyIds , ( { id } ) => id ) ,
173+ } ) ;
174+ ops . push ( ...createTypeOp ) ;
175+
176+ entries . push ( {
177+ typeName : type . name ,
178+ typeIds : [ id ] ,
179+ properties,
180+ relations,
181+ } ) ;
182+ }
183+
184+ // @todo send ops to Knowledge Graph
185+
186+ return pipe (
187+ entries ,
188+ EffectArray . reduce ( { } as Mapping , ( mapping , entry ) => {
189+ const { typeName, ...rest } = entry ;
190+ mapping [ typeName ] = rest ;
191+
192+ return mapping ;
193+ } ) ,
194+ ) ;
195+ }
196+
197+ export function mapSchemaDataTypeToGRC20PropDataType ( dataType : SchemaDataType ) : CreatePropertyParams [ 'dataType' ] {
198+ switch ( true ) {
199+ case dataType === 'Boolean' : {
200+ return 'CHECKBOX' ;
201+ }
202+ case dataType === 'Date' : {
203+ return 'TIME' ;
204+ }
205+ case dataType === 'Number' : {
206+ return 'NUMBER' ;
207+ }
208+ case dataType === 'Point' : {
209+ return 'POINT' ;
210+ }
211+ case dataType === 'Url' : {
212+ return 'TEXT' ;
213+ }
214+ case isDataTypeRelation ( dataType ) : {
215+ return 'RELATION' ;
216+ }
217+ default : {
218+ return 'TEXT' ;
219+ }
220+ }
221+ }
0 commit comments