diff --git a/README.md b/README.md index 800911d..240bda9 100644 --- a/README.md +++ b/README.md @@ -558,6 +558,7 @@ added on a connection (`func_…`). Every tag is of type `CodeMarketplace`. | Connections | `useGetMarketplaceIntegrationsQuery`, `useGetMarketplaceIntegrationQuery`, `useSaveMarketplaceIntegrationMutation`, `useDeleteMarketplaceIntegrationMutation`, `useEnableMarketplaceIntegrationMutation`, `useDisableMarketplaceIntegrationMutation`, `useReplaceMarketplaceIntegrationSecretsMutation`, `useRevealMarketplaceIntegrationSecretsMutation`, `useSetMarketplaceIntegrationTokenMappingsMutation`, `useTestMarketplaceIntegrationMutation`, `useGetMarketplaceFunctionCatalogQuery`, `useGetMarketplaceIntegrationToolsQuery` (the stored MCP tool catalog of an MCP connection, core SDK 4.23.0+) | `INTEGRATIONS`, ``, `CATALOG:`, `TOOLS:` (test and delete invalidate it); reveal touches nothing | | Access | `useGetMarketplaceIntegrationAccessQuery` (the access matrix of a connection: roles, packages, tools, functions), `useSetMarketplaceIntegrationRoleAccessMutation` (a role's package + Manage), `useSaveMarketplaceIntegrationCustomAccessMutation` (a custom policy for one role) — core SDK 4.24.0+ | `ACCESS:`; the two writes invalidate only that connection's matrix, and so do test, delete, function save and function delete | | Sign-in | `useStartMarketplaceIntegrationSignInMutation` (OAuth sign-in of an MCP connection: open `authorizeUrl`; core SDK 4.24.0+) | invalidates nothing — read the connection again (`signedIn`) after the person returns | +| Sign-out | `useSignOutMarketplaceIntegrationMutation` (OAuth sign-out of an MCP connection: `wasSignedIn`, `revoke`; core SDK 4.25.0+) | invalidates `` and `ACCESS:` of that connection | | Functions | `useGetMarketplaceFunctionsQuery`, `useGetMarketplaceFunctionQuery`, `useSaveMarketplaceFunctionMutation`, `useDeleteMarketplaceFunctionMutation`, `useEnableMarketplaceFunctionMutation`, `useDisableMarketplaceFunctionMutation`, `useGetMarketplaceFunctionTokensQuery`, `useInvokeMarketplaceFunctionMutation`, `useGetMarketplaceFunctionRunsQuery` | `FUNCTIONS:`, ``, `RUNS`; invoke touches nothing | Revealing secrets is a mutation on purpose: a query would keep the clear values diff --git a/package-lock.json b/package-lock.json index 9a210d2..7459f74 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@commitlint/cli": "^21.2.2", "@commitlint/config-conventional": "^21.2.2", "@eslint/js": "^10.0.1", - "@norbix.ai/ts": "^4.24.0", + "@norbix.ai/ts": "^4.25.0", "@reduxjs/toolkit": "^2.12.0", "@semantic-release/commit-analyzer": "^13.0.1", "@semantic-release/github": "^12.0.9", @@ -1490,9 +1490,9 @@ } }, "node_modules/@norbix.ai/ts": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@norbix.ai/ts/-/ts-4.24.0.tgz", - "integrity": "sha512-YJrbsb/qvtVkjkzT+DZKVE0MhqxTbVD7K+tkNyB/Eh82dMEnJNu4kvM6YP/t9tbYms5N4rilcl9zISr/VTTDUQ==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/@norbix.ai/ts/-/ts-4.25.0.tgz", + "integrity": "sha512-izKpx/INFqGyoluT44C7M3lE+29Qelkd40OT6YBHi4TkYcWXSt5P4BUx9B+8j2dm3bAmyL2fw6p5R7zvNaxIsQ==", "dev": true, "license": "MIT", "engines": { diff --git a/package.json b/package.json index c81db94..2a1f28a 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "@commitlint/cli": "^21.2.2", "@commitlint/config-conventional": "^21.2.2", "@eslint/js": "^10.0.1", - "@norbix.ai/ts": "^4.24.0", + "@norbix.ai/ts": "^4.25.0", "@reduxjs/toolkit": "^2.12.0", "@semantic-release/commit-analyzer": "^13.0.1", "@semantic-release/github": "^12.0.9", diff --git a/src/hooks/hub/marketplace.ts b/src/hooks/hub/marketplace.ts index 9a90cf0..f39db40 100644 --- a/src/hooks/hub/marketplace.ts +++ b/src/hooks/hub/marketplace.ts @@ -56,6 +56,7 @@ export const hubCodeMarketplace = (b: Builder) => { type SetRoleAccess = Fn<'setMarketplaceIntegrationRoleAccess'>; type SaveCustomAccess = Fn<'saveMarketplaceIntegrationCustomAccess'>; type StartSignIn = Fn<'startMarketplaceIntegrationSignIn'>; + type SignOut = Fn<'signOutMarketplaceIntegration'>; type GetFunctions = Fn<'getMarketplaceFunctions'>; type GetFunction = Fn<'getMarketplaceFunction'>; type SaveFunction = Fn<'saveMarketplaceFunction'>; @@ -188,6 +189,12 @@ export const hubCodeMarketplace = (b: Builder) => { startMarketplaceIntegrationSignIn: b.mutation, Arg>({ query: (args) => (norbix) => callHC(norbix).startMarketplaceIntegrationSignIn(args), }), + // Sign-out (route 30, core SDK 4.25.0) changes the connection at once (`signedIn` false) + // and its access (the connection's MCP tools are no longer reachable). + signOutMarketplaceIntegration: b.mutation, Arg>({ + query: (args) => (norbix) => callHC(norbix).signOutMarketplaceIntegration(args), + invalidatesTags: (_res, _err, arg) => [{ type: T, id: integrationId(arg) }, access(arg)], + }), // ---- Functions on a connection ---- getMarketplaceFunctions: b.query, Arg>({ diff --git a/tests/marketplace.test.ts b/tests/marketplace.test.ts index 053a32d..4af6cf0 100644 --- a/tests/marketplace.test.ts +++ b/tests/marketplace.test.ts @@ -133,6 +133,7 @@ const CASES: Array< }, ], ['startMarketplaceIntegrationSignIn', 'mutation', 'POST', `${I}/oauth/start`, IDS, {}], + ['signOutMarketplaceIntegration', 'mutation', 'POST', `${I}/oauth/sign-out`, IDS, {}], ['getMarketplaceFunctions', 'query', 'GET', `${I}/functions`, IDS, {}], ['getMarketplaceFunction', 'query', 'GET', `${I}/functions/${FUNCTION}`, FN_IDS, {}], [ @@ -187,7 +188,7 @@ function tagsFor(tags: Tags | undefined, arg: unknown): unknown { const endpoints = hubCodeMarketplace(fakeBuilder()) as unknown as Record; describe('hubCodeMarketplace', () => { - it('wraps the 28 marketplace methods of the published core SDK', () => { + it('wraps the 29 marketplace methods of the published core SDK', () => { expect(Object.keys(endpoints).sort()).toEqual([...NAMES].sort()); }); @@ -292,6 +293,22 @@ describe('hubCodeMarketplace', () => { expect(endpoints['startMarketplaceIntegrationSignIn']!.invalidatesTags).toBeUndefined(); }); + it('signing out invalidates the connection and its ACCESS tag, only for that connection (core SDK 4.25.0)', () => { + expect(endpoints['signOutMarketplaceIntegration']!.__kind).toBe('mutation'); + expect(tagsFor(endpoints['signOutMarketplaceIntegration']!.invalidatesTags, IDS)).toEqual([ + { type: 'CodeMarketplace', id: INTEGRATION }, + { type: 'CodeMarketplace', id: `ACCESS:${INTEGRATION}` }, + ]); + expect( + tagsFor(endpoints['signOutMarketplaceIntegration']!.invalidatesTags, { + integrationViewId: 'int_other', + }), + ).toEqual([ + { type: 'CodeMarketplace', id: 'int_other' }, + { type: 'CodeMarketplace', id: 'ACCESS:int_other' }, + ]); + }); + it('the last runs provide CodeMarketplace/RUNS', () => { expect(tagsFor(endpoints['getMarketplaceFunctionRuns']!.providesTags, {})).toEqual([ { type: 'CodeMarketplace', id: 'RUNS' },