Custom App module - parsing dates in reponse that has arrays in array with iterator

I have API response in this format:

[
    {
        "id": 321312,
        "code": "0430d2e5-37ae-4176-b39c-2ac741c8c032",
        "workflowStage": {
        },
        "properties": [
            {
                "id": 321312,
                "name": "Saved Files",
                "propertyGroupId": 321312,
                "priority": 1,
                "type": "system",
                "system": true,
                "systemType": "files",
                "systemName": "0e9af7b9-89b6-48a3-9b56-659194212965",
                "value": []
            },
            {
                "id": 321312,
                "name": "Active",
                "propertyGroupId": 321321,
                "priority": 6,
                "type": "system",
                "system": true,
                "systemType": "last_activity",
                "systemName": "9f0705ea-5cb5-4fc0-a734-fc96a6f13684",
                "value": "2024-04-10T16:50:30+02:00"
            }
            // more properties
        ],
        "persons": [],
        "anonymized": false,
        "documentVersions": [
            {
                "id": 321321,
                "code": "bd46d0c3-d8ce-411b-8ffe-06ca6c4ab9e0",
                "parentId": null,
                "deleted": false,
                "templateSuiteId": 123123,
                "created": "2024-04-10T16:50:30+02:00",
                "authorId": 321312,
                "languageId": 1,
                "documentRecordId": 321312,
                "documentRecordCode": "0430d2e5-37ae-4176-b39c-2ac741c8c032",
                "templates": [
                    {
                        "id": 321321,
                        "templateSuiteId": 321312,
                        "name": "salesForceOppTest",
                        "type": "document",
                        "noExport": false,
                        "internal": false,
                        "priority": 1000
                    }
                ]
            }
            // more document versions
        ],
        "events": [
        ]
    },
    {
       // other document record 
    }
]

And I need to change the dates in properties with parseDate to go public.

But when I use an iterator to iterate through arrays I have an array in an array and the iterator has weird behavior. For example when I do this:

"response": {
    "iterate": "{{body}}",
    "output": {
        "id": "{{item.id}}",
        "code": "{{item.code}}",
        "created": "{{parseDate(item.created, 'YYYY-MM-DDTHH:mm:ssZ', 'Europe/Prague')}}",
        "lastModified": "{{parseDate(item.lastModified, 'YYYY-MM-DDTHH:mm:ssZ', 'Europe/Prague')}}",
        "active": "{{parseDate(item.active, 'YYYY-MM-DDTHH:mm:ssZ', 'Europe/Prague')}}",
        "name": "{{item.name}}",

        "properties": {
            "iterate": "{{item.properties}}",
            "output": {
                "value": "{{parseDate(item.value, 'YYYY-MM-DDTHH:mm:ssZ', 'Europe/Prague')}}"
            }
        },

        "documentVersions": {
            "iterate": "{{item.documentVersions}}"
        },
        "events": {
            "iterate": "{{item.events}}",
            "eventCreated": "{{parseDate(item.created, 'YYYY-MM-DDTHH:mm:ssZ', 'Europe/Prague')}}"
        }
    },
    "limit": "{{parameters.limit}}"
}

It gives me this in properties:

Or this in documentVersions:
image

But neither works and I don’t know how to use the iterator properly.
I need to just have an array of properties under “properties” with parsed dates and the same thing in the document versions.

Hi Petr,

It looks like this might be easier implementing an IML since this is restricted at the moment they can be submitted via the form (see below).

For the module I think processing the body first will make life easier.

Mappable Parameters
Add a default timezone parameter that is an advanced option but defaulted to your preference, this is optional but I think would be more flexible for users if you will be publishing later.

	{
		"name": "default_tz",                              
		"type": "select", 
		"options": "rpc://getTimeZones",
		"mode": "choose",
		"default": "Europe/London",
		"label": "Default timezone.",                                         
		"help": "This is the default timezone to use for processing requests.",
                "advanced": true
	}

RPC
Create an RPC for supported timezones (see. Make Date & Time Functions & Referenced Supported Timezones) called getTimeZones it will drive the drop down for the mappable parameter default_tz as above.

{
  "response": {
    "output": [
      {
        "label": "Africa/Abidjan",
        "value": "Africa/Abidjan"
      },
      {
        ...
        ...
      },
      {
        "label": "W-SU",
        "value": "W-SU"
      },
      {
        "label": "WET",
        "value": "WET"
      },
      {
        "label": "Zulu",
        "value": "Zulu"
      }
    ]
  }
}

getTimeZones.json (52.8 KB)

Module Communication
Modify the iterate to process the payload after dates have been transformed.

Submit code via Let us deploy your IML Functions (tally.so).

   "iterate": "{{transformDates(body, parameters.default_tz}}",

IML
Add an IML to transform the dates, the conditional field handling is moved to the IML as I think the switch statement will make it easier to maintain and support new fields, IMHO if you have the same need in multiple modules then a single IML will be far less hassle than modifying the iterator each time.

Note: This is not fully tested as I haven’t had time to fully validate it, but should be broadly the right direction.

function transformDates(obj, tz = 'Europe/Prague') {
    let tmp = JSON.parse(obj);

    tmp.forEach(item => {

        // TODO: Default timezone may make more sense to be a parameter driven by an RPC to allow users to change it.
        //       iml.parseDate(item.created, 'YYYY-MM-DDTHH:mm:ssZ', parameters.timezone);
        console.debug('Item: ' + item.id)
    	if (item.created) {
            item.created = iml.parseDate(item.created, 'YYYY-MM-DDTHH:mm:ssZ', tz);
        }
    	if (item.lastModified) {
            item.lastModified = iml.parseDate(item.lastModified, 'YYYY-MM-DDTHH:mm:ssZ', tz);
        }
        if (item.active) {
            item.active = iml.parseDate(item.active, 'YYYY-MM-DDTHH:mm:ssZ', tz);
        }
    	if (item.properties){
    		item.properties.forEach(props => {
                console.debug('Property: ' + props.id);
    			switch(props.systemType) {
                    case 'last_activity':
                    case 'example_activity':
                        props.value = iml.parseDate(prop.value, 'YYYY-MM-DDTHH:mm:ssZ', tz);
                        console.debug('Date: ' + formatDate(parseDate(props.value, 'YYYY-MM-DDTHH:mm:ssZ', tz), 'YYYY-MM-DD'))
                        break;
                    default:
    
                }
    		});
    	}
    });

    console.debug('Processed Payload: ' + JSON.stringify(tmp))

	return tmp;
}

Anyhow I hope that helps if you haven’t already solved it, I’ll try and find some time to validate the IML for this scenario and update accordingly.

4 Likes