HubSpot / hubspot-api-nodejs

HubSpot API NodeJS Client Libraries for V3 version of the API
Apache License 2.0
327 stars 106 forks source link

Properties Batch Create method does not accept properties of type `Property` #10

Closed stphnlngdncoding closed 1 year ago

stphnlngdncoding commented 4 years ago

I am attempting to bulk-read properties from one hubspot environment to the other with the code below:

  const productionDealResponse = await hubspot.production.crm.properties.coreApi.getAll(
    'deals',
  )
  const prodDealProperies = productionDealResponse.body.results
  await hubspot.development.crm.properties.batchApi.create('deals', {
    inputs: prodDealProperies,
  })
}

However, it appears that the type Property which is returned from getAll is not compatible with the data that the inputs is expecting, namely PropertyCreate. This is the typescript error that I recieve:

Type 'Property[]' is not assignable to type 'PropertyCreate[]'.
  Type 'Property' is not assignable to type 'PropertyCreate'.
    Types of property ''type'' are incompatible.
      Type 'string' is not assignable to type 'TypeEnum'.

And if I try to use .apiRequest with the following signature:

  try {
    const developmentDealProperties = await hubspot.development.apiRequest({
      method: 'POST',
      path: '/crm/v3/properties/deals/batch/create',
      body: {
        inputs: prodDealProperies,
      },
    })
  } catch (e) {
    console.log(e)
  }

I recieve the following error:

      status: 'error',
      message: 'Invalid input JSON on line 1, column 147731: Cannot deserialize value of type `com.hubspot.crmpublic.core.models.schemas.properties.Type` from String "bool": value not one of declared Enum instance names: [number, datetime, date, enumeration, string]',
      correlationId: '74b4cc15-fc60-47d7-b29b-ac165b4326e5'

Is there any way to work around this?

AProts commented 4 years ago

Hi @stphnlngdncoding ,

Thanks for raising this question.

To correctly migrate properties from one environment to another you have to do following steps:

  1. Synchronize properties groups first:
    • create not existent groups in the target environment if needed
    • update existent properties groups if needed
  2. Get properties from the environment which should be synchronized with the target - propertiesToMigrate
  3. Filter out from propertiesToMigrate properties were modificationMetadata.readOnlyDefinition equals true since such properties can't be mutated
  4. Get properties from target environment - propertiesFromTarget
  5. Based on propertiesToMigrate & propertiesFromTarget prepare properties that should be created - propertiesToCreate
  6. Based on propertiesToMigrate & propertiesFromTarget prepare properties that should be updated - propertiesToUpdate

Regarding errors:

  1. Property[] and PropertyCreate[] are not compatible and it's really hard to convert one to another in TS. Unfortunately, we do not have helper functions for this, at least for now. If it's possible in your case I would recommend using '.apiRequest' as a workaround.

Here is code sample that might with conversion implementation:

        const enumFromValue = <T extends Record<string, any>>(val: string, _enum: T) => {
            const enumName = (Object.keys(_enum) as Array<keyof T>).find(k => _enum[k] === val)
            if (!enumName) {
                throw Error(`Cannot find enum value for ${val}`)
            }
            return _enum[enumName]
        }

        const client = new Client( {apiKey: '123'});
        const result = await client.crm.properties.coreApi.getAll('deals')
        const propertiesToCreate: propertiesModels.PropertyCreate[] = result.body.results
            .filter(property => !property.modificationMetadata.readOnlyDefinition)
            .map(property => (
                {
                    ...property,
                    name: `${property.name}_${new Date().getTime()}`,
                    label: property.label,
                    type: property.type === 'bool'? propertiesModels.PropertyCreate.TypeEnum.Enumeration : enumFromValue(property.type, propertiesModels.PropertyCreate.TypeEnum),
                    fieldType: enumFromValue(property.fieldType, propertiesModels.PropertyCreate.FieldTypeEnum),
                    groupName: property.groupName
                }
            ))

        await client.crm.properties.batchApi.create('deals', {
            inputs: propertiesToCreate
        })
  1. Workaround with '.apiRequest' failed because some old properties have 'type' - bool which is not supported on new API. To make it work, please change bool to enumeration

I'll create an issue in HubSpot about this and keep you posted on any updates.

Kind Regards, Andrii