Skip to content

[BB-1788] Add containerization support implementing ResourceSyncerV2 and SessionStore cache#29

Merged
johnallers merged 6 commits intomainfrom
luisinasantos/add-containerization-support
Dec 12, 2025
Merged

[BB-1788] Add containerization support implementing ResourceSyncerV2 and SessionStore cache#29
johnallers merged 6 commits intomainfrom
luisinasantos/add-containerization-support

Conversation

@luisina-santos
Copy link
Contributor

@luisina-santos luisina-santos commented Nov 20, 2025

Summary by CodeRabbit

  • Refactor

    • Added a generated configuration model and a new exported configuration entry for cleaner settings.
    • Standardized sync APIs across resources to a unified request/response shape.
    • Migrated builders to the new sync contract and introduced a new connector factory entry point.
    • Replaced in-memory caches with session-backed caches for collaborator and role data.
  • Chores

    • Moved a direct module dependency to an indirect requirement.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Nov 20, 2025

Walkthrough

Adds a generated Workato config type with reflection-backed accessors; renames package conf→config and exposes a field-driven Config; migrates builder APIs from pagination tokens/annotations to rs.SyncOpAttrs/SyncOpResults; and replaces in-memory caches with session-backed read/write helpers.

Changes

Cohort / File(s) Summary
Module deps / build
go.mod
Marks github.com/spf13/viper v1.19.0 as indirect (removed from direct require, added as indirect).
Generated config type & accessors
pkg/config/conf.gen.go
Adds generated Workato struct with mapstructure tags and reflection-based accessors: findFieldByTag, GetString, GetStringSlice, GetInt, GetBool, GetStringMap (panics on type mismatch).
Configuration wiring & tests
pkg/config/config.go, pkg/config/config_test.go
Renames package confconfig; removes ValidateConfig; adds exported Config = field.NewConfiguration(...) with go:generate; tests adapted to use a local validateConfig helper and import viper.
Connector factory & ctor
pkg/connector/connector.go
Adds public New(ctx, config, _ *cli.ConnectorOpts) returning connectorbuilder.ConnectorBuilderV2 and keeps NewConnector as the internal ctor; updates ResourceSyncers to return ResourceSyncerV2; adds validation, logging, env resolution, and client construction.
Builder API / pagination migration
pkg/connector/collaborator.go, pkg/connector/folder.go, pkg/connector/privilege.go, pkg/connector/project.go, pkg/connector/role.go
Unified API change: List, Entitlements, Grants now accept rs.SyncOpAttrs and return *rs.SyncOpResults (NextPageToken) instead of pagination tokens and annotations; updated signatures, error paths, and paging handling.
Collaborator builder & cache integration
pkg/connector/collaborator.go
Adds cache *collaboratorCache field; constructor now accepts env workato.Environment; methods updated to SyncOpAttrs/SyncOpResults; populates session cache via setCollaboratorsCache.
Session-based collaborator cache
pkg/connector/collaborator_cache.go
Replaces mutex-backed in-memory maps with session storage-backed caches; adds helpers setCollaboratorsCache, getUsersByPrivilege, getUsersByFolder, getUsersByRole, appendCachedValue[T], and cache-prefix constants; ensures initialization marker and bulk session writes/reads.
Session-based role cache
pkg/connector/role_cache.go
Removes in-memory roleCache; adds session-backed setRolesCache, getRoleById, getRoleByFolder, parseJSONRolesCache, and cache-prefix constants; writes/reads session storage and returns nil-on-miss.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Connector
    participant Builder as Resource Builder
    participant Session as Session Cache
    participant API as Workato API

    rect rgba(220, 235, 255, 0.35)
    Note over Client,Connector: Old flow (pagination + in-memory cache)
    Client->>Connector: ResourceSyncers()
    Connector->>Builder: List(parentID, paginationToken)
    Builder->>API: Fetch resources (paged)
    API-->>Builder: resources, nextToken
    Builder->>Builder: populate in-memory cache (mutex)
    Builder-->>Connector: resources, nextToken, annotations
    end

    rect rgba(220, 255, 230, 0.35)
    Note over Client,Connector: New flow (SyncOpAttrs + session cache)
    Client->>Connector: ResourceSyncers()
    Connector->>Builder: List(_, SyncOpAttrs{Session, PageToken})
    Builder->>Session: getUsers/getRoles... (read)
    Session-->>Builder: cached entries or nil
    alt cache miss
      Builder->>API: Fetch resources
      API-->>Builder: resources
      Builder->>Session: setRolesCache / setCollaboratorsCache (write)
      Session-->>Builder: stored
    end
    Builder-->>Connector: resources, SyncOpResults{NextPageToken}
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45–75 minutes

  • Inspect reflection and panic behavior in pkg/config/conf.gen.go.
  • Verify field.NewConfiguration usage, go:generate, and adapted tests in pkg/config.
  • Ensure all builder signature changes consistently use rs.SyncOpAttrs and construct rs.SyncOpResults.
  • Review session storage key prefixes, JSON encoding/decoding, atomicity, and error handling in pkg/connector/collaborator_cache.go and pkg/connector/role_cache.go.
  • Confirm connector factory New wiring, logging, and distinction from NewConnector.

Poem

🐇
I hopped through code and swapped the tokens,
Sessions hummed soft underneath my potions,
Generated fields tucked in fur so neat,
Builders bound to attrs — a rhythmic beat,
Rabbit cheers: "Sync on, swift little devs!"

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main objectives of the changeset, specifically referencing ResourceSyncerV2 implementation and SessionStore cache changes across multiple connector files.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch luisinasantos/add-containerization-support

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
pkg/connector/collaborator_cache.go (1)

93-151: Fix CompoundUser.User pointer aliasing in setCollaboratorsCache

CompoundUser is built with User: &collaborator, where collaborator is the range variable. This means every cached CompoundUser ends up pointing to the same collaborator (the last one in the slice).

