Issue with Transformer

Hey all,

I would love to get some help in what I am doing wrong with my transformer. I have a similar transformer that works just fine on another workflow, but in this version there is a conditional that needs evaluating beforehand. Here is what I have as data coming out of the workflow:

Now, when I add the transformer I get an error:

Now, the transformer does some flattening of the data and some calculations I like to display for the users on the interface:

const flattenData = (x) => {
let newData = {}
newData.time_id = x.id
newData.spent_date = x.spent_date
newData.hours = Number(x.hours);
newData.hours_without_timer = x.hours_without_timer
newData.rounded_hours = x.rounded_hours
newData.rd_rounded_hours = Math.ceil(x.hours * 10) / 10;
newData.notes = x.notes
newData.is_locked = x.is_locked
newData.locked_reason = x.locked_reason
newData.is_closed = x.is_closed
newData.is_billed = x.is_billed
newData.timer_started_at = x.timer_started_at
newData.started_time = x.started_time
newData.ended_time = x.ended_time
newData.is_running = x.is_running
newData.billable = x.billable
newData.budgeted = x.budgeted
newData.billable_rate = x.billable_rate
newData.cost_rate = x.cost_rate
newData.created_at = x.created_at
newData.updated_at = x.updated_at
newData.invoice = x.invoice

newData.user_id = x.user.id
newData.user_name = x.user.name
var fullName = x.user.name.split(' ')
newData.first_name = fullName[0]
newData.last_name = fullName[fullName.length - 1]
newData.initials = newData.first_name[0] + newData.last_name[0]

newData.client_id = x.client.id
newData.client_name = x.client.name
newData.client_currency = x.client.currency

newData.project_id = x.project.id
newData.project_name = x.project.name

newData.project_code = x.project.code

newData.task_id = parseInt(x.task.id, 10);
newData.task_name = x.task.name

// Deal with the PM+ nonsense
if (x.task && x.task.name && x.task.name.includes("PM+")) {
newData.pm_plus = true;
newData.pm_plus_hours = Math.ceil(Number(x.hours) * 10) / 10;
} else {
newData.pm_plus = false;
newData.pm_plus_hours = 0;
}

// Deal with the Epiq Remediation nonsense
if (x.task && x.task.name && x.task.name.includes("zzz")) {
newData.epiq_rem_hours = Math.ceil(x.hours * 10) / 10;
newData.epiq_rem_cost = Math.ceil(x.hours * x.billable_rate * 10) / 10;
} else {
newData.epiq_rem_hours = 0;
newData.epiq_rem_cost = 0;
}

// Initialize to zero, might be overridden by Extras
newData.pm_plus_tagged_hours = 0;

newData.user_assignment_id = x.user_assignment.id
newData.user_assignment_is_project_manager = x.user_assignment.is_project_manager
newData.user_assignment_is_active = x.user_assignment.is_active
newData.user_assignment_use_default_rates = x.user_assignment.use_default_rates
newData.user_assignment_budget = x.user_assignment.budget
newData.user_assignment_created_at = x.user_assignment.created_at
newData.user_assignment_updated_at = x.user_assignment.updated_at
newData.user_assignment_hourly_rate = x.user_assignment.hourly_rate

newData.task_assignment_id = x.task_assignment.id
newData.task_assignment_billable = x.task_assignment.billable
newData.task_assignment_is_active = x.task_assignment.is_active
newData.task_assignment_created_at = x.task_assignment.created_at
newData.task_assignment_updated_at = x.task_assignment.updated_at
newData.task_assignment_hourly_rate = x.task_assignment.hourly_rate

newData.extra_pm_plus = x.extra_pm_plus
newData.extra_enomaly_flags = x.extra_anomaly_flags
newData.extra_notes = x.extra_notes
newData.extra_grouping = x.extra_grouping
newData.extra_reviewer_comments = x.extra_reviewer_comments
newData.pm_plus_tag = x.pm_plus_tag
newData.manager_approval = x.extra_approval

// Calculate effective rate by letting PM+ rates override regular user rates
//var task_rate = "";
let task_rate = String(x.task_assignment.hourly_rate);
if (!task_rate) {
task_rate = "0";
}
var task_rate_clean = task_rate.replace(/[^0-9.-]+/g,"");
var task_rate_num = Number(task_rate_clean);
if (task_rate_num > 0) {
newData.pm_plus_rate = x.task_assignment.hourly_rate
} else {
newData.pm_plus_rate = x.user_assignment.hourly_rate
}
newData.task_assignment_budget = x.task_assignment.budget

// // Grab external reference data that's necessary to write back if we change anything
// if (x.external_reference) {
// newData.ext_ref_id = x.external_reference.id
// newData.ext_ref_group_id = x.external_reference.group_id
// newData.ext_ref_account_id = x.external_reference.account_id
// newData.ext_ref_permalink = x.external_reference.permalink
// }

return newData
}

const time_entries = data.time_entries.all_entries.map(a => flattenData(a))
const newData = ({}, {search: {total: data.time_entries.all_entries.total, time_entries: time_entries}})

// // Check if project_status is false and run flattenDataFalse if so
// if (data.project_status === "false"){
// const time_entries = data.time_entries.all_entries.map(a => flattenData(a))
// const newData = ({}, {search: {total: data.time_entries.all_entries.total, time_entries: time_entries}})
// }

return newData;

One note: the project_status value is returned as a string. I don't know if this could be causing the issue? I've have tried writing the if statement both as a string and as a Boolean, so I'm not sure.

The structure I am using to access the time_entries seems to be correct also. So again I'm not sure where I'm going wrong. I'm new to JS so it could be something simple I'm not seeing.

Anything pointers in resolving this would be greatly appreciated.

Thanks!

feel free to skip to the tldr; otherwise....

The Meaning Behind the Error

if you have an object like

const myObj = {
    name: 'my_project',
    data: {
      project_status: "false"
    }
}

you would access the status as you've tried with data.project_status or with the obj above myObj.data.project_status.... but, with the object below:

const myObj = {
    name: 'my_project',
    data_: {
      project_status: "false"
    }
}

if you tried to access project_status using myObj.data.project_status forgetting that you changed data to data_ js will grab myObj.data which is null or undefined. no problems yet. next it tries to get the project_status property using undefined.project_status.... and this is why the error is worded the way it is.

tldr;
The Fix
given the following obj

const myObj = {
    name: 'my_project',
    data: {
      project_status: "false"
    }
}

use (myObj.data?.project_status?? 'false') == 'false'

What's Going On

  1. evaluate myObj.data?.project_status and store as temp1
    • when myObj.data doesn't exist or is empty it doesn't check for .project_status and instead uses null making the conditional evaluate to
      null == 'false'
      which can still be problem and thats why we use ?? 'false' which simply says if the right side of the ?? operator is null use false as the value otherwise continue on as normal.
  2. evaluate temp1 ?? 'false' and store as temp2
    • if temp1 is null then temp2 is 'false'
    • if temp1 is 'false' then temp2 is 'false'`
    • if temp1 is 'true' then temp2 is 'true'`
  3. evaluate temp2 == 'false'
2 Likes

Thank you so much for your help!

2 Likes