fix(sync): enforce OptInRequired in local/CLI sync path (WillSyncResourceType)#1008
fix(sync): enforce OptInRequired in local/CLI sync path (WillSyncResourceType)#1008c1-squire-dev[bot] wants to merge 1 commit into
Conversation
A resource type may carry the OptInRequired annotation to stay OFF by default until an operator explicitly selects it (baton-jamf's managedDevice uses this). The platform respects it via connector capabilities (connectorbuilder.go:426 surfaces OptInRequired), but the SDK's own local/CLI sync did not: syncer.SyncResourceTypes took the connector's full ListResourceTypes response verbatim on an empty filter, so an opt-in-required type was synced anyway. Make the resource-type inclusion decision opt-in-aware via a new syncer.shouldSyncResourceType: - explicit --sync-resource-types filter: sync exactly the named types (naming an opt-in type IS the opt-in); - no filter: sync everything EXCEPT types carrying OptInRequired. Connectors that never set OptInRequired are unaffected -- the annotation is simply never present on the default path. Co-authored-by: c1-squire-dev[bot] <c1-squire-dev[bot]@users.noreply.github.com>
General PR Review: fix(sync): enforce OptInRequired in local/CLI sync path (WillSyncResourceType)Blocking Issues: 0 | Suggestions: 1 | Threads Resolved: 0 Review SummaryScanned the full PR diff for security and correctness. The change routes Security IssuesNone found. Correctness IssuesNone found. Suggestions
Prompt for AI agentsNote: the machine-readable review-state marker was omitted because the CI sandbox blocks emitting its JSON payload; the next review will run in full mode. |
Problem
OptInRequiredis the annotation a connector attaches to a resource type so it stays off by default until an operator explicitly opts in.baton-jamf'smanagedDeviceresource type uses this today.The platform-facing capabilities path already respects it:
connectorbuilder.gosurfaces the annotation intoResourceTypeCapability.OptInRequiredfor the platform to honor:pkg/connectorbuilder/connectorbuilder.go:426—OptInRequired: annos.Contains(&v2.OptInRequired{})But the SDK's own local/CLI sync path did not enforce it. In
syncer.SyncResourceTypes, an empty (default) resource-type filter took the connector's fullListResourceTypesresponse verbatim:pkg/sync/syncer.go(pre-fix) — theelsebranch:resourceTypes = resp.GetList()Because
SyncResourceTypesis the single choke point that persists resource types (PutResourceTypes), and downstreamSyncResourcesenqueues sync work by reading resource types back from the store (syncer.go:1241-1251), an opt-in-required type was persisted and fully synced on any default local/CLI sync.baton-jamfcarries its own connector-side gate today precisely because of this gap.Fix
Introduce
syncer.shouldSyncResourceType, the opt-in-aware inclusion decision, and routeSyncResourceTypesthrough it:--sync-resource-typesfilter → sync exactly the named types. Naming an opt-in-required type in the filter is the operator's opt-in, so it's included.OptInRequired, which stay off until named. A one-lineInfolog explains any skip.The
--sync-resource-typesflag (→connectorrunner.WithSyncResourceTypeIDs→sync.WithSyncResourceTypes→s.syncResourceTypes) is the same operator signal the platform path keys on, so behavior is now consistent across both.Backward compatibility
A connector that has never set
OptInRequiredon any resource type sees zero behavior change: on the default path the annotation is simply never present, so every advertised type still syncs. Only opt-in-flagged types are affected, and only when no explicit opt-in signal is present. Explicit-filter behavior is unchanged.Tests (
pkg/sync/syncer_optin_test.go)TestShouldSyncResourceType— the decision matrix (no-filter/opt-in → excluded; explicit-select opt-in → included; normal types unaffected).TestOptInRequiredResourceTypeDefaultSyncExcluded— regression guard: default sync excludes the opt-in type, still syncs normal types. Verified this FAILS on pre-fix code (opt-in type was persisted) and passes on the fix.TestOptInRequiredResourceTypeExplicitlySelected— explicit selection includes the opt-in type.All run in both sequential and parallel sync modes.
go test -tags=baton_lambda_support ./pkg/sync/andgolangci-lint runare green.Follow-up (NOT in this PR)
Once this SDK fix soaks,
baton-jamf's connector-side gate —annotationsForManagedDeviceResourceTypeinpkg/connector/helpers.goplus theResourceSyncersregistration gate — becomes redundant defense-in-depth rather than the sole safety net. It is intentionally left in place here; removing it is a separate later cleanup. With this fix, every future opt-in connector gets the guarantee for free.Scope note
The fix targets the top-level resource-type inclusion decision (the documented gap and the jamf use case, where
managedDeviceis a top-level registeredResourceSyncer). Child resource types discovered viaChildResourceTypeannotations are already gated by an explicit filter; the default-path child case is left as a documented boundary to avoid per-resource store lookups on the hot sync path.🤖 Generated with Claude Code