Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
15 changes: 13 additions & 2 deletions apps/sim/tools/google_drive/create_folder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,19 @@ export const createFolderTool: ToolConfig<GoogleDriveToolParams, GoogleDriveUplo

outputs: {
file: {
type: 'json',
description: 'Created folder metadata including ID, name, and parent information',
type: 'object',
description: 'Created folder metadata from Google Drive',
properties: {
id: { type: 'string', description: 'Google Drive folder ID' },
name: { type: 'string', description: 'Folder name' },
mimeType: { type: 'string', description: 'MIME type (application/vnd.google-apps.folder)' },
webViewLink: { type: 'string', description: 'URL to view in browser' },
webContentLink: { type: 'string', description: 'Direct download URL' },
size: { type: 'string', description: 'Size in bytes' },
createdTime: { type: 'string', description: 'Creation time' },
modifiedTime: { type: 'string', description: 'Last modification time' },
parents: { type: 'json', description: 'Parent folder IDs' },
},
},
},
}
243 changes: 238 additions & 5 deletions apps/sim/tools/google_drive/download.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,115 @@
import { createLogger } from '@sim/logger'
import type { GoogleDriveDownloadResponse, GoogleDriveToolParams } from '@/tools/google_drive/types'
import type {
GoogleDriveDownloadResponse,
GoogleDriveFile,
GoogleDriveRevision,
GoogleDriveToolParams,
} from '@/tools/google_drive/types'
import { DEFAULT_EXPORT_FORMATS, GOOGLE_WORKSPACE_MIME_TYPES } from '@/tools/google_drive/utils'
import type { ToolConfig } from '@/tools/types'

const logger = createLogger('GoogleDriveDownloadTool')

// All available file metadata fields from Google Drive API v3
const ALL_FILE_FIELDS = [
// Basic Info
'id',
'name',
'mimeType',
'kind',
'description',
'originalFilename',
'fullFileExtension',
'fileExtension',
// Ownership & Sharing
'owners',
'permissions',
'permissionIds',
'shared',
'ownedByMe',
'writersCanShare',
'viewersCanCopyContent',
'copyRequiresWriterPermission',
'sharingUser',
// Labels/Tags
'starred',
'trashed',
'explicitlyTrashed',
'properties',
'appProperties',
'folderColorRgb',
// Timestamps
'createdTime',
'modifiedTime',
'modifiedByMeTime',
'viewedByMeTime',
'sharedWithMeTime',
'trashedTime',
// User Info
'lastModifyingUser',
'trashingUser',
'viewedByMe',
'modifiedByMe',
// Links
'webViewLink',
'webContentLink',
'iconLink',
'thumbnailLink',
'exportLinks',
// Size & Storage
'size',
'quotaBytesUsed',
// Checksums
'md5Checksum',
'sha1Checksum',
'sha256Checksum',
// Hierarchy & Location
'parents',
'spaces',
'driveId',
'teamDriveId',
// Capabilities
'capabilities',
// Versions
'version',
'headRevisionId',
// Media Metadata
'hasThumbnail',
'thumbnailVersion',
'imageMediaMetadata',
'videoMediaMetadata',
'contentHints',
// Other
'isAppAuthorized',
'contentRestrictions',
'resourceKey',
'shortcutDetails',
'linkShareMetadata',
].join(',')
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the ALL_FILE_FIELDS constant is missing the 'labels' and 'labelInfo' fields that are defined in the GoogleDriveFile type (types.ts lines 210-213). This means these metadata fields will never be fetched from the Google Drive API even though the type system expects them.

Suggested change
const ALL_FILE_FIELDS = [
// Basic Info
'id',
'name',
'mimeType',
'kind',
'description',
'originalFilename',
'fullFileExtension',
'fileExtension',
// Ownership & Sharing
'owners',
'permissions',
'permissionIds',
'shared',
'ownedByMe',
'writersCanShare',
'viewersCanCopyContent',
'copyRequiresWriterPermission',
'sharingUser',
// Labels/Tags
'starred',
'trashed',
'explicitlyTrashed',
'properties',
'appProperties',
'folderColorRgb',
// Timestamps
'createdTime',
'modifiedTime',
'modifiedByMeTime',
'viewedByMeTime',
'sharedWithMeTime',
'trashedTime',
// User Info
'lastModifyingUser',
'trashingUser',
'viewedByMe',
'modifiedByMe',
// Links
'webViewLink',
'webContentLink',
'iconLink',
'thumbnailLink',
'exportLinks',
// Size & Storage
'size',
'quotaBytesUsed',
// Checksums
'md5Checksum',
'sha1Checksum',
'sha256Checksum',
// Hierarchy & Location
'parents',
'spaces',
'driveId',
'teamDriveId',
// Capabilities
'capabilities',
// Versions
'version',
'headRevisionId',
// Media Metadata
'hasThumbnail',
'thumbnailVersion',
'imageMediaMetadata',
'videoMediaMetadata',
'contentHints',
// Other
'isAppAuthorized',
'contentRestrictions',
'resourceKey',
'shortcutDetails',
'linkShareMetadata',
].join(',')
const ALL_FILE_FIELDS = [
// Basic Info
'id',
'name',
'mimeType',
'kind',
'description',
'originalFilename',
'fullFileExtension',
'fileExtension',
// Ownership & Sharing
'owners',
'permissions',
'permissionIds',
'shared',
'ownedByMe',
'writersCanShare',
'viewersCanCopyContent',
'copyRequiresWriterPermission',
'sharingUser',
// Labels/Tags
'labels',
'labelInfo',
'starred',
'trashed',
'explicitlyTrashed',
'properties',
'appProperties',
'folderColorRgb',
// Timestamps
'createdTime',
'modifiedTime',
'modifiedByMeTime',
'viewedByMeTime',
'sharedWithMeTime',
'trashedTime',
// User Info
'lastModifyingUser',
'trashingUser',
'viewedByMe',
'modifiedByMe',
// Links
'webViewLink',
'webContentLink',
'iconLink',
'thumbnailLink',
'exportLinks',
// Size & Storage
'size',
'quotaBytesUsed',
// Checksums
'md5Checksum',
'sha1Checksum',
'sha256Checksum',
// Hierarchy & Location
'parents',
'spaces',
'driveId',
'teamDriveId',
// Capabilities
'capabilities',
// Versions
'version',
'headRevisionId',
// Media Metadata
'hasThumbnail',
'thumbnailVersion',
'imageMediaMetadata',
'videoMediaMetadata',
'contentHints',
// Other
'isAppAuthorized',
'contentRestrictions',
'resourceKey',
'shortcutDetails',
'linkShareMetadata',
].join(',')
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/sim/tools/google_drive/download.ts
Line: 14:88

Comment:
the ALL_FILE_FIELDS constant is missing the 'labels' and 'labelInfo' fields that are defined in the GoogleDriveFile type (types.ts lines 210-213). This means these metadata fields will never be fetched from the Google Drive API even though the type system expects them.

```suggestion
const ALL_FILE_FIELDS = [
  // Basic Info
  'id',
  'name',
  'mimeType',
  'kind',
  'description',
  'originalFilename',
  'fullFileExtension',
  'fileExtension',
  // Ownership & Sharing
  'owners',
  'permissions',
  'permissionIds',
  'shared',
  'ownedByMe',
  'writersCanShare',
  'viewersCanCopyContent',
  'copyRequiresWriterPermission',
  'sharingUser',
  // Labels/Tags
  'labels',
  'labelInfo',
  'starred',
  'trashed',
  'explicitlyTrashed',
  'properties',
  'appProperties',
  'folderColorRgb',
  // Timestamps
  'createdTime',
  'modifiedTime',
  'modifiedByMeTime',
  'viewedByMeTime',
  'sharedWithMeTime',
  'trashedTime',
  // User Info
  'lastModifyingUser',
  'trashingUser',
  'viewedByMe',
  'modifiedByMe',
  // Links
  'webViewLink',
  'webContentLink',
  'iconLink',
  'thumbnailLink',
  'exportLinks',
  // Size & Storage
  'size',
  'quotaBytesUsed',
  // Checksums
  'md5Checksum',
  'sha1Checksum',
  'sha256Checksum',
  // Hierarchy & Location
  'parents',
  'spaces',
  'driveId',
  'teamDriveId',
  // Capabilities
  'capabilities',
  // Versions
  'version',
  'headRevisionId',
  // Media Metadata
  'hasThumbnail',
  'thumbnailVersion',
  'imageMediaMetadata',
  'videoMediaMetadata',
  'contentHints',
  // Other
  'isAppAuthorized',
  'contentRestrictions',
  'resourceKey',
  'shortcutDetails',
  'linkShareMetadata',
].join(',')
```

How can I resolve this? If you propose a fix, please make it concise.


// All revision fields
const ALL_REVISION_FIELDS = [
'id',
'mimeType',
'modifiedTime',
'keepForever',
'published',
'publishAuto',
'publishedLink',
'publishedOutsideDomain',
'lastModifyingUser',
'originalFilename',
'md5Checksum',
'size',
'exportLinks',
'kind',
].join(',')
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the ALL_FILE_FIELDS and ALL_REVISION_FIELDS constants are duplicated across 4 files (download.ts, get_content.ts, list.ts, and upload.ts). This creates a maintenance burden where any field addition/removal must be synchronized across all files.

These constants should be moved to utils.ts (where other shared constants like GOOGLE_WORKSPACE_MIME_TYPES already live) and imported by the files that need them. This would ensure consistency and make updates easier.

Example:

// In utils.ts
export const ALL_FILE_FIELDS = [
  'id', 'name', 'mimeType', 'kind', 'description', 'originalFilename',
  'fullFileExtension', 'fileExtension', 'owners', 'permissions', 
  // ... all other fields including 'labels' and 'labelInfo'
].join(',')

export const ALL_REVISION_FIELDS = [
  'id', 'mimeType', 'modifiedTime', 'keepForever', 'published',
  // ... all other fields
].join(',')

// Then in each tool file:
import { ALL_FILE_FIELDS, ALL_REVISION_FIELDS } from './utils'

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/sim/tools/google_drive/download.ts
Line: 14:106

Comment:
the ALL_FILE_FIELDS and ALL_REVISION_FIELDS constants are duplicated across 4 files (download.ts, get_content.ts, list.ts, and upload.ts). This creates a maintenance burden where any field addition/removal must be synchronized across all files.

These constants should be moved to utils.ts (where other shared constants like GOOGLE_WORKSPACE_MIME_TYPES already live) and imported by the files that need them. This would ensure consistency and make updates easier.

Example:
```typescript
// In utils.ts
export const ALL_FILE_FIELDS = [
  'id', 'name', 'mimeType', 'kind', 'description', 'originalFilename',
  'fullFileExtension', 'fileExtension', 'owners', 'permissions', 
  // ... all other fields including 'labels' and 'labelInfo'
].join(',')

export const ALL_REVISION_FIELDS = [
  'id', 'mimeType', 'modifiedTime', 'keepForever', 'published',
  // ... all other fields
].join(',')

// Then in each tool file:
import { ALL_FILE_FIELDS, ALL_REVISION_FIELDS } from './utils'
```

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.


export const downloadTool: ToolConfig<GoogleDriveToolParams, GoogleDriveDownloadResponse> = {
id: 'google_drive_download',
name: 'Download File from Google Drive',
description: 'Download a file from Google Drive (exports Google Workspace files automatically)',
description:
'Download a file from Google Drive with complete metadata (exports Google Workspace files automatically)',
version: '1.0',

oauth: {
Expand Down Expand Up @@ -41,11 +142,17 @@ export const downloadTool: ToolConfig<GoogleDriveToolParams, GoogleDriveDownload
visibility: 'user-only',
description: 'Optional filename override',
},
includeRevisions: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether to include revision history in the metadata (default: true)',
},
},

request: {
url: (params) =>
`https://www.googleapis.com/drive/v3/files/${params.fileId}?fields=id,name,mimeType&supportsAllDrives=true`,
`https://www.googleapis.com/drive/v3/files/${params.fileId}?fields=${ALL_FILE_FIELDS}&supportsAllDrives=true`,
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.accessToken}`,
Expand All @@ -64,7 +171,7 @@ export const downloadTool: ToolConfig<GoogleDriveToolParams, GoogleDriveDownload
throw new Error(errorDetails.error?.message || 'Failed to get file metadata')
}

const metadata = await response.json()
const metadata: GoogleDriveFile = await response.json()
const fileId = metadata.id
const mimeType = metadata.mimeType
const authHeader = `Bearer ${params?.accessToken || ''}`
Expand Down Expand Up @@ -132,13 +239,49 @@ export const downloadTool: ToolConfig<GoogleDriveToolParams, GoogleDriveDownload
fileBuffer = Buffer.from(arrayBuffer)
}

// Fetch revisions if requested (default: true)
const includeRevisions = params?.includeRevisions !== false
if (includeRevisions) {
try {
const revisionsResponse = await fetch(
`https://www.googleapis.com/drive/v3/files/${fileId}/revisions?fields=revisions(${ALL_REVISION_FIELDS})&pageSize=100`,
{
headers: {
Authorization: authHeader,
},
}
)

if (revisionsResponse.ok) {
const revisionsData = await revisionsResponse.json()
metadata.revisions = revisionsData.revisions as GoogleDriveRevision[]
logger.info('Fetched file revisions', {
fileId,
revisionCount: metadata.revisions?.length || 0,
})
Comment on lines +157 to +172
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the revision fetching logic only retrieves the first 100 revisions (pageSize=100) but does not handle pagination. If a file has more than 100 revisions, only the first 100 will be returned and subsequent revisions will be silently omitted without any indication to the user.

The Google Drive API returns a nextPageToken field when there are more results, but this code doesn't check for it or fetch additional pages.

Consider either:

  1. Documenting in the output schema that only the first 100 revisions are returned
  2. Implementing pagination to fetch all revisions
  3. Making the pageSize configurable via a parameter
  4. Logging a warning when there are more revisions available

Note: The same issue exists in get_content.ts at line 145.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/sim/tools/google_drive/download.ts
Line: 156:171

Comment:
the revision fetching logic only retrieves the first 100 revisions (pageSize=100) but does not handle pagination. If a file has more than 100 revisions, only the first 100 will be returned and subsequent revisions will be silently omitted without any indication to the user.

The Google Drive API returns a `nextPageToken` field when there are more results, but this code doesn't check for it or fetch additional pages.

Consider either:
1. Documenting in the output schema that only the first 100 revisions are returned
2. Implementing pagination to fetch all revisions
3. Making the pageSize configurable via a parameter
4. Logging a warning when there are more revisions available

Note: The same issue exists in `get_content.ts` at line 145.

How can I resolve this? If you propose a fix, please make it concise.

} else {
logger.warn('Failed to fetch revisions, continuing without them', {
status: revisionsResponse.status,
statusText: revisionsResponse.statusText,
})
}
} catch (revisionError: any) {
logger.warn('Error fetching revisions, continuing without them', {
error: revisionError.message,
})
}
}
Comment on lines +153 to +188
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revision history is fetched for all files without first checking if the user has permission to read revisions. The metadata already includes a capabilities.canReadRevisions field that indicates whether the current user can access revision history. Making unnecessary API calls when the user doesn't have permission wastes API quota and adds latency.

Consider checking the capability first:

Suggested change
const includeRevisions = params?.includeRevisions !== false
if (includeRevisions) {
try {
const revisionsResponse = await fetch(
`https://www.googleapis.com/drive/v3/files/${fileId}/revisions?fields=revisions(${ALL_REVISION_FIELDS})&pageSize=100`,
{
headers: {
Authorization: authHeader,
},
}
)
if (revisionsResponse.ok) {
const revisionsData = await revisionsResponse.json()
metadata.revisions = revisionsData.revisions as GoogleDriveRevision[]
logger.info('Fetched file revisions', {
fileId,
revisionCount: metadata.revisions?.length || 0,
})
} else {
logger.warn('Failed to fetch revisions, continuing without them', {
status: revisionsResponse.status,
statusText: revisionsResponse.statusText,
})
}
} catch (revisionError: any) {
logger.warn('Error fetching revisions, continuing without them', {
error: revisionError.message,
})
}
}
const includeRevisions = params?.includeRevisions !== false
if (includeRevisions && metadata.capabilities?.canReadRevisions) {
try {
const revisionsResponse = await fetch(
`https://www.googleapis.com/drive/v3/files/${fileId}/revisions?fields=revisions(${ALL_REVISION_FIELDS})&pageSize=100`,
{
headers: {
Authorization: authHeader,
},
}
)
if (revisionsResponse.ok) {
const revisionsData = await revisionsResponse.json()
metadata.revisions = revisionsData.revisions as GoogleDriveRevision[]
logger.info('Fetched file revisions', {
fileId,
revisionCount: metadata.revisions?.length || 0,
})
} else {
logger.warn('Failed to fetch revisions, continuing without them', {
status: revisionsResponse.status,
statusText: revisionsResponse.statusText,
})
}
} catch (revisionError: any) {
logger.warn('Error fetching revisions, continuing without them', {
error: revisionError.message,
})
}
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/sim/tools/google_drive/download.ts
Line: 152:182

Comment:
revision history is fetched for all files without first checking if the user has permission to read revisions. The metadata already includes a `capabilities.canReadRevisions` field that indicates whether the current user can access revision history. Making unnecessary API calls when the user doesn't have permission wastes API quota and adds latency.

Consider checking the capability first:

```suggestion
      const includeRevisions = params?.includeRevisions !== false
      if (includeRevisions && metadata.capabilities?.canReadRevisions) {
        try {
          const revisionsResponse = await fetch(
            `https://www.googleapis.com/drive/v3/files/${fileId}/revisions?fields=revisions(${ALL_REVISION_FIELDS})&pageSize=100`,
            {
              headers: {
                Authorization: authHeader,
              },
            }
          )

          if (revisionsResponse.ok) {
            const revisionsData = await revisionsResponse.json()
            metadata.revisions = revisionsData.revisions as GoogleDriveRevision[]
            logger.info('Fetched file revisions', {
              fileId,
              revisionCount: metadata.revisions?.length || 0,
            })
          } else {
            logger.warn('Failed to fetch revisions, continuing without them', {
              status: revisionsResponse.status,
              statusText: revisionsResponse.statusText,
            })
          }
        } catch (revisionError: any) {
          logger.warn('Error fetching revisions, continuing without them', {
            error: revisionError.message,
          })
        }
      }
```

How can I resolve this? If you propose a fix, please make it concise.


const resolvedName = params?.fileName || metadata.name || 'download'

logger.info('File downloaded successfully', {
fileId,
name: resolvedName,
size: fileBuffer.length,
mimeType: finalMimeType,
hasOwners: !!metadata.owners?.length,
hasPermissions: !!metadata.permissions?.length,
hasRevisions: !!metadata.revisions?.length,
})

return {
Expand All @@ -150,6 +293,7 @@ export const downloadTool: ToolConfig<GoogleDriveToolParams, GoogleDriveDownload
data: fileBuffer,
size: fileBuffer.length,
},
metadata,
},
}
} catch (error: any) {
Expand All @@ -162,6 +306,95 @@ export const downloadTool: ToolConfig<GoogleDriveToolParams, GoogleDriveDownload
},

outputs: {
file: { type: 'file', description: 'Downloaded file stored in execution files' },
file: {
type: 'object',
description: 'Downloaded file stored in execution files',
properties: {
id: { type: 'string', description: 'Unique file identifier' },
name: { type: 'string', description: 'File name' },
size: { type: 'number', description: 'File size in bytes' },
type: { type: 'string', description: 'MIME type of the file' },
url: { type: 'string', description: 'Signed URL to access the file' },
key: { type: 'string', description: 'Storage key for the file' },
context: { type: 'string', description: 'Context where file is stored' },
},
Comment on lines +226 to +234
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the output schema for the file field does not match what is actually returned by the code.

The schema promises these properties:

  • id, name, size, type, url, key, context

But the actual return value (lines 204-209) only contains:

  • name, mimeType, data, size

The schema is missing the data property (which contains the actual Buffer) and includes properties (id, url, key, context) that are never set. The schema also uses type instead of mimeType.

This mismatch could cause issues for consumers relying on the schema definition.

Suggested change
file: {
type: 'object',
description: 'Downloaded file stored in execution files',
properties: {
id: { type: 'string', description: 'Unique file identifier' },
name: { type: 'string', description: 'File name' },
size: { type: 'number', description: 'File size in bytes' },
type: { type: 'string', description: 'MIME type of the file' },
url: { type: 'string', description: 'Signed URL to access the file' },
key: { type: 'string', description: 'Storage key for the file' },
context: { type: 'string', description: 'Context where file is stored' },
},
file: {
type: 'object',
description: 'Downloaded file stored in execution files',
properties: {
name: { type: 'string', description: 'File name' },
mimeType: { type: 'string', description: 'MIME type of the file' },
data: { type: 'buffer', description: 'File content as Buffer' },
size: { type: 'number', description: 'File size in bytes' },
},
},
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/sim/tools/google_drive/download.ts
Line: 223:234

Comment:
the output schema for the `file` field does not match what is actually returned by the code. 

The schema promises these properties:
- `id`, `name`, `size`, `type`, `url`, `key`, `context`

But the actual return value (lines 204-209) only contains:
- `name`, `mimeType`, `data`, `size`

The schema is missing the `data` property (which contains the actual Buffer) and includes properties (`id`, `url`, `key`, `context`) that are never set. The schema also uses `type` instead of `mimeType`.

This mismatch could cause issues for consumers relying on the schema definition.

```suggestion
    file: {
      type: 'object',
      description: 'Downloaded file stored in execution files',
      properties: {
        name: { type: 'string', description: 'File name' },
        mimeType: { type: 'string', description: 'MIME type of the file' },
        data: { type: 'buffer', description: 'File content as Buffer' },
        size: { type: 'number', description: 'File size in bytes' },
      },
    },
```

How can I resolve this? If you propose a fix, please make it concise.

},
metadata: {
type: 'object',
description: 'Complete file metadata from Google Drive',
properties: {
// Basic Info
id: { type: 'string', description: 'Google Drive file ID' },
name: { type: 'string', description: 'File name' },
mimeType: { type: 'string', description: 'MIME type' },
kind: { type: 'string', description: 'Resource type identifier' },
description: { type: 'string', description: 'File description' },
originalFilename: { type: 'string', description: 'Original uploaded filename' },
fullFileExtension: { type: 'string', description: 'Full file extension' },
fileExtension: { type: 'string', description: 'File extension' },
// Ownership & Sharing
owners: { type: 'json', description: 'List of file owners' },
permissions: { type: 'json', description: 'File permissions' },
permissionIds: { type: 'json', description: 'Permission IDs' },
shared: { type: 'boolean', description: 'Whether file is shared' },
ownedByMe: { type: 'boolean', description: 'Whether owned by current user' },
writersCanShare: { type: 'boolean', description: 'Whether writers can share' },
viewersCanCopyContent: { type: 'boolean', description: 'Whether viewers can copy' },
copyRequiresWriterPermission: {
type: 'boolean',
description: 'Whether copy requires writer permission',
},
sharingUser: { type: 'json', description: 'User who shared the file' },
// Labels/Tags
starred: { type: 'boolean', description: 'Whether file is starred' },
trashed: { type: 'boolean', description: 'Whether file is in trash' },
explicitlyTrashed: { type: 'boolean', description: 'Whether explicitly trashed' },
properties: { type: 'json', description: 'Custom properties' },
appProperties: { type: 'json', description: 'App-specific properties' },
// Timestamps
createdTime: { type: 'string', description: 'File creation time' },
modifiedTime: { type: 'string', description: 'Last modification time' },
modifiedByMeTime: { type: 'string', description: 'When modified by current user' },
viewedByMeTime: { type: 'string', description: 'When last viewed by current user' },
sharedWithMeTime: { type: 'string', description: 'When shared with current user' },
// User Info
lastModifyingUser: { type: 'json', description: 'User who last modified the file' },
viewedByMe: { type: 'boolean', description: 'Whether viewed by current user' },
modifiedByMe: { type: 'boolean', description: 'Whether modified by current user' },
// Links
webViewLink: { type: 'string', description: 'URL to view in browser' },
webContentLink: { type: 'string', description: 'Direct download URL' },
iconLink: { type: 'string', description: 'URL to file icon' },
thumbnailLink: { type: 'string', description: 'URL to thumbnail' },
exportLinks: { type: 'json', description: 'Export format links' },
// Size & Storage
size: { type: 'string', description: 'File size in bytes' },
quotaBytesUsed: { type: 'string', description: 'Storage quota used' },
// Checksums
md5Checksum: { type: 'string', description: 'MD5 hash' },
sha1Checksum: { type: 'string', description: 'SHA-1 hash' },
sha256Checksum: { type: 'string', description: 'SHA-256 hash' },
// Hierarchy & Location
parents: { type: 'json', description: 'Parent folder IDs' },
spaces: { type: 'json', description: 'Spaces containing file' },
driveId: { type: 'string', description: 'Shared drive ID' },
// Capabilities
capabilities: { type: 'json', description: 'User capabilities on file' },
// Versions
version: { type: 'string', description: 'Version number' },
headRevisionId: { type: 'string', description: 'Head revision ID' },
// Media Metadata
hasThumbnail: { type: 'boolean', description: 'Whether has thumbnail' },
thumbnailVersion: { type: 'string', description: 'Thumbnail version' },
imageMediaMetadata: { type: 'json', description: 'Image-specific metadata' },
videoMediaMetadata: { type: 'json', description: 'Video-specific metadata' },
// Other
isAppAuthorized: { type: 'boolean', description: 'Whether created by requesting app' },
contentRestrictions: { type: 'json', description: 'Content restrictions' },
linkShareMetadata: { type: 'json', description: 'Link share metadata' },
// Revisions
revisions: { type: 'json', description: 'File revision history' },
},
},
},
}
Loading
Loading