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
32 changes: 32 additions & 0 deletions apps/docs/openapi-v2-files-audit.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,38 @@
},
{
"$ref": "#/components/parameters/Cursor"
},
{
"name": "folderId",
"in": "query",
"required": false,
"description": "Restrict the list to one folder. Omit to list every file in the workspace.",
"schema": { "type": "string", "minLength": 1 }
},
{
"name": "search",
"in": "query",
"required": false,
"description": "Case-insensitive substring match against the file `name`. Matches nothing else — not ids, descriptions, or content. `%` and `_` are matched literally. Must be non-empty; omit the parameter instead of sending a blank one.",
"schema": { "type": "string", "minLength": 1, "maxLength": 200 }
},
{
"name": "sortBy",
"in": "query",
"required": false,
"description": "Field to sort by. The cursor is a keyset over the active sort, so it carries the sort it was minted under. Replaying a cursor after changing `sortBy` or `sortOrder` returns `400`; restart pagination without a cursor instead.",
"schema": {
"type": "string",
"enum": ["name", "size", "uploadedAt", "updatedAt"],
"default": "uploadedAt"
}
},
{
"name": "sortOrder",
"in": "query",
"required": false,
"description": "Sort direction. The cursor is a keyset over the active sort, so it carries the sort it was minted under. Replaying a cursor after changing `sortBy` or `sortOrder` returns `400`; restart pagination without a cursor instead.",
"schema": { "type": "string", "enum": ["asc", "desc"], "default": "asc" }
}
],
"responses": {
Expand Down
32 changes: 32 additions & 0 deletions apps/docs/openapi-v2-knowledge.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,38 @@
"parameters": [
{
"$ref": "#/components/parameters/WorkspaceIdQuery"
},
{
"name": "folderId",
"in": "query",
"required": false,
"description": "Restrict the list to one folder. Omit to list every knowledge base in the workspace.",
"schema": { "type": "string", "minLength": 1 }
},
{
"name": "search",
"in": "query",
"required": false,
"description": "Case-insensitive substring match against the knowledge base `name`. Matches nothing else — not ids, descriptions, or content. `%` and `_` are matched literally. Must be non-empty; omit the parameter instead of sending a blank one.",
"schema": { "type": "string", "minLength": 1, "maxLength": 200 }
},
{
"name": "sortBy",
"in": "query",
"required": false,
"description": "Field to sort by.",
"schema": {
"type": "string",
"enum": ["name", "createdAt", "updatedAt"],
"default": "createdAt"
}
},
{
"name": "sortOrder",
"in": "query",
"required": false,
"description": "Sort direction.",
"schema": { "type": "string", "enum": ["asc", "desc"], "default": "asc" }
}
],
"responses": {
Expand Down
137 changes: 134 additions & 3 deletions apps/docs/openapi-v2-resources.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,34 @@
"source": "curl \\\n \"https://www.sim.ai/api/v2/mcp-servers?workspaceId=YOUR_WORKSPACE_ID\" \\\n -H \"X-API-Key: YOUR_API_KEY\""
}
],
"parameters": [{ "$ref": "#/components/parameters/WorkspaceIdQuery" }],
"parameters": [
{ "$ref": "#/components/parameters/WorkspaceIdQuery" },
{
"name": "search",
"in": "query",
"required": false,
"description": "Case-insensitive substring match against the MCP server `name`. Matches nothing else — not ids, descriptions, or content. `%` and `_` are matched literally. Must be non-empty; omit the parameter instead of sending a blank one.",
"schema": { "type": "string", "minLength": 1, "maxLength": 200 }
},
{
"name": "sortBy",
"in": "query",
"required": false,
"description": "Field to sort by.",
"schema": {
"type": "string",
"enum": ["name", "createdAt", "updatedAt"],
"default": "createdAt"
}
},
{
"name": "sortOrder",
"in": "query",
"required": false,
"description": "Sort direction.",
"schema": { "type": "string", "enum": ["asc", "desc"], "default": "desc" }
}
],
"responses": {
"200": {
"description": "MCP servers registered in the workspace.",
Expand Down Expand Up @@ -405,7 +432,34 @@
"source": "curl \\\n \"https://www.sim.ai/api/v2/skills?workspaceId=YOUR_WORKSPACE_ID\" \\\n -H \"X-API-Key: YOUR_API_KEY\""
}
],
"parameters": [{ "$ref": "#/components/parameters/WorkspaceIdQuery" }],
"parameters": [
{ "$ref": "#/components/parameters/WorkspaceIdQuery" },
{
"name": "search",
"in": "query",
"required": false,
"description": "Case-insensitive substring match against the skill `name`. Matches nothing else — not ids, descriptions, or content. `%` and `_` are matched literally. Must be non-empty; omit the parameter instead of sending a blank one.",
"schema": { "type": "string", "minLength": 1, "maxLength": 200 }
},
{
"name": "sortBy",
"in": "query",
"required": false,
"description": "Field to sort by. Built-in skills have no stored timestamps and sort as if created at the Unix epoch.",
"schema": {
"type": "string",
"enum": ["name", "createdAt", "updatedAt"],
"default": "createdAt"
}
},
{
"name": "sortOrder",
"in": "query",
"required": false,
"description": "Sort direction.",
"schema": { "type": "string", "enum": ["asc", "desc"], "default": "desc" }
}
],
"responses": {
"200": {
"description": "Skills available in the workspace.",
Expand Down Expand Up @@ -706,7 +760,34 @@
"source": "curl \\\n \"https://www.sim.ai/api/v2/custom-tools?workspaceId=YOUR_WORKSPACE_ID\" \\\n -H \"X-API-Key: YOUR_API_KEY\""
}
],
"parameters": [{ "$ref": "#/components/parameters/WorkspaceIdQuery" }],
"parameters": [
{ "$ref": "#/components/parameters/WorkspaceIdQuery" },
{
"name": "search",
"in": "query",
"required": false,
"description": "Case-insensitive substring match against the custom tool `title`. Matches nothing else — not ids, descriptions, or content. `%` and `_` are matched literally. Must be non-empty; omit the parameter instead of sending a blank one.",
"schema": { "type": "string", "minLength": 1, "maxLength": 200 }
},
{
"name": "sortBy",
"in": "query",
"required": false,
"description": "Field to sort by.",
"schema": {
"type": "string",
"enum": ["title", "createdAt", "updatedAt"],
"default": "createdAt"
}
},
{
"name": "sortOrder",
"in": "query",
"required": false,
"description": "Sort direction.",
"schema": { "type": "string", "enum": ["asc", "desc"], "default": "desc" }
}
],
"responses": {
"200": {
"description": "Custom tools defined in the workspace.",
Expand Down Expand Up @@ -1057,6 +1138,31 @@
"required": false,
"description": "`active` (default) lists live folders; `archived` lists Recently Deleted.",
"schema": { "type": "string", "enum": ["active", "archived"], "default": "active" }
},
{
"name": "search",
"in": "query",
"required": false,
"description": "Case-insensitive substring match against the folder `name`. Matches nothing else — not ids, descriptions, or content. `%` and `_` are matched literally. Must be non-empty; omit the parameter instead of sending a blank one.",
"schema": { "type": "string", "minLength": 1, "maxLength": 200 }
},
{
"name": "sortBy",
"in": "query",
"required": false,
"description": "Field to sort by. `position` is the tree's own manual arrangement, which is the default order.",
"schema": {
"type": "string",
"enum": ["position", "name", "createdAt", "updatedAt"],
"default": "position"
}
},
{
"name": "sortOrder",
"in": "query",
"required": false,
"description": "Sort direction.",
"schema": { "type": "string", "enum": ["asc", "desc"], "default": "asc" }
}
],
"responses": {
Expand Down Expand Up @@ -1407,6 +1513,31 @@
"required": false,
"description": "Only return credentials for this integration.",
"schema": { "type": "string", "minLength": 1, "example": "slack" }
},
{
"name": "search",
"in": "query",
"required": false,
"description": "Case-insensitive substring match against the credential `displayName`. Matches nothing else — not ids, descriptions, or content. `%` and `_` are matched literally. Must be non-empty; omit the parameter instead of sending a blank one.",
"schema": { "type": "string", "minLength": 1, "maxLength": 200 }
},
{
"name": "sortBy",
"in": "query",
"required": false,
"description": "Field to sort by.",
"schema": {
"type": "string",
"enum": ["displayName", "createdAt", "updatedAt"],
"default": "createdAt"
}
},
{
"name": "sortOrder",
"in": "query",
"required": false,
"description": "Sort direction.",
"schema": { "type": "string", "enum": ["asc", "desc"], "default": "desc" }
}
],
"responses": {
Expand Down
32 changes: 32 additions & 0 deletions apps/docs/openapi-v2-tables.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,38 @@
"parameters": [
{
"$ref": "#/components/parameters/WorkspaceIdQuery"
},
{
"name": "folderId",
"in": "query",
"required": false,
"description": "Restrict the list to one folder. Omit to list every table in the workspace.",
"schema": { "type": "string", "minLength": 1 }
},
{
"name": "search",
"in": "query",
"required": false,
"description": "Case-insensitive substring match against the table `name`. Matches nothing else — not ids, descriptions, or content. `%` and `_` are matched literally. Must be non-empty; omit the parameter instead of sending a blank one.",
"schema": { "type": "string", "minLength": 1, "maxLength": 200 }
},
{
"name": "sortBy",
"in": "query",
"required": false,
"description": "Field to sort by.",
"schema": {
"type": "string",
"enum": ["name", "createdAt", "updatedAt"],
"default": "createdAt"
}
},
{
"name": "sortOrder",
"in": "query",
"required": false,
"description": "Sort direction.",
"schema": { "type": "string", "enum": ["asc", "desc"], "default": "asc" }
}
],
"responses": {
Expand Down
25 changes: 25 additions & 0 deletions apps/docs/openapi-v2-workflows.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,31 @@
"schema": {
"type": "string"
}
},
{
"name": "search",
"in": "query",
"required": false,
"description": "Case-insensitive substring match against the workflow `name`. Matches nothing else — not ids, descriptions, or content. `%` and `_` are matched literally. Must be non-empty; omit the parameter instead of sending a blank one.",
"schema": { "type": "string", "minLength": 1, "maxLength": 200 }
},
{
"name": "sortBy",
"in": "query",
"required": false,
"description": "Field to sort by. `position` is the workspace's own manual arrangement of its workflows, which is the default order. The cursor is a keyset over the active sort, so it carries the sort it was minted under. Replaying a cursor after changing `sortBy` or `sortOrder` returns `400`; restart pagination without a cursor instead.",
"schema": {
"type": "string",
"enum": ["position", "name", "createdAt", "updatedAt", "runCount"],
"default": "position"
}
},
{
"name": "sortOrder",
"in": "query",
"required": false,
"description": "Sort direction. The cursor is a keyset over the active sort, so it carries the sort it was minted under. Replaying a cursor after changing `sortBy` or `sortOrder` returns `400`; restart pagination without a cursor instead.",
"schema": { "type": "string", "enum": ["asc", "desc"], "default": "asc" }
}
],
"responses": {
Expand Down
27 changes: 27 additions & 0 deletions apps/sim/app/api/v2/credentials/route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,33 @@ describe('GET /api/v2/credentials', () => {
expect.objectContaining({ type: 'oauth', providerId: 'slack' })
)
})
it('400s on a sort field outside the enum instead of letting it reach the query', async () => {
const res = await callList(`workspaceId=${WORKSPACE_ID}&sortBy=name);--`)

expect(res.status).toBe(400)
expect((await res.json()).error.code).toBe('BAD_REQUEST')
})

