Ver Fonte

InputParser - allow "new" and "existing" inside UPDATE operations

Leszek Wiesner há 4 anos atrás
pai
commit
8eeea6ce27

+ 6 - 6
content-directory-schemas/README.md

@@ -182,7 +182,7 @@ The best way to ilustrate this would be by providing some examples:
       .signAndSend(SENDER_KEYPAIR)
   }
 ```
-__Full example with comments can be found in `content-directory-schemas/examples/createChannel.ts` and ran with `yarn workspace cd-schemas example:createChannel`__
+_Full example with comments can be found in `content-directory-schemas/examples/createChannel.ts` and ran with `yarn workspace cd-schemas example:createChannel`_
 
 #### Creating a video
 ```
@@ -239,12 +239,10 @@ async main() {
     .signAndSend(SENDER_KEYPAIR)
 }
 ```
-__Full example with comments can be found in `content-directory-schemas/examples/createVideo.ts` and ran with `yarn workspace cd-schemas example:createChannel`__
+_Full example with comments can be found in `content-directory-schemas/examples/createVideo.ts` and ran with `yarn workspace cd-schemas example:createChannel`_
 
 #### Update channel title
 
-Note that updates are currently very limitied (ie. the `new` and `existing` keywords are not supported for references etc.)
-
 ```
 import { InputParser } from 'cd-schemas'
 import { ChannelEntity } from 'cd-schemas/types/entities/ChannelEntity'
@@ -261,14 +259,16 @@ async function main() {
 
   const CHANNEL_ID = await parser.findEntityIdByUniqueQuery({ title: 'Example channel' }, 'Channel')
 
-  const updateOperation = await parser.createEntityUpdateOperation(channelUpdateInput, 'Channel', CHANNEL_ID)
+  const updateOperations = await parser.getEntityUpdateOperations(channelUpdateInput, 'Channel', CHANNEL_ID)
 
   await api.tx.contentDirectory
     .transaction({ Member: SENDER_MEMBER_ID }, [updateOperation])
     .signAndSend(SENDER_KEYPAIR)
 }
 ```
-__Full example with comments can be found in `content-directory-schemas/examples/updateChannelTitle.ts` and ran with `yarn workspace cd-schemas example:updateChannelTitle`__
+_Full example with comments can be found in `content-directory-schemas/examples/updateChannelTitle.ts` and ran with `yarn workspace cd-schemas example:updateChannelTitle`_
+
+Note: Updates can also inlucde `new` and `existing` keywords. In case `new` is specified inside the update - `CreateEntity` and `AddSchemaSupportToEntity` operations will be included as part of the operations returned by `InputParser.getEntityUpdateOperations`.
 
 ## Current limitations
 

+ 3 - 3
content-directory-schemas/examples/updateChannelTitle.ts

@@ -27,8 +27,8 @@ async function main() {
   // created in ./createChannel.ts example (normally we would probably use some other way to do it, ie.: query node)
   const CHANNEL_ID = await parser.findEntityIdByUniqueQuery({ title: 'Example channel' }, 'Channel')
 
-  // Use createEntityUpdateOperation to parse the input
-  const updateOperation = await parser.createEntityUpdateOperation(
+  // Use getEntityUpdateOperations to parse the update input
+  const updateOperations = await parser.getEntityUpdateOperations(
     channelUpdateInput,
     'Channel', // Class name
     CHANNEL_ID // Id of the entity we want to update
@@ -37,7 +37,7 @@ async function main() {
   await api.tx.contentDirectory
     .transaction(
       { Member: 0 }, // We use member with id 0 as actor (in this case we assume this is Alice)
-      [updateOperation] // The only operation we execute in this transaction is a single updateOperation
+      updateOperations // In this case this will be just a single UpdateEntityPropertyValues operation
     )
     .signAndSend(ALICE)
 }

+ 70 - 37
content-directory-schemas/src/helpers/InputParser.ts

@@ -25,6 +25,7 @@ export class InputParser {
   private batchInputs: EntityBatch[]
   private createEntityOperations: OperationType[] = []
   private addSchemaToEntityOprations: OperationType[] = []
+  private updateEntityPropertyValuesOperations: OperationType[] = []
   private entityIndexByUniqueQueryMap = new Map<string, number>()
   private entityIdByUniqueQueryMap = new Map<string, number>()
   private entityByUniqueQueryCurrentIndex = 0
@@ -32,7 +33,7 @@ export class InputParser {
   private classMapInitialized = false
   private entityIdByUniqueQueryMapInitialized = false
 
-  static createWithKnownSchemas(api: ApiPromise, entityBatches?: EntityBatch[]) {
+  static createWithKnownSchemas(api: ApiPromise, entityBatches?: EntityBatch[]): InputParser {
     return new InputParser(
       api,
       [],
@@ -230,7 +231,34 @@ export class InputParser {
     return parametrizedClassPropValues
   }
 
-  private async parseEntityInput(entityInput: Record<string, any>, schema: AddClassSchema) {
+  private async existingEntityQueryToParametrizedPropertyValue(className: string, uniquePropVal: Record<string, any>) {
+    try {
+      // First - try to find in existing batches
+      const entityIndex = this.findEntityIndexByUniqueQuery(uniquePropVal, className)
+      return createType('ParametrizedPropertyValue', { InternalEntityJustAdded: entityIndex })
+    } catch (e) {
+      // If not found - fallback to chain search
+      const entityId = await this.findEntityIdByUniqueQuery(uniquePropVal, className)
+      return createType('ParametrizedPropertyValue', {
+        InputPropertyValue: { Single: { Reference: entityId } },
+      })
+    }
+  }
+
+  // parseEntityInput Overloads
+  private parseEntityInput(entityInput: Record<string, any>, schema: AddClassSchema): Promise<number>
+  private parseEntityInput(
+    entityInput: Record<string, any>,
+    schema: AddClassSchema,
+    updatedEntityId: number
+  ): Promise<void>
+
+  // Parse entity input. Speficy "updatedEntityId" only if want to parse into update operation!
+  private async parseEntityInput(
+    entityInput: Record<string, any>,
+    schema: AddClassSchema,
+    updatedEntityId?: number
+  ): Promise<void | number> {
     const parametrizedPropertyValues = await this.createParametrizedPropertyValues(
       entityInput,
       schema,
@@ -243,38 +271,41 @@ export class InputParser {
             const entityIndex = await this.parseEntityInput(value.new, refEntitySchema)
             return createType('ParametrizedPropertyValue', { InternalEntityJustAdded: entityIndex })
           } else if (Object.keys(value).includes('existing')) {
-            try {
-              const entityIndex = this.findEntityIndexByUniqueQuery(value.existing, refEntitySchema.className)
-              return createType('ParametrizedPropertyValue', { InternalEntityJustAdded: entityIndex })
-            } catch (e) {
-              // Fallback to chain search
-              const entityId = await this.findEntityIdByUniqueQuery(value.existing, refEntitySchema.className)
-              return createType('ParametrizedPropertyValue', {
-                InputPropertyValue: { Single: { Reference: entityId } },
-              })
-            }
+            return this.existingEntityQueryToParametrizedPropertyValue(refEntitySchema.className, value.existing)
           }
         }
         return undefined
       }
     )
 
-    // Add operations
-    const createEntityOperationIndex = this.createEntityOperations.length
-    const classId = this.classIdByNameMap.get(schema.className)
-    this.createEntityOperations.push(createType('OperationType', { CreateEntity: { class_id: classId } }))
-    this.addSchemaToEntityOprations.push(
-      createType('OperationType', {
-        AddSchemaSupportToEntity: {
-          schema_id: 0,
-          entity_id: { InternalEntityJustAdded: createEntityOperationIndex },
-          parametrized_property_values: parametrizedPropertyValues,
-        },
-      })
-    )
+    if (updatedEntityId) {
+      // Update operation
+      this.updateEntityPropertyValuesOperations.push(
+        createType('OperationType', {
+          UpdatePropertyValues: {
+            entity_id: { ExistingEntity: updatedEntityId },
+            new_parametrized_property_values: parametrizedPropertyValues,
+          },
+        })
+      )
+    } else {
+      // Add operations (createEntity, AddSchemaSupportToEntity)
+      const createEntityOperationIndex = this.createEntityOperations.length
+      const classId = this.getClassIdByName(schema.className)
+      this.createEntityOperations.push(createType('OperationType', { CreateEntity: { class_id: classId } }))
+      this.addSchemaToEntityOprations.push(
+        createType('OperationType', {
+          AddSchemaSupportToEntity: {
+            schema_id: 0,
+            entity_id: { InternalEntityJustAdded: createEntityOperationIndex },
+            parametrized_property_values: parametrizedPropertyValues,
+          },
+        })
+      )
 
-    // Return CreateEntity operation index
-    return createEntityOperationIndex
+      // Return CreateEntity operation index
+      return createEntityOperationIndex
+    }
   }
 
   private reset() {
@@ -282,6 +313,7 @@ export class InputParser {
     this.classIdByNameMap = new Map<string, number>()
     this.createEntityOperations = []
     this.addSchemaToEntityOprations = []
+    this.updateEntityPropertyValuesOperations = []
     this.entityByUniqueQueryCurrentIndex = 0
   }
 
@@ -306,21 +338,22 @@ export class InputParser {
     return operations
   }
 
-  public async createEntityUpdateOperation(
-    entityInput: Record<string, any>,
+  public async getEntityUpdateOperations(
+    input: Record<string, any>,
     className: string,
     entityId: number
-  ): Promise<OperationType> {
+  ): Promise<OperationType[]> {
     await this.initializeClassMap()
     const schema = this.schemaByClassName(className)
-    const parametrizedPropertyValues = await this.createParametrizedPropertyValues(entityInput, schema)
+    await this.parseEntityInput(input, schema, entityId)
+    const operations = [
+      ...this.createEntityOperations,
+      ...this.addSchemaToEntityOprations,
+      ...this.updateEntityPropertyValuesOperations,
+    ]
+    this.reset()
 
-    return createType('OperationType', {
-      UpdatePropertyValues: {
-        entity_id: { ExistingEntity: entityId },
-        new_parametrized_property_values: parametrizedPropertyValues,
-      },
-    })
+    return operations
   }
 
   public async parseAddClassSchemaExtrinsic(inputData: AddClassSchema) {