-
Notifications
You must be signed in to change notification settings - Fork 6
feat: Artist TikTok Connections via Composio #170
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
47 commits
Select commit
Hold shift + click to select a range
3c18d64
feat: [US-002] Create Supabase functions for artist_composio_connections
sidneyswift fd74598
feat: [US-003] Create GET /api/artist-connectors endpoint
sidneyswift ca25ef5
feat: [US-004] Create POST /api/artist-connectors/authorize endpoint
sidneyswift cceffd4
feat: [US-005] Add DELETE /api/artist-connectors endpoint
sidneyswift 5445abb
feat: [US-006] Add artist-connectors callback URL destination
sidneyswift ac6da1d
feat: [US-007] Add TikTok to enabled toolkits
sidneyswift b276a7a
feat: [US-008] Modify createSession to accept connectedAccounts
sidneyswift e359693
feat: [US-009] Wire up artistId in chat tool setup
sidneyswift e39c59d
feat: [US-014] Handle OAuth callback with complete endpoint
sidneyswift 6f51f40
refactor: use artistId as Composio entity for artist connections
sidneyswift 933a702
fix: resolve TypeScript errors with readonly array types
sidneyswift 23248d4
refactor: DRY up composio connectors - unified authorizeConnector, ge…
sidneyswift c7e4773
refactor: unify /api/connectors with entity_type support, delete /api…
sidneyswift 3b404a4
refactor: address code review - SRP handlers, unified validators, tes…
sidneyswift 3a9858b
docs: add API route patterns and reviewer principles to CLAUDE.md
sidneyswift c12250c
Merge remote-tracking branch 'origin/test' into feat/artist-composio-…
sidneyswift fbf424d
fix: update setupToolsForRequest test for new artistId parameter
sidneyswift fa577e7
refactor: extract getArtistConnectionsFromComposio to own file (SRP)
sidneyswift 93639d9
refactor: rename createSession.ts to createToolRouterSession.ts
sidneyswift 7eec10a
test: add unit tests for composio toolRouter changes
sidneyswift cf0616e
test: add comprehensive unit tests for all changed files
sidneyswift 92b1733
fix: revert validateCreateChatBody.test.ts to original (no changes ne…
sidneyswift 426382f
refactor: remove entity_type, use entity_id presence for connection type
sidneyswift f066eb2
fix: remove unrelated changes, keep only composio feature
sidneyswift fd93442
Merge remote-tracking branch 'origin/main' into feat/artist-composio-…
sidneyswift 76fcfd4
fix: remove entity_type from JSDoc comments in route files
sidneyswift 42d7b85
fix: replace 'user' with 'account' in all composio JSDoc comments
sidneyswift 26a30e6
refactor: use validateAuthContext instead of validateAccountIdHeaders
sidneyswift 9cef620
fix: restore artistId wiring and add access check in getComposioTools
sidneyswift ddc5eab
refactor: remove entity-connectors callback URL logic (deferred to se…
sidneyswift 3b8e628
Merge remote-tracking branch 'origin/test' into feat/artist-composio-…
sweetmantech 29b1cdf
refactor: rename entity_id to account_id in connectors API
sidneyswift 0752c37
feat: broaden account_id access to support self, artist, workspace, a…
sidneyswift 1d223fa
docs: add terminology and flat response shape rules to CLAUDE.md
sidneyswift 74265d0
refactor: move connector restrictions from API to tool router level
sidneyswift ac82877
Merge pull request #220 from recoupable/test
sweetmantech c1130ca
Merge remote-tracking branch 'origin/main' into feat/artist-composio-…
sweetmantech f9ef320
refactor: move POST /api/connectors/authorize to POST /api/connectors
sweetmantech 20389c6
refactor: extract standalone supabase queries from checkAccountArtist…
sweetmantech ab95783
refactor: extract selectAccountWorkspaceId from checkAccountWorkspace…
sweetmantech f077d25
refactor: rename entityId to accountId across all connector code
sweetmantech 0f74641
refactor: remove unused allowedToolkits filtering from getConnectors
sweetmantech f380a4b
fix: correct misleading docstring in validateDisconnectConnectorBody
sweetmantech a213e31
refactor: move checkAccountArtistAccess to lib/artists/
sweetmantech bbd5415
refactor: delete checkAccountWorkspaceAccess wrapper, use selectAccou…
sweetmantech f8e8bbc
fix: filter artist connections locally after allowedToolkits removal
sweetmantech acf019d
fix: add missing_fields to connector validation error responses
sweetmantech File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| import { describe, it, expect, vi, beforeEach } from "vitest"; | ||
| import { checkAccountArtistAccess } from "../checkAccountArtistAccess"; | ||
|
|
||
| vi.mock("@/lib/supabase/account_artist_ids/selectAccountArtistId", () => ({ | ||
| selectAccountArtistId: vi.fn(), | ||
| })); | ||
|
|
||
| vi.mock("@/lib/supabase/artist_organization_ids/selectArtistOrganizationIds", () => ({ | ||
| selectArtistOrganizationIds: vi.fn(), | ||
| })); | ||
|
|
||
| vi.mock("@/lib/supabase/account_organization_ids/selectAccountOrganizationIds", () => ({ | ||
| selectAccountOrganizationIds: vi.fn(), | ||
| })); | ||
|
|
||
| import { selectAccountArtistId } from "@/lib/supabase/account_artist_ids/selectAccountArtistId"; | ||
| import { selectArtistOrganizationIds } from "@/lib/supabase/artist_organization_ids/selectArtistOrganizationIds"; | ||
| import { selectAccountOrganizationIds } from "@/lib/supabase/account_organization_ids/selectAccountOrganizationIds"; | ||
|
|
||
| describe("checkAccountArtistAccess", () => { | ||
| beforeEach(() => { | ||
| vi.clearAllMocks(); | ||
| }); | ||
|
|
||
| it("should return true when account has direct access to artist", async () => { | ||
| vi.mocked(selectAccountArtistId).mockResolvedValue({ artist_id: "artist-123" }); | ||
|
|
||
| const result = await checkAccountArtistAccess("account-123", "artist-123"); | ||
|
|
||
| expect(selectAccountArtistId).toHaveBeenCalledWith("account-123", "artist-123"); | ||
| expect(result).toBe(true); | ||
| expect(selectArtistOrganizationIds).not.toHaveBeenCalled(); | ||
| }); | ||
|
|
||
| it("should return true when account and artist share an organization", async () => { | ||
| vi.mocked(selectAccountArtistId).mockResolvedValue(null); | ||
| vi.mocked(selectArtistOrganizationIds).mockResolvedValue([ | ||
| { organization_id: "org-1" }, | ||
| ]); | ||
| vi.mocked(selectAccountOrganizationIds).mockResolvedValue([ | ||
| { organization_id: "org-1" }, | ||
| ]); | ||
|
|
||
| const result = await checkAccountArtistAccess("account-123", "artist-456"); | ||
|
|
||
| expect(selectArtistOrganizationIds).toHaveBeenCalledWith("artist-456"); | ||
| expect(selectAccountOrganizationIds).toHaveBeenCalledWith("account-123", ["org-1"]); | ||
| expect(result).toBe(true); | ||
| }); | ||
|
|
||
| it("should return false when artist org lookup errors (fail closed)", async () => { | ||
| vi.mocked(selectAccountArtistId).mockResolvedValue(null); | ||
| vi.mocked(selectArtistOrganizationIds).mockResolvedValue(null); | ||
|
|
||
| const result = await checkAccountArtistAccess("account-123", "artist-123"); | ||
|
|
||
| expect(result).toBe(false); | ||
| }); | ||
|
|
||
| it("should return false when account has no access", async () => { | ||
| vi.mocked(selectAccountArtistId).mockResolvedValue(null); | ||
| vi.mocked(selectArtistOrganizationIds).mockResolvedValue([]); | ||
|
|
||
| const result = await checkAccountArtistAccess("account-123", "artist-456"); | ||
|
|
||
| expect(result).toBe(false); | ||
| expect(selectAccountOrganizationIds).not.toHaveBeenCalled(); | ||
| }); | ||
|
|
||
| it("should return false when account org lookup errors (fail closed)", async () => { | ||
| vi.mocked(selectAccountArtistId).mockResolvedValue(null); | ||
| vi.mocked(selectArtistOrganizationIds).mockResolvedValue([ | ||
| { organization_id: "org-1" }, | ||
| ]); | ||
| vi.mocked(selectAccountOrganizationIds).mockResolvedValue(null); | ||
|
|
||
| const result = await checkAccountArtistAccess("account-123", "artist-456"); | ||
|
|
||
| expect(result).toBe(false); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| import { selectAccountArtistId } from "@/lib/supabase/account_artist_ids/selectAccountArtistId"; | ||
| import { selectArtistOrganizationIds } from "@/lib/supabase/artist_organization_ids/selectArtistOrganizationIds"; | ||
| import { selectAccountOrganizationIds } from "@/lib/supabase/account_organization_ids/selectAccountOrganizationIds"; | ||
|
|
||
| /** | ||
| * Check if an account has access to a specific artist. | ||
| * | ||
| * Access is granted if: | ||
| * 1. Account has direct access via account_artist_ids, OR | ||
| * 2. Account and artist share an organization | ||
| * | ||
| * Fails closed: returns false on any database error to deny access safely. | ||
| * | ||
| * @param accountId - The account ID to check | ||
| * @param artistId - The artist ID to check access for | ||
| * @returns true if the account has access to the artist, false otherwise | ||
| */ | ||
| export async function checkAccountArtistAccess( | ||
| accountId: string, | ||
| artistId: string, | ||
| ): Promise<boolean> { | ||
| // 1. Check direct access via account_artist_ids | ||
| const directAccess = await selectAccountArtistId(accountId, artistId); | ||
|
|
||
| if (directAccess) return true; | ||
|
|
||
| // 2. Check organization access: account and artist share an org | ||
| const artistOrgs = await selectArtistOrganizationIds(artistId); | ||
|
|
||
| if (!artistOrgs) return false; // Fail closed on error | ||
|
|
||
| if (!artistOrgs.length) return false; | ||
|
|
||
| const orgIds = artistOrgs | ||
| .map((o) => o.organization_id) | ||
| .filter((id): id is string => Boolean(id)); | ||
| if (!orgIds.length) return false; | ||
|
|
||
| const userOrgAccess = await selectAccountOrganizationIds(accountId, orgIds); | ||
|
|
||
| if (!userOrgAccess) return false; // Fail closed on error | ||
|
|
||
| return !!userOrgAccess.length; | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.