it('400s on a sort direction outside the enum', async () => {
const res = await callList(`workspaceId=${WORKSPACE_ID}&sortOrder=sideways`)

expect(res.status).toBe(400)
})

it('400s on an empty search rather than treating it as unsearched', async () => {
const res = await callList(`workspaceId=${WORKSPACE_ID}&search=`)

expect(res.status).toBe(400)
})

it('forwards search and sort into the query and still terminates pagination', async () => {
const res = await callList(
`workspaceId=${WORKSPACE_ID}&search=report&sortBy=displayName&sortOrder=asc`
)

expect(res.status).toBe(200)
expect((await res.json()).nextCursor).toBeNull()
})
})

describe('POST /api/v2/credentials', () => {
Expand Down
5 changes: 4 additions & 1 deletion apps/sim/app/api/v2/credentials/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export const GET = withRouteHandler(async (request: NextRequest) => {
)
if (!parsed.success) return parsed.response

const { workspaceId, type, providerId } = parsed.data.query
const { workspaceId, type, providerId, search, sortBy, sortOrder } = parsed.data.query

const access = await resolveWorkspaceAccess(rateLimit, userId, workspaceId, 'read')
if (access) return v2WorkspaceAccessError(access)
Expand All @@ -71,6 +71,9 @@ export const GET = withRouteHandler(async (request: NextRequest) => {
workspaceAccess,
type,
providerId,
search,
sortBy,
sortOrder,
})

// The per-workspace credential set is small and bounded → a single full page.
Expand Down
Loading
Loading