Upstream chain

This is the critical part of Launch comparison workflows: Adobe does not return one special upstream-chain object, but ReactorSDK now assembles one for resource-centric lookups. You can still work at the raw library level when you want full control, but most applications should start with the higher-level upstream_chain helpers.

What ReactorSDK exposes

AttributeData typeDescription
upstream_library(library_id)
Library

Returns only the immediate upstream library from Adobe's /libraries/:id/upstream_library endpoint.

upstream_libraries(library_id, property_id:)
Array<Library>

Returns the full chain of upstream libraries, nearest first, based on environment stage.

upstream_chain_for_resource(resource_or_id, library_id:, property_id:)
UpstreamChain

Returns the SDK-assembled upstream chain for a single resource in the context of a target library.

find_snapshot(library_id, property_id:)
LibrarySnapshot

Returns the snapshot-aware library wrapper used by the comprehensive review helpers.

comprehensive_upstream_chain_for_resource(resource_or_id, library_id:, property_id:, resource_type: nil)
ComprehensiveUpstreamChain

Returns the snapshot-aware upstream chain with associated records and normalized review payloads.

find_with_resources(library_id)
LibraryWithResources

Loads one library together with its included rules, data elements, and extensions, with each included resource exposing a gem-added revision_id.

revisions.find(revision_id)
Revision

Resolves a revision ID into a point-in-time resource snapshot through entity_snapshot.

How upstream libraries are fetched

The helper computes the chain in multiple requests because Adobe does not expose the full ordered chain in a single response:

  1. GET /libraries/:library_id is called first so missing target libraries fail immediately.
  2. GET /libraries/:library_id/relationships/environment fetches the target environment ID.
  3. GET /environments/:environment_id reads the target environment stage.
  4. If the target stage is production, the helper returns [].
  5. GET /properties/:property_id/libraries loads the property's library list.
  6. For each candidate library, the helper resolves that library's environment relationship and environment stage.
  7. The helper returns only the libraries in stages above the target, ordered nearest first.

The stage order used by the helper is:

  • development
  • staging
  • production

That means the returned chain looks like this:

  • Target library in development -> [staging_library, production_library]
  • Target library in staging -> [production_library]
  • Target library in production -> []

The return value of upstream_libraries is a plain Array<Library>. The order of the array is the stage relationship.

Important details:

  • Each element is a normal Library resource, not a custom chain node.
  • The returned Library objects do not get a derived stage attribute attached.
  • If you need the actual stage for a chain node, call client.libraries.environment(library.id) and read the environment's stage.
  • The chain only tells you which library to inspect next. It does not include resource-level revision data by itself.

Fetch the upstream chain

# LB_DEV = development library ID.
# PR123 = property ID.
# Fetch the ordered upstream libraries for a development library.
chain = client.libraries.upstream_libraries(
  "LB_DEV",
  property_id: "PR123"
)

# Print the Adobe IDs of the upstream libraries in traversal order.
chain.map(&:id)
# LB_STG = staging library ID.
# LB_PRD = production library ID.
# => ["LB_STG", "LB_PRD"]

The easiest API is to ask the resource family directly for its upstream chain:

  • client.rules.upstream_chain(...)
  • client.data_elements.upstream_chain(...)
  • client.extensions.upstream_chain(...)

All three return the same UpstreamChain wrapper. If you need the generic version, client.libraries.upstream_chain_for_resource(...) provides the same behavior.

The returned UpstreamChain includes:

  • target_resource
  • target_revision_id
  • entries
  • nearest_match
  • found?

Each upstream entry includes:

  • library
  • stage
  • resource
  • revision_id
  • revision
  • entity_snapshot

The comprehensive helpers return the same ordered chain shape, but each entry also includes:

  • comprehensive_resource
  • normalized_payload
  • normalized_json

Resolve a resource upstream with upstream_chain

# RL123 = rule ID.
# LB_DEV = development library ID.
# PR123 = property ID.
# Resolve the full upstream chain for one rule.
chain = client.rules.upstream_chain(
  "RL123",
  library_id: "LB_DEV",
  property_id: "PR123"
)

# Print the revision currently present in the target library.
puts chain.target_revision_id

# The first matching upstream library is usually the one you compare first.
nearest = chain.nearest_match

# Print the nearest matching library ID and stage.
puts nearest&.library&.id
puts nearest&.stage

# Print the upstream revision ID and point-in-time snapshot.
puts nearest&.revision_id
puts nearest&.entity_snapshot

# Iterate over every inspected upstream library.
chain.each do |entry|
  puts "#{entry.library.id} -> #{entry.present?}"
end

Comprehensive lookup

Use the comprehensive lookup when you want the associated records and the normalized JSON that is easier to feed into review tooling.

  • client.rules.comprehensive_upstream_chain(...)
  • client.data_elements.comprehensive_upstream_chain(...)
  • client.extensions.comprehensive_upstream_chain(...)
  • client.libraries.comprehensive_upstream_chain_for_resource(...)

Resolve a resource upstream with the comprehensive chain

# RL123 = rule ID.
# LB_DEV = development library ID.
# PR123 = property ID.
chain = client.rules.comprehensive_upstream_chain(
  "RL123",
  library_id: "LB_DEV",
  property_id: "PR123"
)

# Snapshot-aware target resource with rule components and normalized JSON.
puts chain.target_comprehensive_resource.normalized_json

# Use the staging entry explicitly when you want a dev-vs-staging review.
staging = chain.entries.find { |entry| entry.stage == "staging" }

# Print the normalized JSON that is ready for diff tooling.
puts staging&.normalized_json

For rules, the comprehensive chain uses the rule revision to determine point-in-time rule-component membership when Adobe includes that linkage. Component payloads are then resolved through the associated rule-component resources so the review object stays readable and diff-friendly.

Manual traversal

If you need lower-level control, you can still work directly with upstream_libraries and find_with_resources.

For each library you inspect with find_with_resources:

  • rules, data_elements, and extensions are typed resource arrays.
  • Each included resource gets a gem-added revision_id reader populated from relationships.latest_revision.data.id.
  • resource_index collapses those arrays into { resource_id => revision_id }.
  • A missing resource is represented by the key being absent from resource_index, not by a null placeholder object.

Resolve a resource upstream manually

# LB_DEV = development library ID.
# PR123 = property ID.
# RL123 = rule ID.
# Load the target library together with its included resources.
target_library = client.libraries.find_with_resources("LB_DEV")
# Fetch the ordered upstream library chain.
chain = client.libraries.upstream_libraries("LB_DEV", property_id: "PR123")

# Read the target library's revision ID for one rule.
target_revision_id = target_library.resource_index["RL123"]

# Walk upstream until the same rule ID appears in another library.
upstream_revision_id =
  chain.lazy
    # Expand each upstream library into a LibraryWithResources object.
    .map { |library| client.libraries.find_with_resources(library.id) }
    # Pull the revision ID for the same rule from each upstream library.
    .map { |library| library.resource_index["RL123"] }
    # Stop at the first non-nil revision ID.
    .find(&:itself)

# Resolve the upstream revision into a full revision object if one was found.
revision = client.revisions.find(upstream_revision_id) if upstream_revision_id

# Print the revision ID from the target library.
puts target_revision_id
# Print the nearest upstream revision ID.
puts upstream_revision_id
# Print the full point-in-time snapshot for the upstream revision.
puts revision&.entity_snapshot

That manual path is still valuable when you want custom traversal rules, but the new upstream_chain helper is the better default for most applications.

Was this page helpful?