Use a per-iteration copy before taking the address:

 func (c *collaboratorCache) setCollaboratorsCache(ctx context.Context, sessionStorage sessions.SessionStore, collaborators []client.Collaborator) error {
@@
-	for _, collaborator := range collaborators {
+	for _, collaborator := range collaborators {
 		collaboratorRoles, err := c.client.GetCollaboratorPrivileges(ctx, collaborator.Id)
@@
-		compoundUser := &CompoundUser{
-			User:       &collaborator,
+		collaboratorCopy := collaborator
+		compoundUser := &CompoundUser{
+			User:       &collaboratorCopy,
 			UserDetail: collaboratorRoles,
 		}

This preserves the rest of the logic while ensuring each CompoundUser references the correct collaborator.

pkg/connector/privilege.go (1)

50-61: Fix entitlement display-name typo and update outdated comment

  • Line 50: The doc comment still mentions “users” and an empty slice, but this builder now returns a real entitlement for privileges. Consider updating the comment to reflect the current behavior.
  • Line 56: The display name format string has a trailing backtick, which will show up in UI and looks like a typo.

You can minimally fix both with something like:

-// Entitlements always returns an empty slice for users.
+// Entitlements returns the assignment entitlement for a privilege.

-    entitlement.WithDisplayName(fmt.Sprintf("%s have %s`", collaboratorResourceType.DisplayName, resource.DisplayName)),
+    entitlement.WithDisplayName(fmt.Sprintf("%s have %s", collaboratorResourceType.DisplayName, resource.DisplayName)),
pkg/connector/folder.go (1)

95-107: Fix entitlement description/display-name typos and comment wording

  • Line 95: The comment still claims this returns an empty slice “for users”, but this builder now emits a folder access entitlement. Updating the wording would avoid confusion.
  • Lines 101–102: Both strings use “acess” instead of “access”, which will surface in UI.

A minimal fix:

-// Entitlements always returns an empty slice for users.
+// Entitlements returns the collaborator-access entitlement for a folder.

-    entitlement.WithDescription(fmt.Sprintf("%s can acess %s", collaboratorResourceType.DisplayName, resource.DisplayName)),
-    entitlement.WithDisplayName(fmt.Sprintf("%s acess %s", collaboratorResourceType.DisplayName, resource.DisplayName)),
+    entitlement.WithDescription(fmt.Sprintf("%s can access %s", collaboratorResourceType.DisplayName, resource.DisplayName)),
+    entitlement.WithDisplayName(fmt.Sprintf("%s access %s", collaboratorResourceType.DisplayName, resource.DisplayName)),

(or tweak the grammar to whatever product copy you prefer).

🧹 Nitpick comments (10)
pkg/connector/role_cache.go (1)

15-18: Reuse cache prefix constants in setRolesCache

setRolesCache hard-codes "roles" and "folder_roles" instead of using rolesCachePrefix and folderRolesCachePrefix, which is easy to desync later.

Consider this small cleanup:

 const (
-	rolesCachePrefix       = "roles"
-	folderRolesCachePrefix = "folder_roles"
+	rolesCachePrefix       = "roles"
+	folderRolesCachePrefix = "folder_roles"
 )
@@
 func setRolesCache(ctx context.Context, sessionStorage sessions.SessionStore, roles []client.Role) error {
-	err := session.SetManyJSON(ctx, sessionStorage, parseJSONRolesCache(roles), sessions.WithPrefix("roles"))
+	err := session.SetManyJSON(ctx, sessionStorage, parseJSONRolesCache(roles), sessions.WithPrefix(rolesCachePrefix))
@@
-			folderRoles, found, err := session.GetJSON[[]*client.Role](ctx, sessionStorage, folderIDStr, sessions.WithPrefix("folder_roles"))
+			folderRoles, found, err := session.GetJSON[[]*client.Role](ctx, sessionStorage, folderIDStr, sessions.WithPrefix(folderRolesCachePrefix))
@@
-			err = session.SetJSON(ctx, sessionStorage, folderIDStr, folderRoles, sessions.WithPrefix("folder_roles"))
+			err = session.SetJSON(ctx, sessionStorage, folderIDStr, folderRoles, sessions.WithPrefix(folderRolesCachePrefix))

Also applies to: 52-76

pkg/connector/collaborator_cache.go (1)

154-172: Clarify generic cache helper error message

appendCachedValue is used for privilege, folder, and role caches, but the error message is hard-coded as “failed to set folder roles…”, which can be misleading when debugging.

Consider generalizing the error text:

 func appendCachedValue[T any](ctx context.Context, sessionStorage sessions.SessionStore, cachePrefix string, cacheKey string, cacheValue T) error {
@@
-	err = session.SetJSON(ctx, sessionStorage, cacheKey, values, sessions.WithPrefix(cachePrefix))
-	if err != nil {
-		return fmt.Errorf("failed to set folder roles in session storage: %w", err)
-	}
+	err = session.SetJSON(ctx, sessionStorage, cacheKey, values, sessions.WithPrefix(cachePrefix))
+	if err != nil {
+		return fmt.Errorf("failed to set cached values in session storage (prefix=%s, key=%s): %w", cachePrefix, cacheKey, err)
+	}
pkg/config/conf.gen.go (2)

13-25: Defensive check for nil receiver in findFieldByTag

reflect.ValueOf(c).Elem() will panic if c is nil. Even though this is generated code and callers should always have a non-nil *Workato, a quick nil guard (or a clearer panic message including tagValue) would make failures easier to debug and slightly safer if misused in the future.


28-88: Generated accessors use panic and may trigger linters

All the getters (GetStringSlice, GetString, GetInt, GetBool, GetStringMap) panic("wrong type") on mismatches. That’s reasonable for generated config access, but:

  • The panic message is very opaque.
  • golangci-lint may complain about panics or other patterns in generated code (as hinted by the pipeline failure).

Consider either:

  • Enriching panic messages with field/tag and concrete/expected types, or
  • Tweaking golangci-lint config to ignore *.gen.go files if you don’t want to change generated output.
pkg/connector/project.go (1)

21-45: List implementation aligns with SyncOpAttrs/SyncOpResults; update comment

The pagination wiring via attr.PageToken.Token and returning &rs.SyncOpResults{NextPageToken: nextToken} looks consistent with the new V2 pattern, and error paths returning (nil, nil, err) are correct.

Minor: the comment still refers to “users” even though this is a project builder; consider updating it to avoid confusion.

pkg/connector/role.go (1)

95-199: Grants logic and cache usage look good; clarify behavior on missing custom role

The Grants implementation:

  • Uses getUsersByRole(ctx, attr.Session, resource.DisplayName) to derive collaborator grants.
  • Handles base roles by expanding to privilege grants with the expected annotations.
  • For custom roles, uses getRoleById(ctx, attr.Session, resource.Id.Resource) and workato.FindRelatedPrivilegesErr to derive privilege grants.

This is a solid migration to session-backed caches and SyncOpAttrs/Results. One nuance: when getRoleById returns nil, you log a warning and return rv, nil, NotFound error. Since rv is initially empty, that effectively yields “no grants + error”, which is reasonable but slightly asymmetric with other error paths that return (nil, nil, err).

If you want stricter consistency, consider returning nil, nil, err here as well; otherwise, the current behavior is acceptable as long as callers are aware that rv may be non-nil even when an error is returned.

pkg/connector/collaborator.go (1)

68-89: Duplicate WithUserLogin call in collaboratorResource

collaboratorResource calls resource.WithUserLogin(collaborator.Email) twice in the traits slice. This is harmless but redundant and slightly noisy.

You can drop one of them:

-		resource.WithUserLogin(collaborator.Email),
-		resource.WithUserLogin(collaborator.Email),
+		resource.WithUserLogin(collaborator.Email),
pkg/connector/privilege.go (1)

93-97: Consider dropping the unused client from privilegeBuilder

newPrivilegeBuilder stores a *client.WorkatoClient on privilegeBuilder, but that field is not used anywhere in this file (listing, entitlements, and grants all work off static data and session helpers). If it’s not used elsewhere, consider removing the field and constructor parameter to avoid confusion about unused dependencies.

pkg/connector/folder.go (2)

110-198: Bag-based Grants pagination is valid but can be simplified

The cpagination-based Grants flow is structurally sound—GenBagFromToken is used to reconstruct state, bag.Current() seeds initial work with collaborator/role entries, and NextPageToken is driven by bag.Marshal(), matching the generic bag helper pattern (see pkg/connector/cpagination/generic_bag.go). Based on relevant snippets.

A couple of cleanups you might consider:

  • The first call where bag.Current() == nil returns no grants and only a token. You could instead initialize the bag when empty and immediately pop/process the first state, avoiding an extra “empty” round trip.
  • The Bag.Page field is currently unused; unless you plan to add per-type pagination soon, dropping it would reduce noise.
  • When o.disableCustomRolesSync is true, you still push a role state on initialization that will never produce grants; you could conditionally omit that push to avoid an extra iteration.

These are all behavior-preserving refactors and can be deferred if you prefer.


200-205: folderBuilder.cache field is unused and not initialized

folderBuilder still declares a cache *collaboratorCache field, but newFolderBuilder only sets client and disableCustomRolesSync, and this file never references o.cache. If there are no external uses, consider removing the field (and any dead code around collaboratorCache) to make it clear that folder grants are purely session-based now.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7372f03 and 84c4c82.

⛔ Files ignored due to path filters (3)
  • Makefile is excluded by none and included by none
  • cmd/baton-workato/main.go is excluded by none and included by none
  • pkg/config/gen/gen.go is excluded by !**/gen/** and included by pkg/**
📒 Files selected for processing (12)
  • go.mod (1 hunks)
  • pkg/config/conf.gen.go (1 hunks)
  • pkg/config/config.go (2 hunks)
  • pkg/config/config_test.go (1 hunks)
  • pkg/connector/collaborator.go (2 hunks)
  • pkg/connector/collaborator_cache.go (4 hunks)
  • pkg/connector/connector.go (3 hunks)
  • pkg/connector/folder.go (5 hunks)
  • pkg/connector/privilege.go (4 hunks)
  • pkg/connector/project.go (1 hunks)
  • pkg/connector/role.go (7 hunks)
  • pkg/connector/role_cache.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (7)
pkg/connector/role_cache.go (1)
pkg/connector/client/models.go (1)
  • Role (65-73)
pkg/connector/collaborator_cache.go (3)
pkg/connector/client/client.go (1)
  • WorkatoClient (28-33)
pkg/connector/workato/env.go (1)
  • Environment (10-10)
pkg/connector/client/models.go (1)
  • Collaborator (35-49)
pkg/config/conf.gen.go (1)
pkg/config/config.go (2)
  • WorkatoEnv (23-28)
  • DisableCustomRolesSync (30-35)
pkg/connector/role.go (1)
pkg/connector/workato/privileges.go (2)
  • Privileges (31-430)
  • FindRelatedPrivilegesErr (448-480)
pkg/connector/connector.go (5)
pkg/connector/client/client.go (2)
  • WorkatoClient (28-33)
  • NewWorkatoClient (35-61)
pkg/connector/workato/env.go (2)
  • Environment (10-10)
  • EnvFromString (22-33)
pkg/config/conf.gen.go (1)
  • Workato (6-11)
pkg/config/config.go (3)
  • Config (55-61)
  • WorkatoEnv (23-28)
  • DisableCustomRolesSync (30-35)
pkg/connector/client/helpers.go (1)
  • WorkatoDataCenters (18-24)
pkg/connector/collaborator.go (2)
pkg/connector/client/client.go (1)
  • WorkatoClient (28-33)
pkg/connector/workato/env.go (1)
  • Environment (10-10)
pkg/connector/folder.go (1)
pkg/connector/cpagination/generic_bag.go (1)
  • GenBagFromToken (23-30)
🪛 GitHub Actions: ci
pkg/config/config_test.go

[error] 20-20: undefined: ValidateConfig (typecheck) encountered during tests.

pkg/config/conf.gen.go

[error] 1-1: golangci-lint config issue reported for generated file: '# github.com/conductorone/baton-workato/pkg/config [github.com/conductorone/baton-workato/pkg/config.test]'.

🔇 Additional comments (12)
go.mod (1)

86-86: Viper moved to indirect looks fine

Marking github.com/spf13/viper v1.19.0 as indirect is consistent with removing direct config usage; no issues from this change alone.

pkg/config/config_test.go (1)

1-21: Fix config tests: ValidateConfig is gone, causing CI failure

config.go removed ValidateConfig, but this test still references it, leading to the reported undefined: ValidateConfig typecheck failure.

Assuming you no longer have custom validation logic beyond the schema, the fix is:

  1. Reuse the exported Config instead of rebuilding it
  2. Pass nil as the validator to test.ExerciseTestCases (which accepts a function or nil)
 func TestConfigs(t *testing.T) {
-	configurationSchema := field.NewConfiguration(
-		ConfigurationFields,
-		field.WithConstraints(FieldRelationships...),
-	)
+	configurationSchema := Config
 
 	testCases := []test.TestCase{
 		// Add test cases here.
 	}
 
-	test.ExerciseTestCases(t, configurationSchema, ValidateConfig, testCases)
+	test.ExerciseTestCases(t, configurationSchema, nil, testCases)
 }

Verify this matches your pinned github.com/conductorone/baton-sdk v0.5.21 test API documentation.

pkg/connector/project.go (1)

47-55: No-op Entitlements/Grants for projects are fine

Returning (nil, nil, nil) for both Entitlements and Grants makes it explicit that projects have no entitlements/grants. This is consistent with the previous behavior and the new V2 signatures.

pkg/connector/connector.go (1)

27-35: ResourceSyncers V2 wiring looks consistent

The updated ResourceSyncers returning []connectorbuilder.ResourceSyncerV2 and using newCollaboratorBuilder(d.client, d.env), newPrivilegeBuilder(d.client), newRoleBuilder(d.client, d.env, d.disableCustomRolesSync), newFolderBuilder(d.client, d.disableCustomRolesSync), and newProjectBuilder(d.client) is consistent with the new builder signatures and the broader V2 refactor.

pkg/connector/role.go (2)

38-79: Role listing + session cache integration looks correct

Using attr.PageToken.Token with GetRoles, caching roles into the session via setRolesCache(ctx, attr.Session, roles), and returning &rs.SyncOpResults{NextPageToken: nextToken} is consistent with the new SyncOpAttrs/Results pattern and the role cache design. Error paths correctly return (nil, nil, err).

No functional issues spotted here; just ensure callers always supply a non-nil attr.Session when custom role sync is enabled so caching works as intended.


82-92: Entitlements signature migration is clean

The updated Entitlements method correctly adopts rs.SyncOpAttrs and returns (rv, nil, nil). The entitlement construction itself is unchanged aside from the signature shape, so behavior should be preserved.

pkg/connector/collaborator.go (3)

26-49: List migration + collaborator cache integration look good

The List method now:

  • Uses resource.SyncOpAttrs and returns ([]*v2.Resource, *resource.SyncOpResults, error).
  • Populates the collaborator cache via o.cache.setCollaboratorsCache(ctx, attr.Session, collaborators).
  • Returns (rv, nil, nil) on success and (nil, nil, err) on failure.

This is consistent with the new SyncOp pattern and the session-based cache design. Just ensure collaboratorBuilder instances are always constructed via newCollaboratorBuilder so o.cache is never nil.


52-59: No-op Entitlements/Grants for collaborators are explicit and fine

Having collaborators expose no entitlements or grants and returning (nil, nil, nil) matches the prior semantics and aligns with the V2 signatures.


61-65: All call sites verified; constructor changes are correct

The search confirms only one external call site in ./pkg/connector/connector.go:30, which correctly uses the new signature newCollaboratorBuilder(d.client, d.env). No remaining calls to an old signature exist.

pkg/connector/privilege.go (2)

31-47: Privilege List implementation aligns with new SyncOp API

Static privilege enumeration via workato.AllCompoundPrivileges() with SyncOpResults set to nil (no pagination) is consistent and error handling is correct. No changes needed here.


64-91: Grants use session-based privilege lookup correctly

The Grants method’s use of getUsersByPrivilege(ctx, attr.Session, privilegeId) and construction of immutable collaborator grants looks consistent with the session-backed cache pattern and the new SyncOpAttrs/SyncOpResults shapes. Error propagation is straightforward and there’s no pagination state to maintain here, which is fine if the privilege→user fanout is expected to be small/medium.

pkg/connector/folder.go (1)

36-93: Folder List pagination and parent-type handling look correct

The List implementation branches correctly on parentResourceID.ResourceType, passes attr.PageToken.Token through to the client, and wraps the client’s nextToken in SyncOpResults. Early returns for nil parent and unknown types (with a warning log) are consistent with a “no children” semantics. No changes needed here.

@luisina-santos luisina-santos force-pushed the luisinasantos/add-containerization-support branch from 4351b56 to 0d666e0 Compare November 25, 2025 20:51
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4351b56 and 0d666e0.

📒 Files selected for processing (4)
  • pkg/config/config_test.go (2 hunks)
  • pkg/connector/collaborator.go (2 hunks)
  • pkg/connector/folder.go (5 hunks)
  • pkg/connector/role.go (7 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/config/config_test.go
🧰 Additional context used
🧬 Code graph analysis (2)
pkg/connector/collaborator.go (2)
pkg/connector/client/client.go (1)
  • WorkatoClient (28-33)
pkg/connector/workato/env.go (1)
  • Environment (10-10)
pkg/connector/role.go (1)
pkg/connector/workato/privileges.go (2)
  • Privileges (31-430)
  • FindRelatedPrivilegesErr (448-480)
🔇 Additional comments (10)
pkg/connector/folder.go (4)

35-92: LGTM! Migration to SyncOpAttrs/SyncOpResults is consistent.

The List method has been successfully migrated to use SyncOpAttrs for input and SyncOpResults for output. The pagination token is correctly accessed via attr.PageToken.Token, and the next page token is properly returned in SyncOpResults.NextPageToken.


199-204: I'll help you verify that all call sites to newFolderBuilder have been updated to match the new signature. Let me search the codebase for all occurrences of this function call.
<function_calls>


#!/bin/bash

Search for all calls to newFolderBuilder to verify signature changes

rg -nP 'newFolderBuilder(' --type=go -C3

</function_calls>
<function_calls>


#!/bin/bash

Also search for newFolderBuilder in comments or other contexts

rg -nP 'newFolderBuilder' --type=go -C2

</function_calls>


169-169: I'll help you verify the review comment about the getRoleByFolder function. Let me start by searching for its definition and analyzing its error handling.
<function_calls>

#!/bin/bash

Search for getRoleByFolder function definition

ast-grep --pattern $'func getRoleByFolder($$$) {
$$$
}'


</function_calls>

Now let me also search for the function with rg to get more context:
<function_calls>

#!/bin/bash

Search for getRoleByFolder definition and usages with context

rg -n "getRoleByFolder" -A 15 -B 2


</function_calls>


147-147: Verify that getUsersByFolder helper exists and handles session errors.

The function is properly defined at pkg/connector/collaborator_cache.go:61 and implements error handling:

  • Catches errors from session.GetJSON and logs them (line 66), returning nil on error
  • Handles cache misses by checking the found flag (line 70), returning nil when empty
  • The function correctly accepts sessions.SessionStore from attr.Session and delegates session operations to the SDK's session.GetJSON, which implicitly handles nil or invalid session storage scenarios
pkg/connector/collaborator.go (3)

26-52: LGTM! Cache population integrated correctly.

The List method has been successfully migrated to use SyncOpAttrs and SyncOpResults. The cache population via setCollaboratorsCache(ctx, attr.Session, collaborators) is correctly placed after fetching collaborators and before building the resource list. Error handling is consistent throughout.


35-39: The implementation properly handles session persistence with appropriate error handling.

The setCollaboratorsCache method (pkg/connector/collaborator_cache.go:93) correctly persists collaborators to the session through the appendCachedValue helper function. This helper:

  • Retrieves existing cached values using session.GetJSON()
  • Appends new collaborators to the cache
  • Persists the updated data using session.SetJSON()
  • Returns wrapped errors if serialization or storage operations fail

Error handling covers both retrieval failures ("failed to get cached value from session storage") and persistence failures ("failed to set [privilege/folder/role] to user cache in session storage"), ensuring any serialization or storage errors are properly caught and propagated.


64-69: Perfect! I've completed the verification. Let me trace the complete env parameter propagation flow:

Verification Results:

  1. Configuration to Initialization: In the New() function (line 77), env is derived from config.WorkatoEnv via workato.EnvFromString()
  2. Connector Creation: The env is passed to NewConnector() (line 86)
  3. Struct Assignment: In NewConnector() (line 62), the env parameter is correctly assigned to the Connector struct field
  4. Usage in ResourceSyncers: In ResourceSyncers() (line 30), d.env is correctly passed to newCollaboratorBuilder()
  5. Cache Initialization: In newCollaboratorBuilder() (line 67), the env is passed to newCollaboratorCache()

Summary: The env parameter is correctly propagated from connector initialization through all call sites. There is only one call site to newCollaboratorBuilder(), and it properly passes the environment value from the Connector instance.


The env parameter is correctly propagated through the connector initialization flow. The environment value originates from configuration validation, is properly stored in the Connector struct, and is correctly passed to newCollaboratorBuilder in the only call site.

pkg/connector/role.go (3)

38-82: LGTM! Role caching integrated correctly.

The List method has been successfully migrated to use SyncOpAttrs and SyncOpResults. The cache population via setRolesCache(ctx, attr.Session, roles) is correctly placed after fetching roles from the API. The method properly handles both custom roles and base roles.


102-102: getUsersByRole helper function exists and properly handles session retrieval and error cases.

The function is defined in pkg/connector/collaborator_cache.go (lines 77-91) and includes comprehensive error handling:

  • Errors from session storage retrieval are caught and logged (lines 81-83), returning nil
  • When the cache entry is not found, the function returns nil (lines 86-87)
  • The function returns the retrieved users when successfully found (line 90)

The implementation safely handles both nil sessions and empty cache scenarios by delegating session management to the session.GetJSON() helper and returning nil appropriately in all error paths.


54-58: setRolesCache properly handles session persistence with comprehensive error handling.

Verified: The function uses session.SetManyJSON() to persist roles to session storage with a "roles" prefix (line 53, pkg/connector/role_cache.go), wraps storage errors with descriptive context using fmt.Errorf(), and also creates folder-level indices for efficient lookups. All session operations include proper error handling and are passed to the underlying sessionStorage parameter, ensuring session state is correctly persisted.


// Entitlements always returns an empty slice for users.
func (o *folderBuilder) Entitlements(_ context.Context, resource *v2.Resource, _ *pagination.Token) ([]*v2.Entitlement, string, annotations.Annotations, error) {
func (o *folderBuilder) Entitlements(_ context.Context, resource *v2.Resource, _ rs.SyncOpAttrs) ([]*v2.Entitlement, *rs.SyncOpResults, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be a static entitlements now

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we are experiencing issues with skipping entitlements and use StaticEntitlements, so for now I'll keep this as regular entitlements


// Entitlements always returns an empty slice for users.
func (o *roleBuilder) Entitlements(_ context.Context, resource *v2.Resource, _ *pagination.Token) ([]*v2.Entitlement, string, annotations.Annotations, error) {
func (o *roleBuilder) Entitlements(_ context.Context, resource *v2.Resource, _ rs.SyncOpAttrs) ([]*v2.Entitlement, *rs.SyncOpResults, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be static entitlements

@luisina-santos luisina-santos requested a review from a team December 10, 2025 16:27
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/connector/collaborator_cache.go (1)

93-171: Fix pointer aliasing in setCollaboratorsCache (all CompoundUser.User point to last collaborator)

Inside the for _, collaborator := range collaborators loop, CompoundUser.User is set to &collaborator, which is the address of the loop variable. In Go, this means every cached CompoundUser ends up pointing at the same memory location and will all reference the last collaborator processed.

Adjust the loop to take the address of the slice element instead of the loop variable so each CompoundUser points to the correct collaborator.

-func (c *collaboratorCache) setCollaboratorsCache(ctx context.Context, sessionStorage sessions.SessionStore, collaborators []client.Collaborator) error {
+func (c *collaboratorCache) setCollaboratorsCache(ctx context.Context, sessionStorage sessions.SessionStore, collaborators []client.Collaborator) error {
 	l := ctxzap.Extract(ctx)
@@
-	for _, collaborator := range collaborators {
-		collaboratorRoles, err := c.client.GetCollaboratorPrivileges(ctx, collaborator.Id)
+	for i := range collaborators {
+		collaborator := &collaborators[i]
+
+		collaboratorRoles, err := c.client.GetCollaboratorPrivileges(ctx, collaborator.Id)
@@
-		compoundUser := &CompoundUser{
-			User:       &collaborator,
-			UserDetail: collaboratorRoles,
-		}
+		compoundUser := &CompoundUser{
+			User:       collaborator,
+			UserDetail: collaboratorRoles,
+		}
@@
-		// Build for roles
-		for _, role := range collaborator.Roles {
+		// Build for roles
+		for _, role := range collaborator.Roles {
 			if role.EnvironmentType != c.env.String() {
 				continue
 			}
 			appendCachedValue(roleToUsers, role.RoleName, compoundUser)
 		}
 	}
♻️ Duplicate comments (2)
pkg/connector/role.go (1)

38-58: Defensively require a non‑nil attr.Session before using session-backed caches

Both List and Grants assume attr.Session is non‑nil (for setRolesCache, getUsersByRole, and getRoleById). If a caller ever passes a SyncOpAttrs without a SessionStore, these calls will fail at runtime instead of returning a clear error.

Add a defensive check near the top of each method that touches attr.Session so callers get a structured InvalidArgument error instead of a panic or opaque failure.

 func (o *roleBuilder) List(ctx context.Context, _ *v2.ResourceId, attr rs.SyncOpAttrs) ([]*v2.Resource, *rs.SyncOpResults, error) {
 	l := ctxzap.Extract(ctx)
 	l.Debug("Listing roles")
 
 	rv := make([]*v2.Resource, 0)
 
 	var nextToken string
 
+	if attr.Session == nil {
+		return nil, nil, uhttp.WrapErrors(codes.InvalidArgument, "baton-workato: session storage is required")
+	}
+
 	if !o.disableCustomRolesSync {
@@
 func (o *roleBuilder) Grants(ctx context.Context, resource *v2.Resource, attr rs.SyncOpAttrs) ([]*v2.Grant, *rs.SyncOpResults, error) {
 	l := ctxzap.Extract(ctx)
 
+	if attr.Session == nil {
+		return nil, nil, uhttp.WrapErrors(codes.InvalidArgument, "baton-workato: session storage is required")
+	}
+
 	// Since roles names are unique, we can use the role name as the key to get all the users that have that role.
 	collaborators := getUsersByRole(ctx, attr.Session, resource.DisplayName)

This also ensures the subsequent use of getRoleById(ctx, attr.Session, ...) in the custom roles path is always safe.

Also applies to: 97-199

pkg/connector/connector.go (1)

68-92: Guard WorkatoDataCenters map lookup to avoid invalid/empty base URLs

dataCenterUrl := client.WorkatoDataCenters[config.WorkatoDataCenter] assumes the key is always valid. A bad or unexpected WorkatoDataCenter value will yield the zero value (""), which then flows into NewWorkatoClient and can produce confusing runtime errors instead of a clear configuration failure.

Add an existence check and return a descriptive error when the configured data center is not known.

 func New(ctx context.Context, config *cfg.Workato, _ *cli.ConnectorOpts) (connectorbuilder.ConnectorBuilderV2, []connectorbuilder.Opt, error) {
 	l := ctxzap.Extract(ctx)
 	err := field.Validate(cfg.Config, config)
 	if err != nil {
 		return nil, nil, err
 	}
 
-	dataCenterUrl := client.WorkatoDataCenters[config.WorkatoDataCenter]
+	dataCenterUrl, ok := client.WorkatoDataCenters[config.WorkatoDataCenter]
+	if !ok || dataCenterUrl == "" {
+		return nil, nil, fmt.Errorf("baton-workato: invalid data center %q", config.WorkatoDataCenter)
+	}
🧹 Nitpick comments (1)
pkg/connector/role_cache.go (1)

52-93: Reuse cache-prefix constants in setRolesCache and thanks for fixing pointer aliasing

parseJSONRolesCache now uses a per-iteration roleCopy, so each cached *client.Role points to a distinct instance—this fixes the earlier pointer alias bug. The folder-role map also uses copyRole := role, which is correct.

To keep readers and writers in lockstep, it would be clearer to reuse the prefix constants instead of string literals in setRolesCache:

 func setRolesCache(ctx context.Context, sessionStorage sessions.SessionStore, roles []client.Role) error {
-	err := session.SetManyJSON(ctx, sessionStorage, parseJSONRolesCache(roles), sessions.WithPrefix("roles"))
+	err := session.SetManyJSON(ctx, sessionStorage, parseJSONRolesCache(roles), sessions.WithPrefix(rolesCachePrefix))
@@
-	if (len(mapRoles)) > 0 {
-		err = session.SetManyJSON(ctx, sessionStorage, mapRoles, sessions.WithPrefix("folder_roles"))
+	if len(mapRoles) > 0 {
+		err = session.SetManyJSON(ctx, sessionStorage, mapRoles, sessions.WithPrefix(folderRolesCachePrefix))
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0d666e0 and a779a8a.

📒 Files selected for processing (5)
  • pkg/config/config.go (2 hunks)
  • pkg/connector/collaborator_cache.go (4 hunks)
  • pkg/connector/connector.go (3 hunks)
  • pkg/connector/role.go (7 hunks)
  • pkg/connector/role_cache.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/config/config.go
🧰 Additional context used
🧬 Code graph analysis (3)
pkg/connector/collaborator_cache.go (3)
pkg/connector/client/client.go (1)
  • WorkatoClient (28-33)
pkg/connector/workato/env.go (1)
  • Environment (10-10)
pkg/connector/client/models.go (1)
  • Collaborator (35-49)
pkg/connector/connector.go (5)
pkg/connector/client/client.go (2)
  • WorkatoClient (28-33)
  • NewWorkatoClient (35-61)
pkg/connector/workato/env.go (2)
  • Environment (10-10)
  • EnvFromString (22-33)
pkg/config/conf.gen.go (1)
  • Workato (6-11)
pkg/config/config.go (3)
  • Config (55-61)
  • WorkatoEnv (23-28)
  • DisableCustomRolesSync (30-35)
pkg/connector/client/helpers.go (1)
  • WorkatoDataCenters (18-24)
pkg/connector/role.go (1)
pkg/connector/workato/privileges.go (2)
  • Privileges (31-430)
  • FindRelatedPrivilegesErr (448-480)
🔇 Additional comments (3)
pkg/connector/collaborator_cache.go (1)

45-91: Session-backed collaborator lookup helpers look good

The getUsersByPrivilege, getUsersByFolder, and getUsersByRole helpers correctly scope keys by prefix, log failures, and safely treat cache misses as empty (nil) slices, which works fine with range semantics at call sites.

pkg/connector/role_cache.go (1)

20-50: Role lookup helpers are consistent with session caching pattern

getRoleByFolder and getRoleById correctly scope by prefixes, log retrieval errors, and return nil on cache miss, which matches how the callers treat “no cached roles”.

pkg/connector/connector.go (1)

28-35: ResourceSyncers migration to V2 builders looks consistent

The ResourceSyncers implementation cleanly migrates to ResourceSyncerV2 and wires each builder with the expected dependencies (env, disableCustomRolesSync), so the connector surface matches the updated Baton builder API.

Copy link
Contributor

@johnallers johnallers left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested locally and it worked.

I have some concerns for how the caches are initialized. It will break down if we ever implement parallel sync, but that's a problem for the future.

@luisina-santos luisina-santos changed the title Add containerization support implementing ResourceSyncerV2 and SessionStore cache [BB-1788] Add containerization support implementing ResourceSyncerV2 and SessionStore cache Dec 11, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
pkg/connector/folder.go (1)

96-112: Fix folder entitlement/grant comments and typos in descriptions

The comments and messages are now misleading:

  • // Entitlements always returns an empty slice for users. — this builder returns real entitlements for folder resources.
  • // Grants always returns an empty slice for users since they don't have any entitlements. — grants are being emitted for collaborators and roles.
  • User-facing strings use "acess" instead of "access".

Consider updating to something like:

-// Entitlements always returns an empty slice for users.
+// Entitlements returns collaborator-access entitlements for folder resources.
@@
-    entitlement.WithDescription(fmt.Sprintf("%s can acess %s", collaboratorResourceType.DisplayName, resource.DisplayName)),
-    entitlement.WithDisplayName(fmt.Sprintf("%s acess %s", collaboratorResourceType.DisplayName, resource.DisplayName)),
+    entitlement.WithDescription(fmt.Sprintf("%s can access %s", collaboratorResourceType.DisplayName, resource.DisplayName)),
+    entitlement.WithDisplayName(fmt.Sprintf("%s access %s", collaboratorResourceType.DisplayName, resource.DisplayName)),
@@
-// Grants always returns an empty slice for users since they don't have any entitlements.
+// Grants returns collaborator and role grants for folder resources.

to keep docs and UX strings accurate.

pkg/connector/role.go (2)

85-96: Update Entitlements doc comment to reflect actual behavior

The comment // Entitlements always returns an empty slice for users. is now incorrect: this method emits a collaborator-has entitlement for each role.

Consider updating it to something like:

-// Entitlements always returns an empty slice for users.
+// Entitlements returns a collaborator-has entitlement for each role resource.

to keep the docs aligned with the implementation.


39-59: Propagate errors from cache operations in Grants and ensure session availability

The cache read helpers (getUsersByRole, getRoleById, getUsersByFolder, getUsersByPrivilege) silently fail by logging errors and returning nil/empty results. This masks session storage problems:

  • roleBuilder.Grants (line 103, 165): o.cache.getUsersByRole() and getRoleById() return nil without error
  • folderBuilder.Grants: o.cache.getUsersByFolder() and getRoleByFolder() return nil without error
  • privilegeBuilder.Grants: o.cache.getUsersByPrivilege() returns nil without error

When attr.Session is unavailable, these methods silently return empty results instead of signaling the failure, making the code return incorrect grant lists. Unlike setRolesCache() which properly propagates errors (line 56), read methods should also return errors to callers. Consider updating cache helpers to return (results, error) or at minimum validating attr.Session != nil at method entry to fail fast with a clear error rather than silent empty results.

Also fix the doc comment for Entitlements (line 85): it claims the method "always returns an empty slice for users" but the implementation creates a single entitlement.

pkg/connector/privilege.go (1)

52-62: Fix privilege Entitlements/Grants comments and user-facing text

A few small but user-visible issues:

  • // Entitlements always returns an empty slice for users. — this emits a real entitlement per privilege.
  • // Grants always returns an empty slice for users since they don't have any entitlements. — this method returns collaborator grants for the privilege.
  • WithDisplayName(fmt.Sprintf("%s have %s", ...))` has a stray backtick and “have”/grammar issues.

Suggested cleanup:

-// Entitlements always returns an empty slice for users.
+// Entitlements returns assignment entitlements for each privilege resource.
@@
-    entitlement.WithDisplayName(fmt.Sprintf("%s have %s`", collaboratorResourceType.DisplayName, resource.DisplayName)),
+    entitlement.WithDisplayName(fmt.Sprintf("%s has %s", collaboratorResourceType.DisplayName, resource.DisplayName)),
@@
-// Grants always returns an empty slice for users since they don't have any entitlements.
+// Grants returns collaborator grants for the given privilege; privilege grants for roles are handled on the role resource.

This keeps docs and UI text coherent with the implementation.

Also applies to: 64-65

♻️ Duplicate comments (1)
pkg/connector/connector.go (1)

68-82: Validate WorkatoDataCenter before indexing WorkatoDataCenters

dataCenterUrl := client.WorkatoDataCenters[config.WorkatoDataCenter] assumes the key is valid; a typo or bad config yields "", which NewWorkatoClient accepts and only fails later in surprising ways.

Recommend guarding the lookup:

-    dataCenterUrl := client.WorkatoDataCenters[config.WorkatoDataCenter]
+    dataCenterUrl, ok := client.WorkatoDataCenters[config.WorkatoDataCenter]
+    if !ok || dataCenterUrl == "" {
+        return nil, nil, fmt.Errorf("baton-workato invalid data center %q", config.WorkatoDataCenter)
+    }

(and ensure fmt is imported). This gives a clear, early error on misconfiguration.

🧹 Nitpick comments (5)
pkg/connector/collaborator_cache.go (2)

87-151: Consider consolidating the three getter functions into a generic helper.

The getUsersByPrivilege, getUsersByFolder, and getUsersByRole functions follow an identical pattern (initialize cache, retrieve from session storage with a specific prefix, handle errors/not-found). This duplication could be eliminated with a single generic helper function.

Example refactor:

+func (c *collaboratorCache) getUsersByKey(ctx context.Context, sessionStorage sessions.SessionStore, key string, prefix string) []*CompoundUser {
+	l := ctxzap.Extract(ctx)
+
+	// Ensure cache is initialized before attempting to retrieve
+	if err := c.initializeCache(ctx, sessionStorage); err != nil {
+		l.Error("failed to ensure cache is initialized", zap.Error(err))
+		return nil
+	}
+
+	users, found, err := session.GetJSON[[]*CompoundUser](ctx, sessionStorage, key, sessions.WithPrefix(prefix))
+	if err != nil {
+		l.Error("failed to get users from session storage", zap.String("prefix", prefix), zap.Error(err))
+		return nil
+	}
+
+	if !found {
+		return nil
+	}
+
+	return users
+}
+
 func (c *collaboratorCache) getUsersByPrivilege(ctx context.Context, sessionStorage sessions.SessionStore, privilegeKey string) []*CompoundUser {
-	l := ctxzap.Extract(ctx)
-
-	// Ensure cache is initialized before attempting to retrieve
-	if err := c.initializeCache(ctx, sessionStorage); err != nil {
-		l.Error("failed to ensure cache is initialized", zap.Error(err))
-		return nil
-	}
-
-	users, found, err := session.GetJSON[[]*CompoundUser](ctx, sessionStorage, privilegeKey, sessions.WithPrefix(privilegeToUserCachePrefix))
-	if err != nil {
-		l.Error("failed to get users by privilege from session storage", zap.Error(err))
-		return nil
-	}
-
-	if !found {
-		return nil
-	}
-
-	return users
+	return c.getUsersByKey(ctx, sessionStorage, privilegeKey, privilegeToUserCachePrefix)
 }
 
 func (c *collaboratorCache) getUsersByFolder(ctx context.Context, sessionStorage sessions.SessionStore, folderId string) []*CompoundUser {
-	l := ctxzap.Extract(ctx)
-
-	// Ensure cache is initialized before attempting to retrieve
-	if err := c.initializeCache(ctx, sessionStorage); err != nil {
-		l.Error("failed to ensure cache is initialized", zap.Error(err))
-		return nil
-	}
-
-	users, found, err := session.GetJSON[[]*CompoundUser](ctx, sessionStorage, folderId, sessions.WithPrefix(folderToUserCachePrefix))
-	if err != nil {
-		l.Error("failed to get users by folder from session storage", zap.Error(err))
-		return nil
-	}
-
-	if !found {
-		return nil
-	}
-
-	return users
+	return c.getUsersByKey(ctx, sessionStorage, folderId, folderToUserCachePrefix)
 }
 
 func (c *collaboratorCache) getUsersByRole(ctx context.Context, sessionStorage sessions.SessionStore, roleName string) []*CompoundUser {
-	l := ctxzap.Extract(ctx)
-
-	// Ensure cache is initialized before attempting to retrieve
-	if err := c.initializeCache(ctx, sessionStorage); err != nil {
-		l.Error("failed to ensure cache is initialized", zap.Error(err))
-		return nil
-	}
-
-	users, found, err := session.GetJSON[[]*CompoundUser](ctx, sessionStorage, roleName, sessions.WithPrefix(roleToUserCachePrefix))
-	if err != nil {
-		l.Error("failed to get users by role from session storage", zap.Error(err))
-		return nil
-	}
-
-	if !found {
-		return nil
-	}
-
-	return users
+	return c.getUsersByKey(ctx, sessionStorage, roleName, roleToUserCachePrefix)
 }

208-225: Remove redundant parentheses in length checks.

The parentheses around len(...) in the conditional checks are unnecessary.

Apply this diff:

-	if (len(privilegeToUsers)) > 0 {
+	if len(privilegeToUsers) > 0 {
 		err := session.SetManyJSON(ctx, sessionStorage, privilegeToUsers, sessions.WithPrefix(privilegeToUserCachePrefix))
 		if err != nil {
 			return fmt.Errorf("failed to set privilege to user cache in session storage: %w", err)
 		}
 	}
-	if (len(folderToUsers)) > 0 {
+	if len(folderToUsers) > 0 {
 		err := session.SetManyJSON(ctx, sessionStorage, folderToUsers, sessions.WithPrefix(folderToUserCachePrefix))
 		if err != nil {
 			return fmt.Errorf("failed to set folder to user cache in session storage: %w", err)
 		}
 	}
-	if (len(roleToUsers)) > 0 {
+	if len(roleToUsers) > 0 {
 		err := session.SetManyJSON(ctx, sessionStorage, roleToUsers, sessions.WithPrefix(roleToUserCachePrefix))
 		if err != nil {
 			return fmt.Errorf("failed to set role to user cache in session storage: %w", err)
 		}
 	}
pkg/connector/role_cache.go (2)

20-50: Distinguish session errors from cache misses in role helpers

Both getRoleByFolder and getRoleById treat any session.GetJSON error the same as a cache miss (log then return nil). Callers (e.g., roleBuilder.Grants) then convert nil into a NotFound, which can mask real storage or serialization issues.

Consider changing these helpers to return either (value, found bool, err error) or (value, error) so callers can differentiate “role not cached/not found” from “session backend error” and surface a more appropriate status.


52-95: Minor: reuse cache prefix constants in setters

setRolesCache currently hardcodes "roles" and "folder_roles" instead of using the defined constants, which can drift if prefixes change.

You can tighten this up with:

-    err := session.SetManyJSON(ctx, sessionStorage, parseJSONRolesCache(roles), sessions.WithPrefix("roles"))
+    err := session.SetManyJSON(ctx, sessionStorage, parseJSONRolesCache(roles), sessions.WithPrefix(rolesCachePrefix))
...
-    err := session.SetManyJSON(ctx, sessionStorage, mapRoles, sessions.WithPrefix("folder_roles"))
+    err := session.SetManyJSON(ctx, sessionStorage, mapRoles, sessions.WithPrefix(folderRolesCachePrefix))

Also, the roleCopy pattern in parseJSONRolesCache correctly avoids the pointer-aliasing bug from the previous revision.

pkg/connector/privilege.go (1)

65-92: Note on session usage in privilege Grants

privilegeBuilder.Grants now relies on o.cache.getUsersByPrivilege(ctx, attr.Session, privilegeId), mirroring the session-backed approach in roleBuilder and folderBuilder. Once you add a defensive attr.Session nil check in roleBuilder as suggested, it would be good to apply the same pattern here (or factor out a shared helper) to prevent nil-session panics across all collaborators/privilege grant paths.

Also applies to: 94-99

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a779a8a and f642994.

⛔ Files ignored due to path filters (1)
  • cmd/baton-workato/main.go is excluded by none and included by none
📒 Files selected for processing (6)
  • pkg/connector/collaborator_cache.go (4 hunks)
  • pkg/connector/connector.go (3 hunks)
  • pkg/connector/folder.go (6 hunks)
  • pkg/connector/privilege.go (4 hunks)
  • pkg/connector/role.go (9 hunks)
  • pkg/connector/role_cache.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (5)
pkg/connector/collaborator_cache.go (3)
pkg/connector/client/client.go (1)
  • WorkatoClient (28-33)
pkg/connector/workato/env.go (1)
  • Environment (10-10)
pkg/connector/client/models.go (1)
  • Collaborator (35-49)
pkg/connector/connector.go (4)
pkg/connector/client/client.go (2)
  • WorkatoClient (28-33)
  • NewWorkatoClient (35-61)
pkg/connector/workato/env.go (2)
  • Environment (10-10)
  • EnvFromString (22-33)
pkg/config/conf.gen.go (1)
  • Workato (6-11)
pkg/connector/client/helpers.go (1)
  • WorkatoDataCenters (18-24)
pkg/connector/role_cache.go (2)
pkg/connector/client/models.go (1)
  • Role (65-73)
pkg/connector/workato/roles.go (1)
  • Role (7-10)
pkg/connector/role.go (1)
pkg/connector/workato/privileges.go (2)
  • Privileges (31-430)
  • FindRelatedPrivilegesErr (448-480)
pkg/connector/folder.go (1)
pkg/connector/cpagination/generic_bag.go (1)
  • GenBagFromToken (23-30)
🔇 Additional comments (10)
pkg/connector/collaborator_cache.go (7)

3-16: LGTM!

The new imports for session storage (session, sessions) and error formatting (fmt) are appropriate for the migration from in-memory to session-backed caching.


27-37: LGTM!

The struct has been properly simplified to remove in-memory cache state and mutex, keeping only the client and environment fields needed for session-backed operations.


39-45: LGTM!

The cache prefix constants provide clear namespacing for different cache types and the initialization marker, ensuring no key collisions in session storage.


47-54: LGTM!

The cache initialization check is straightforward and properly handles errors from session storage operations.


56-85: LGTM!

The initialization flow correctly checks for existing cache, fetches collaborators only when needed, and properly delegates to the cache-setting function with appropriate error handling.


208-225: Good use of bulk writes with SetManyJSON.

The implementation now correctly uses SetManyJSON to write all cache entries in bulk operations, addressing the concern from previous reviews about reducing network requests to session storage.

Based on past review comments, this approach is more efficient than writing in a loop.


236-238: LGTM!

The generic helper function provides a clean, type-safe way to append values to the cache maps.

pkg/connector/folder.go (1)

37-90: List: SyncOpAttrs/SyncOpResults migration looks consistent

The updated List implementation uses attr.PageToken.Token for GetProjects/GetFolders and returns NextPageToken via rs.SyncOpResults, which aligns with the new V2 patterns. Treating parentResourceID == nil as a no-op for folderBuilder (projects own the roots) also makes sense.

pkg/connector/role.go (1)

26-31: Collaborator cache wiring for roles looks consistent

Adding cache *collaboratorCache to roleBuilder and initializing it via newCollaboratorCache(client, env) in newRoleBuilder matches how folder and privilege builders are using the shared collaborator cache. This keeps collaborator lookups centralized and session-backed.

Also applies to: 278-283

pkg/connector/privilege.go (1)

32-49: Privilege List: SyncOpAttrs migration looks fine

List ignores the parent ID (as before), materializes all compound privileges, and now returns (*rs.SyncOpResults)(nil) which is appropriate for a non-paginated list. Using rs.SyncOpAttrs here is consistent with the other builders.

)

DisableCustomRolesSync = field.BoolField(
"disable-custom-roles-sync",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just a heads up, this config name is not the same in c1 It'll need mapping

@johnallers johnallers merged commit 4b65354 into main Dec 12, 2025
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants