Skip to content

content: resolveContentSources does not resolve concrete relationship classes in resolved paths #1442

Description

@grigasp

Summary

Source resolution (resolveContentSources
packages/content/src/content/ResolveContentSources.ts) resolves the concrete entity endpoints
of each relationship-path step from the data, but leaves each step's relationshipName as the
declared (possibly base/abstract) relationship class. As a result, ResolvedPath steps can
carry non-concrete relationship classes, unlike their concrete sourceClassName/targetClassName.

Current behavior

resolveDeclarationPaths runs a data-driven query and maps each result row into a ResolvedPath.
It overwrites only the entity endpoints with the concrete classes found in the data, and spreads
the rest of the declared step verbatim:

path: declaration.path.map((step, i) => ({
  ...step,                                                    // relationshipName carried over as declared
  sourceClassName: (i === 0 ? target.primaryClass : row[i]), // concrete (from data)
  targetClassName: row[i + 1],                               // concrete (from data)
})),

The resolution queries (originalStrategy, rewriteStrategy, crossJoinStrategy) only project the
concrete class names of the step targets (buildClassNameColumns / buildClassIdColumns over
[s{i}]), never the relationship ([r{i}]). So the concrete relationship class is never read from
the data and never written back onto the step.

Expected behavior

ResolvedPath steps should carry concrete relationship classes discovered in the data, matching
the guarantee already provided for sourceClassName/targetClassName — i.e. "All path classes are
concrete — no base classes" (see ResolvedPath in
packages/content/src/content/ContentTarget.ts). For a polymorphic relationship, this means the
concrete relationship subclass(es) actually present in the scanned data.

Impact

Any consumer that relies on ResolvedPath steps being fully concrete is affected. Concretely, it
breaks the PropertyField.valueClassNames "concrete value-supplier classes" invariant for
relationship-class property fields (see the interim mitigation in createFieldsForStep,
packages/content/src/content/descriptor-building/RelatedFields.ts), which in turn makes
TransformableDescriptor.forkField carving by concrete relationship subclass impossible.

Proposed fix

Resolve the concrete relationship class per step alongside the entity endpoints:

  1. In each resolution strategy query, also project the concrete relationship class per step, e.g.
    ec_classname([r{i}].[ECClassId], 's.c'), and group on [r{i}].[ECClassId] (mirroring how the
    target class name/id columns are built today).
  2. Extend the result-row layout and the resolveDeclarationPaths map(...) to overwrite each
    step's relationshipName with the concrete value from the row, exactly like sourceClassName /
    targetClassName.

Affected files

  • packages/content/src/content/ResolveContentSources.ts (query projection + row mapping)
  • Tests under packages/content/src/test/ covering polymorphic relationship-class resolution
  • Optionally tighten the ResolvedPath doc in packages/content/src/content/ContentTarget.ts

Acceptance criteria

  • A resolved path over a polymorphic relationship reports the concrete relationship class(es) found
    in the data, not the declared base.
  • Existing sourceClassName / targetClassName resolution behavior is unchanged.
  • No regression in the resolution strategy race/short-circuit behavior.

Metadata

Metadata

Assignees

Type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions