Skip to content

feat(team-library): share connections and saved queries with your team#1832

Merged
datlechin merged 5 commits into
mainfrom
feat/team-library
Jul 8, 2026
Merged

feat(team-library): share connections and saved queries with your team#1832
datlechin merged 5 commits into
mainfrom
feat/team-library

Conversation

@datlechin

@datlechin datlechin commented Jul 8, 2026

Copy link
Copy Markdown
Member

App side of the Team Library: publish connections and saved queries to your team through your account (not a shared folder), and see the shared set in-app. Team-tier gated. Passwords are never sent. Pairs with the backend PR (TableProApp/license#68).

What's included

  • Gating: new ProFeature.teamLibrary (requires the Team tier), reusing the existing tier resolution.
  • Publish: right-click a connection > Share > Publish to Team Library (welcome list), and Publish Saved Queries to Team (Favorites sidebar). Both reuse ConnectionExportService.buildEnvelope, so no password, passphrase, TOTP secret, or secure plugin field ever leaves the app.
  • Pull + cache: TeamLibrarySyncCoordinator pulls the shared set on the license revalidation cadence (and at launch), caches it via a small actor-backed store, and drives the UI. License access and credentials are injected so it is unit-testable.
  • Display: shared connections appear read-only in a Team Library section of the welcome connection list and connect through the existing linked-connection path (transient, never saved to storage or synced). Shared queries appear read-only in the Favorites sidebar and open in a new tab.
  • Networking mirrors LicenseAPIClient; the wire format is snake_case via CodingKeys so the embedded connection payload keeps its own encoding.

Tests

6 Swift Testing cases (wire-format decode/encode round-trip, gating, pull caching, 7-day cadence, publish mapping). App builds; SwiftLint clean. CHANGELOG and docs updated.

Adversarially reviewed; the main-actor isolation of the publish alert tasks came out of that review.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4883c67d9a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

}

func pullIfNeeded() async {
guard isFeatureAvailable(), TeamLibraryMetadataStorage.isPullDue else { return }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Scope the team-library cache to the active license

After a successful pull, lastPullAt is global and start() loads the single team_library.json cache before this guard. If someone deactivates Team A and activates/joins Team B on the same Mac within seven days, isFeatureAvailable() is true but this guard skips the network pull, so the UI continues exposing Team A’s cached queries/connections under Team B. Key the cache and pull timestamp by license/team, or clear/pull whenever the active license changes.

Useful? React with 👍 / 👎.

AnalyticsService.shared.startPeriodicHeartbeat()
SyncCoordinator.shared.start()
LinkedFolderWatcher.shared.start()
TeamLibrarySyncCoordinator.shared.start()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Trigger team-library sync after activation

Starting the coordinator only from post-launch activation means a common flow is missed: when the app launches unlicensed or on Starter, start() returns at its feature gate, and activating a Team license or accepting an invite later does not call start() or pull(). Since periodic validation only runs on the 7-day cadence, newly joined Team users will not see shared queries until relaunch or revalidation; trigger a pull from the activation/status-change path as well.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c66570cf20

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

let placeholderURL = URL(fileURLWithPath: "/")
return TeamLibrarySyncCoordinator.shared.library.connections.map { connection in
LinkedConnection(
id: UUID(uuidString: connection.sourceConnectionId ?? "") ?? UUID(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Use a distinct ID for team-library rows

When a user has already published one of their local connections, sourceConnectionId is that local connection UUID, so using it as the LinkedConnection.id gives the Team Library row the same List tag as the local row. Selecting or double-clicking that Team Library row then makes primaryAction match both the local-connection loop and the team-library loop, opening both copies and resolving context menus as the local connection. Use a team-library-specific stable ID, such as the server connection.id, instead of the source local ID.

Useful? React with 👍 / 👎.

vm.connectToLinkedConnection(linked)
}
for linked in vm.teamLibraryConnections where ids.contains(linked.id) {
vm.connectToLinkedConnection(linked)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve the full shared connection payload

When a shared Team Library connection uses SSH/SSL settings, Redis DB, startup commands, or plugin additional fields, routing it through connectToLinkedConnection drops those fields because that helper reconstructs a DatabaseConnection from only id/name/host/port/database/username/type before opening it. The pulled ExportableConnection already contains the remaining payload, so Team Library connections that need those settings will fail or connect incorrectly; this path should import/convert the full exportable payload instead.

Useful? React with 👍 / 👎.

@mintlify

mintlify Bot commented Jul 8, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
TablePro 🟢 Ready View Preview Jul 8, 2026, 10:12 AM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@datlechin datlechin merged commit 8196b23 into main Jul 8, 2026
3 checks passed
@datlechin datlechin deleted the feat/team-library branch July 8, 2026 11:23
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.

1 participant