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
| Attribute | Data type | Description |
|---|---|---|
| upstream_library(library_id) | Library | Returns only the immediate upstream library from Adobe's |
| 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 |
| revisions.find(revision_id) | Revision | Resolves a revision ID into a point-in-time resource snapshot through |
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:
GET /libraries/:library_idis called first so missing target libraries fail immediately.GET /libraries/:library_id/relationships/environmentfetches the target environment ID.GET /environments/:environment_idreads the target environmentstage.- If the target stage is
production, the helper returns[]. GET /properties/:property_id/librariesloads the property's library list.- For each candidate library, the helper resolves that library's environment relationship and environment stage.
- The helper returns only the libraries in stages above the target, ordered nearest first.
The stage order used by the helper is:
developmentstagingproduction
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
Libraryresource, not a custom chain node. - The returned
Libraryobjects do not get a derivedstageattribute attached. - If you need the actual stage for a chain node, call
client.libraries.environment(library.id)and read the environment'sstage. - 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"]
Recommended lookup
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_resourcetarget_revision_identriesnearest_matchfound?
Each upstream entry includes:
librarystageresourcerevision_idrevisionentity_snapshot
The comprehensive helpers return the same ordered chain shape, but each entry also includes:
comprehensive_resourcenormalized_payloadnormalized_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, andextensionsare typed resource arrays.- Each included resource gets a gem-added
revision_idreader populated fromrelationships.latest_revision.data.id. resource_indexcollapses 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.