Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`, `<integrationViewId>`, `CATALOG:<integrationViewId>`, `TOOLS:<integrationViewId>` (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:<integrationViewId>`; 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 `<integrationViewId>` and `ACCESS:<integrationViewId>` of that connection |
| Functions | `useGetMarketplaceFunctionsQuery`, `useGetMarketplaceFunctionQuery`, `useSaveMarketplaceFunctionMutation`, `useDeleteMarketplaceFunctionMutation`, `useEnableMarketplaceFunctionMutation`, `useDisableMarketplaceFunctionMutation`, `useGetMarketplaceFunctionTokensQuery`, `useInvokeMarketplaceFunctionMutation`, `useGetMarketplaceFunctionRunsQuery` | `FUNCTIONS:<integrationViewId>`, `<functionViewId>`, `RUNS`; invoke touches nothing |

Revealing secrets is a mutation on purpose: a query would keep the clear values
Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
7 changes: 7 additions & 0 deletions src/hooks/hub/marketplace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'>;
Expand Down Expand Up @@ -188,6 +189,12 @@ export const hubCodeMarketplace = (b: Builder) => {
startMarketplaceIntegrationSignIn: b.mutation<Result<StartSignIn>, Arg<StartSignIn>>({
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<Result<SignOut>, Arg<SignOut>>({
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<Result<GetFunctions>, Arg<GetFunctions>>({
Expand Down
19 changes: 18 additions & 1 deletion tests/marketplace.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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, {}],
[
Expand Down Expand Up @@ -187,7 +188,7 @@ function tagsFor(tags: Tags | undefined, arg: unknown): unknown {
const endpoints = hubCodeMarketplace(fakeBuilder()) as unknown as Record<string, Def>;

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());
});

Expand Down Expand Up @@ -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' },
Expand Down