Skip to content

Dry-run diff log renders array items as [object Object] #1452

Description

@mbogunovic

Version

auth0-deploy-cli 8.42.0, and present on main at the time of writing.

What happens

When a dry run finds an array item present on one side only, the diff log records the item as [object Object]:

Array length difference for [acme.connections]: local:1 vs remote:0
Array item difference at [acme.connections[0]]: local:[object Object] vs remote:undefined

The message carries no information about which item would be added or removed, which for organization connections is the entire content of the change. The same strings are what --dry-run --interactive writes to dry-run-diff-log.json, so a machine-readable log of an object-array change is unusable as well.

Reproduction

const {
  getObjectDifferences,
} = require('auth0-deploy-cli/lib/tools/calculateDryRunChanges');

getObjectDifferences(
  { connections: [{ connection_id: 'con_abc', assign_membership_on_login: false }] },
  { connections: [] },
  'acme',
  'organizations'
);
// [
//   'Array length difference for [acme.connections]: local:1 vs remote:0',
//   'Array item difference at [acme.connections[0]]: local:[object Object] vs remote:undefined'
// ]

Root cause

The message is built by template-literal interpolation, which calls Object.prototype.toString on an object item:

// src/tools/calculateDryRunChanges.ts:235
const message = `Array item difference at [${currentPath}[${index}]]: local:${item} vs remote:${normalizedRemoteValue[index]}`;

The else if at line 234 is reached exactly when one of the two sides is not an object: an added or removed element, or a mixed-type array. The object-vs-object case recurses at line 226 and is fine.

Note the diff log stores formatted strings rather than the values, so the information is not recoverable downstream: by the time anything reads getDiffLog() or dry-run-diff-log.json, the object is gone.

Expected

The added or removed item is rendered as something a reader can act on, for example:

Array item difference at [acme.connections[0]]: local:{"assign_membership_on_login":false,"connection_id":"con_abc"} vs remote:undefined

Suggested fix

Serialize non-primitives instead of interpolating them:

const format = (value: unknown): string =>
  typeof value === 'object' && value !== null ? JSON.stringify(value) : String(value);

const message = `Array item difference at [${currentPath}[${index}]]: local:${format(
  item
)} vs remote:${format(normalizedRemoteValue[index])}`;

A length cap would be reasonable if unbounded items are a concern.

Related: #1451 - both are in the handling of object arrays in getObjectDifferences, but they are independent, and this one is cosmetic next to that one.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions