@@ -82,47 +82,96 @@ export type SchemaDataTypeRelation = typeof SchemaDataTypeRelation.Type;
8282/**
8383 * @since 0.0.1
8484 */
85- export const SchemaDataType = EffectSchema . Union (
86- EffectSchema . Literal ( 'Text' , 'Number' , 'Boolean' , 'Date' , 'Point' , 'Url' ) ,
87- SchemaDataTypeRelation ,
88- ) ;
85+ export const SchemaDataTypePrimitive = EffectSchema . Literal ( 'Text' , 'Number' , 'Boolean' , 'Date' , 'Point' , 'Url' ) ;
86+ /**
87+ * @since 0.0.1
88+ */
89+ export type SchemaDataTypePrimitive = typeof SchemaDataTypePrimitive . Type ;
90+ /**
91+ * @since 0.0.1
92+ */
93+ export const SchemaDataType = EffectSchema . Union ( SchemaDataTypePrimitive , SchemaDataTypeRelation ) ;
8994/**
9095 * @since 0.0.1
9196 */
9297export type SchemaDataType = typeof SchemaDataType . Type ;
98+ /**
99+ * @since 0.0.1
100+ */
101+ export const SchemaTypePropertyRelation = EffectSchema . Struct ( {
102+ name : EffectSchema . NonEmptyTrimmedString ,
103+ knowledgeGraphId : EffectSchema . NullOr ( EffectSchema . UUID ) ,
104+ dataType : SchemaDataTypeRelation ,
105+ relationType : EffectSchema . NonEmptyTrimmedString . annotations ( {
106+ identifier : 'SchemaTypePropertyRelation.relationType' ,
107+ description : 'name of the type within the schema that this property is related to' ,
108+ examples : [ 'Account' ] ,
109+ } ) ,
110+ } ) ;
111+ /**
112+ * @since 0.0.1
113+ */
114+ export type SchemaTypePropertyRelation = typeof SchemaTypePropertyRelation . Type ;
115+ /**
116+ * @since 0.0.1
117+ */
118+ export const SchemaTypePropertyPrimitive = EffectSchema . Struct ( {
119+ name : EffectSchema . NonEmptyTrimmedString ,
120+ knowledgeGraphId : EffectSchema . NullOr ( EffectSchema . UUID ) ,
121+ dataType : SchemaDataTypePrimitive ,
122+ } ) ;
123+ /**
124+ * @since 0.0.1
125+ */
126+ export type SchemaTypePropertyPrimitive = typeof SchemaTypePropertyPrimitive . Type ;
127+
128+ /**
129+ * @since 0.0.1
130+ */
131+ export function propertyIsRelation (
132+ property : SchemaTypePropertyPrimitive | SchemaTypePropertyRelation ,
133+ ) : property is SchemaTypePropertyRelation {
134+ return isDataTypeRelation ( property . dataType ) ;
135+ }
136+
137+ /**
138+ * @since 0.0.1
139+ */
140+ export const SchemaType = EffectSchema . Struct ( {
141+ name : EffectSchema . NonEmptyTrimmedString ,
142+ knowledgeGraphId : EffectSchema . NullOr ( EffectSchema . UUID ) ,
143+ properties : EffectSchema . Array ( EffectSchema . Union ( SchemaTypePropertyPrimitive , SchemaTypePropertyRelation ) ) . pipe (
144+ EffectSchema . minItems ( 1 ) ,
145+ EffectSchema . filter ( namesAreUnique , {
146+ identifier : 'DuplicatePropertyNames' ,
147+ jsonSchema : { } ,
148+ description : 'The property.name must be unique across all properties in the type' ,
149+ } ) ,
150+ ) ,
151+ } ) ;
152+ /**
153+ * @since 0.0.1
154+ */
155+ export type SchemaType = typeof SchemaType . Type ;
93156
94157/**
95158 * Represents the user-built schema object to generate a `Mappings` definition for
96159 *
97160 * @since 0.0.1
98161 */
99162export const Schema = EffectSchema . Struct ( {
100- types : EffectSchema . Array (
101- EffectSchema . Struct ( {
102- name : EffectSchema . NonEmptyTrimmedString ,
103- knowledgeGraphId : EffectSchema . NullOr ( EffectSchema . UUID ) ,
104- properties : EffectSchema . Array (
105- EffectSchema . Struct ( {
106- name : EffectSchema . NonEmptyTrimmedString ,
107- knowledgeGraphId : EffectSchema . NullOr ( EffectSchema . UUID ) ,
108- dataType : SchemaDataType ,
109- } ) ,
110- ) . pipe (
111- EffectSchema . minItems ( 1 ) ,
112- EffectSchema . filter ( namesAreUnique , {
113- identifier : 'DuplicatePropertyNames' ,
114- jsonSchema : { } ,
115- description : 'The property.name must be unique across all properties in the type' ,
116- } ) ,
117- ) ,
118- } ) ,
119- ) . pipe (
163+ types : EffectSchema . Array ( SchemaType ) . pipe (
120164 EffectSchema . minItems ( 1 ) ,
121165 EffectSchema . filter ( namesAreUnique , {
122166 identifier : 'DuplicateTypeNames' ,
123167 jsonSchema : { } ,
124168 description : 'The type.name must be unique across all types in the schema' ,
125169 } ) ,
170+ EffectSchema . filter ( allRelationPropertyTypesExist , {
171+ identifier : 'AllRelationTypesExist' ,
172+ jsonSchema : { } ,
173+ description : 'Each type property of dataType RELATION must have a type of the same name in the schema' ,
174+ } ) ,
126175 ) ,
127176} ) . annotations ( {
128177 identifier : 'typesync/Schema' ,
@@ -163,14 +212,88 @@ export const SchemaKnownDecoder = EffectSchema.decodeSync(Schema);
163212export const SchemaUnknownDecoder = EffectSchema . decodeUnknownSync ( Schema ) ;
164213
165214/**
215+ * Iterate through all properties in all types in the schema of `dataType` === `Relation(${string})`
216+ * and validate that the schema.types have a type for the existing relation
166217 *
218+ * @example <caption>All types exist</caption>
219+ * ```ts
220+ * import { allRelationPropertyTypesExist, type Mapping } from '@graphprotocol/typesync/Mapping'
221+ *
222+ * const types: Mapping['types'] = [
223+ * {
224+ * name: "Account",
225+ * knowledgeGraphId: null,
226+ * properties: [
227+ * {
228+ * name: "username",
229+ * dataType: "Text",
230+ * knowledgeGraphId: null
231+ * }
232+ * ]
233+ * },
234+ * {
235+ * name: "Event",
236+ * knowledgeGraphId: null,
237+ * properties: [
238+ * {
239+ * name: "speaker",
240+ * dataType: "Relation(Account)"
241+ * relationType: "Account",
242+ * knowledgeGraphId: null,
243+ * }
244+ * ]
245+ * }
246+ * ]
247+ * expect(allRelationPropertyTypesExist(types)).toEqual(true)
248+ * ```
249+ *
250+ * @example <caption>Account type is missing</caption>
251+ * ```ts
252+ * import { allRelationPropertyTypesExist, type Mapping } from '@graphprotocol/typesync/Mapping'
253+ *
254+ * const types: Mapping['types'] = [
255+ * {
256+ * name: "Event",
257+ * knowledgeGraphId: null,
258+ * properties: [
259+ * {
260+ * name: "speaker",
261+ * dataType: "Relation(Account)",
262+ * relationType: "Account",
263+ * knowledgeGraphId: null,
264+ * }
265+ * ]
266+ * }
267+ * ]
268+ * expect(allRelationPropertyTypesExist(types)).toEqual(false)
269+ * ```
270+ *
271+ * @since 0.0.1
272+ *
273+ * @param types the user-submitted schema types
274+ */
275+ export function allRelationPropertyTypesExist ( types : ReadonlyArray < SchemaType > ) : boolean {
276+ const unqTypeNames = EffectArray . reduce ( types , new Set < string > ( ) , ( names , curr ) => names . add ( curr . name ) ) ;
277+ return pipe (
278+ types ,
279+ EffectArray . flatMap ( ( curr ) => curr . properties ) ,
280+ EffectArray . filter ( ( prop ) => propertyIsRelation ( prop ) ) ,
281+ EffectArray . every ( ( prop ) => unqTypeNames . has ( prop . relationType ) ) ,
282+ ) ;
283+ }
284+
285+ /**
286+ * Takes the user-submitted schema, validates it, and build the `Mapping` definition for the schema.
167287 *
168288 * @since 0.0.1
169289 *
170- * @param schema user-built and submitted schema
290+ * @param input user-built and submitted schema
171291 * @returns the generated [Mapping] definition from the submitted schema
172292 */
173- export async function generateMapping ( schema : Schema ) : Promise < Mapping > {
293+ export async function generateMapping ( input : Schema ) : Promise < Mapping > {
294+ // validate the schema since the input is the type, but the schema has additional filters against it to validate as well
295+ const schema = SchemaKnownDecoder ( input ) ;
296+
174297 const entries : Array < MappingEntry & { typeName : string } > = [ ] ;
175298 const ops : Array < Op > = [ ] ;
176299
@@ -188,6 +311,9 @@ export async function generateMapping(schema: Schema): Promise<Mapping> {
188311 const { id, ops : createTypePropOp } = Graph . createProperty ( {
189312 name : property . name ,
190313 dataType : 'RELATION' ,
314+ /**
315+ * @todo fill in the relationValueTypes and properties for creating a relation property
316+ */
191317 relationValueTypes : [ ] ,
192318 properties : [ ] ,
193319 } ) ;
@@ -198,7 +324,7 @@ export async function generateMapping(schema: Schema): Promise<Mapping> {
198324 }
199325 const { id, ops : createTypePropOp } = Graph . createProperty ( {
200326 name : property . name ,
201- dataType : mapSchemaDataTypeToGRC20PropDataType ( property . dataType ) ,
327+ dataType,
202328 } ) ;
203329 typePropertyIds . push ( { propName : property . name , id } ) ;
204330 ops . push ( ...createTypePropOp ) ;
@@ -250,6 +376,12 @@ export async function generateMapping(schema: Schema): Promise<Mapping> {
250376 ) ;
251377}
252378
379+ /**
380+ * @since 0.0.1
381+ *
382+ * @param dataType the dataType from the user-submitted schema
383+ * @returns the mapped to GRC-20 dataType for the GRC-20 ops
384+ */
253385export function mapSchemaDataTypeToGRC20PropDataType ( dataType : SchemaDataType ) : CreatePropertyParams [ 'dataType' ] {
254386 switch ( true ) {
255387 case dataType === 'Boolean' : {
0 commit comments