Skip to content

[Feat] Deploy DataSpace-Scoped Components with their retrieveWith Closure - #1649

Open
aparnagoy wants to merge 2 commits into
salesforcecli:mainfrom
aparnagoy:d360-cdp-dataspace-deploy-closure
Open

aparnagoy wants to merge 2 commits into
salesforcecli:mainfrom
aparnagoy:d360-cdp-dataspace-deploy-closure

Conversation

@aparnagoy

@aparnagoy aparnagoy commented Sep 21, 2026

Copy link
Copy Markdown

What this does

When you deploy a Data Cloud dataspace-scoped component, it usually can't be deployed on its
own — it has peer components that must go up in the same deploy for the server to accept it. This
change makes the plugin pull those peers in automatically, so a deploy of one dataspace-scoped
component also deploys everything it needs, and nothing it doesn't.

It's generic: it works for any dataspace-scoped type, present or future. There is no per-type
logic here — the behavior is driven entirely by the component metadata, so new types are picked up
without touching this code.

When the call reaches this repo

This plugin is the entry point for sf project retrieve start and sf project deploy start. Here
is where each command lands and what this repo does with it before/after handing off to SDR
(@salesforce/source-deploy-retrieve), which does the actual resolve/convert/transfer.

sf project retrieve start

sf project retrieve start --metadata ‹Type›:‹dataspace›.‹name›
└─ src/commands/project/retrieve/start.ts  (command)
   └─ ComponentSetBuilder.build(requested)          # SDR
   └─ componentSet.retrieve(org)  → MetadataApiRetrieve   # SDR
# SDR resolves + converts the d360/… two-file layout; this repo just writes the
# FileResponses SDR returns. There is NO d360-specific branch in the plugin on retrieve.

Retrieve needs no dataspace-scoped code in this repo. SDR's D360SourceAdapter /
D360MetadataTransformer produce the d360/dataspace/… payload + .meta.json sidecar; the plugin
only writes what SDR hands back.

sf project deploy start

sf project deploy start --source-dir …
└─ src/commands/project/deploy/start.ts  (command)
   └─ executeDeploy(opts)                           # src/utils/deploy.ts  ← the only d360-aware code
      └─ buildComponentSet(opts, stl)               # requested set
      └─ if hasD360Components(set):                  # cheap gate — is any member strategy 'd360'?
            set = expandD360Closure(set, registry)
               ├─ ComponentSetBuilder.build(whole project)   # resolve peers by sidecar componentName
               └─ expandD360ComponentSet(full, requested)    # SDR: walk retrieveWith closure
      └─ componentSet.deploy(org)  → MetadataApiDeploy        # SDR

Deploy is where this repo does its one dataspace-scoped thing: expand the requested set with its
retrieveWith closure before the deploy. Everything downstream (convert → package → transfer) is
unchanged SDR.

How a component says what it needs

Every dataspace-scoped component ships with a sidecar that lists its deploy peers under
retrieveWith. That list is the deploy unit: the component plus its retrieveWith peers (and
their peers, transitively) travel together. We follow retrieveWith and only retrieveWith — a
separate dependsOn list also exists but is informational and deliberately not used to decide what
gets deployed.

Design

The expansion is off the hot path and only runs when it's actually needed:

Piece Role
hasD360Components(set) Cheap gate — returns true only if the deploy contains a dataspace-scoped component (strategy d360). Every ordinary deploy short-circuits here and is completely unaffected.
expandD360Closure(requested) Resolves the whole project once, then returns the requested set expanded with its transitive retrieveWith closure. Only called when the gate passes (it is not a no-op — it resolves the full project).
SDR expandD360ComponentSet Does the actual walk: index candidates by their sidecar componentName, then follow each component's retrieveWith transitively, adding only the referenced dataspace-scoped peers.

Where it hooks in

Inside executeDeploy, on the source-format path only (mdapi-directory/zip deploys are untouched),
right after the component set is built:

buildComponentSet(opts, stl)
  └─ if hasD360Components(set):
        set = expandD360Closure(set, registry)
           └─ ComponentSetBuilder.build(whole project)          # resolve candidates
           └─ expandD360ComponentSet(full, requested)           # walk retrieveWith closure

The result flows into the normal deploy exactly as before — the closure just changes which
components are in the set, not how they deploy.

Notes

  • Zero impact on non-Data-Cloud deploys: the gate is the first thing checked and returns false for
    every ordinary component set.
  • The closure is minimal by construction: it adds the requested components' retrieveWith peers and
    nothing else from the project.
  • Requires the companion source-deploy-retrieve change (the d360 adapter/transformer strategy
    and the expandD360ComponentSet export) to be present.

@salesforce-cla

Copy link
Copy Markdown

Thanks for the contribution! Unfortunately we can't verify the commit author(s): Aparna Goyal <r***@A***.com>. One possible solution is to add that email to your GitHub account. Alternatively you can change your commits to another email and force push the change. After getting your commits associated with your GitHub account, sign the Salesforce Inc. Contributor License Agreement and this Pull Request will be revalidated.

@aparnagoy aparnagoy changed the title feat: deploy dataspace-scoped components with their retrieveWith closure [Feat] Deploy DataSpace-Scoped Components with their retrieveWith Closure Sep 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant