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
6 changes: 3 additions & 3 deletions apps/docs/content/docs/cli/files.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ sim files describe <fileId> [options]

| Argument | Required | Description |
| --- | --- | --- |
| `fileId` | Yes | File identifier, or the file’s VFS path: `files/<folder>/<name>`. |
| `fileId` | Yes | File identifier, or the VFS path of a live file: `files/<folder>/<name>`. Pass the id when two files in a folder have names that display alike. |

</CommandTable>

Expand Down Expand Up @@ -303,7 +303,7 @@ sim files rename <fileId> [options]

| Argument | Required | Description |
| --- | --- | --- |
| `fileId` | Yes | File identifier, or the file’s VFS path: `files/<folder>/<name>`. |
| `fileId` | Yes | File identifier, or the VFS path of a live file: `files/<folder>/<name>`. Pass the id when two files in a folder have names that display alike. |

</CommandTable>

Expand Down Expand Up @@ -613,7 +613,7 @@ sim files delete <fileId> [options]

| Argument | Required | Description |
| --- | --- | --- |
| `fileId` | Yes | File identifier, or the file’s VFS path: `files/<folder>/<name>`. |
| `fileId` | Yes | File identifier, or the VFS path of a live file: `files/<folder>/<name>`. Pass the id when two files in a folder have names that display alike. |

</CommandTable>

Expand Down
6 changes: 3 additions & 3 deletions apps/docs/content/docs/cli/reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1181,7 +1181,7 @@ sim files delete <fileId> [options]

| Argument | Required | Description |
| --- | --- | --- |
| `fileId` | Yes | File identifier, or the file’s VFS path: `files/<folder>/<name>`. |
| `fileId` | Yes | File identifier, or the VFS path of a live file: `files/<folder>/<name>`. Pass the id when two files in a folder have names that display alike. |

</CommandTable>

Expand Down Expand Up @@ -1426,7 +1426,7 @@ sim files describe <fileId> [options]

| Argument | Required | Description |
| --- | --- | --- |
| `fileId` | Yes | File identifier, or the file’s VFS path: `files/<folder>/<name>`. |
| `fileId` | Yes | File identifier, or the VFS path of a live file: `files/<folder>/<name>`. Pass the id when two files in a folder have names that display alike. |

</CommandTable>

Expand Down Expand Up @@ -1591,7 +1591,7 @@ sim files rename <fileId> [options]

| Argument | Required | Description |
| --- | --- | --- |
| `fileId` | Yes | File identifier, or the file’s VFS path: `files/<folder>/<name>`. |
| `fileId` | Yes | File identifier, or the VFS path of a live file: `files/<folder>/<name>`. Pass the id when two files in a folder have names that display alike. |

</CommandTable>

Expand Down
12 changes: 6 additions & 6 deletions apps/docs/openapi-v2-files-audit.json
Original file line number Diff line number Diff line change
Expand Up @@ -2151,12 +2151,12 @@
"name": "fileId",
"in": "path",
"required": true,
"description": "File identifier, or the file’s VFS path: `files/<folder>/<name>`.",
"description": "File identifier, or the VFS path of a live file: `files/<folder>/<name>`. Pass the id when two files in a folder have names that display alike.",
"schema": {
"type": "string",
"minLength": 1,
"maxLength": 4096,
"description": "File identifier, or the file’s VFS path: `files/<folder>/<name>`."
"description": "File identifier, or the VFS path of a live file: `files/<folder>/<name>`. Pass the id when two files in a folder have names that display alike."
}
},
{
Expand Down Expand Up @@ -2237,12 +2237,12 @@
"name": "fileId",
"in": "path",
"required": true,
"description": "File identifier, or the file’s VFS path: `files/<folder>/<name>`.",
"description": "File identifier, or the VFS path of a live file: `files/<folder>/<name>`. Pass the id when two files in a folder have names that display alike.",
"schema": {
"type": "string",
"minLength": 1,
"maxLength": 4096,
"description": "File identifier, or the file’s VFS path: `files/<folder>/<name>`."
"description": "File identifier, or the VFS path of a live file: `files/<folder>/<name>`. Pass the id when two files in a folder have names that display alike."
}
}
],
Expand Down Expand Up @@ -2487,12 +2487,12 @@
"name": "fileId",
"in": "path",
"required": true,
"description": "File identifier, or the file’s VFS path: `files/<folder>/<name>`.",
"description": "File identifier, or the VFS path of a live file: `files/<folder>/<name>`. Pass the id when two files in a folder have names that display alike.",
"schema": {
"type": "string",
"minLength": 1,
"maxLength": 4096,
"description": "File identifier, or the file’s VFS path: `files/<folder>/<name>`."
"description": "File identifier, or the VFS path of a live file: `files/<folder>/<name>`. Pass the id when two files in a folder have names that display alike."
}
},
{
Expand Down
21 changes: 18 additions & 3 deletions apps/sim/app/api/v2/files/[fileId]/metadata/route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,12 @@ describe('GET /api/v2/files/[fileId]/metadata', () => {
expect((await response.json()).error.code).toBe('NOT_FOUND')
expect(readMetadata).toHaveBeenCalledWith(
expect.objectContaining({
input: { fileId: FILE_ID, assertedWorkspaceId: WORKSPACE_ID, includeDeleted: false },
input: {
fileId: FILE_ID,
acceptVfsPath: true,
assertedWorkspaceId: WORKSPACE_ID,
includeDeleted: false,
},
})
)
})
Expand All @@ -127,7 +132,12 @@ describe('GET /api/v2/files/[fileId]/metadata', () => {
expect((await response.json()).error.code).toBe('NOT_FOUND')
expect(readMetadata).toHaveBeenCalledWith(
expect.objectContaining({
input: { fileId: FILE_ID, assertedWorkspaceId: WORKSPACE_ID, includeDeleted: false },
input: {
fileId: FILE_ID,
acceptVfsPath: true,
assertedWorkspaceId: WORKSPACE_ID,
includeDeleted: false,
},
})
)
})
Expand Down Expand Up @@ -159,7 +169,12 @@ describe('GET /api/v2/files/[fileId]/metadata', () => {
expect(readMetadata).toHaveBeenCalledWith(
expect.objectContaining({
principal: auth.principal,
input: { fileId: FILE_ID, assertedWorkspaceId: WORKSPACE_ID, includeDeleted: true },
input: {
fileId: FILE_ID,
acceptVfsPath: true,
assertedWorkspaceId: WORKSPACE_ID,
includeDeleted: true,
},
})
)
})
Expand Down
1 change: 1 addition & 0 deletions apps/sim/app/api/v2/files/[fileId]/metadata/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export const GET = defineV2JsonRoute({
errorPolicy: v2FileErrorPolicies.concealResourceAuthorization,
mapInput: ({ params, query }) => ({
fileId: params.fileId,
acceptVfsPath: true,
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
Resolved
assertedWorkspaceId: query.workspaceId,
includeDeleted: query.scope === 'archived',
}),
Expand Down
2 changes: 2 additions & 0 deletions apps/sim/app/api/v2/files/[fileId]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export const PATCH = defineV2JsonRoute({
errorPolicy: v2FileErrorPolicies.concealResourceAuthorization,
mapInput: ({ params, body }) => ({
fileId: params.fileId,
acceptVfsPath: true,
assertedWorkspaceId: body.workspaceId,
name: body.name,
}),
Expand All @@ -85,6 +86,7 @@ export const DELETE = defineV2JsonRoute({
errorPolicy: v2FileErrorPolicies.concealResourceAuthorization,
mapInput: ({ params, query }) => ({
fileId: params.fileId,
acceptVfsPath: true,
assertedWorkspaceId: query.workspaceId,
}),
useCase: deleteWorkspaceFileOperation,
Expand Down
4 changes: 3 additions & 1 deletion apps/sim/lib/api/contracts/v2/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,9 @@ const v2FileIdOrPathParamsSchema = z.object({
(value) => isWorkspaceFileVfsPath(value) || workspaceFileIdSchema.safeParse(value).success,
'Invalid file id or path: pass a file id or its `files/<folder>/<name>` VFS path'
)
.describe('File identifier, or the file’s VFS path: `files/<folder>/<name>`.'),
.describe(
'File identifier, or the VFS path of a live file: `files/<folder>/<name>`. Pass the id when two files in a folder have names that display alike.'
),
})

export const v2CreateFileBodySchema = z
Expand Down
34 changes: 0 additions & 34 deletions apps/sim/lib/uploads/contexts/workspace/file-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2342,40 +2342,6 @@ async function getWorkspaceFileByExactReference(
return folderId ? getWorkspaceFileByName(workspaceId, segments.at(-1) ?? '', { folderId }) : null
}

/**
* The live workspace file whose canonical `files/…` path — as listings print it — is `vfsPath`,
* or null. Unlike {@link findWorkspaceFileRecord} there is no virtual `/content` or `/meta.json`
* suffix and no leaf-name fallback, so a mutation addressed by path lands only on the file it
* names. Two names that display alike are ambiguous and refused. Throws `VfsPathError` if
* malformed.
*/
export async function getWorkspaceFileByVfsPath(
workspaceId: string,
vfsPath: string
): Promise<WorkspaceFileRecord | null> {
const segments = decodeVfsReferenceSegments(vfsPath.slice('files/'.length))
const leaf = segments.at(-1)
if (leaf === undefined) return null
const pathKey = segments.map(normalizeVfsSegment).join('/')
const candidates = await listWorkspaceFileCandidates(workspaceId, {
ids: [],
leafNames: [normalizeDisplaySegment(leaf)],
})
const matches = candidates.filter(
(file) =>
canonicalWorkspaceFilePath({ folderPath: file.folderPath, name: file.name }).slice(
'files/'.length
) === pathKey
)
if (matches.length > 1) {
throw new OrchestrationError(
'validation',
`"${vfsPath}" names more than one file whose names display alike; pass the file id instead`
)
}
return matches[0] ?? null
}

/**
* Resolve a workspace file record from either its id or a VFS/name reference.
*
Expand Down
7 changes: 5 additions & 2 deletions apps/sim/lib/vfs/path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@ export class VfsPathError extends Error {
}
}

/** A `files/…` workspace-file VFS path. File ids are drawn from `[A-Za-z0-9_-]`, so no id is one. */
/**
* A `files/…` workspace-file VFS path naming something under the prefix. File ids are drawn from
* `[A-Za-z0-9_-]`, so no id is one.
*/
export function isWorkspaceFileVfsPath(reference: string): boolean {
return reference.startsWith('files/')
return reference.startsWith('files/') && reference.length > 'files/'.length
}

/**
Expand Down
Loading
Loading