Skip to content

Commit 861dc29

Browse files
authored
fix: exclude bucket type in response for storage-py < 0.12.1 (#744)
* fix: exclude bucket type in response for storage-py < 0.12.1 * generalize python user agent check and move to limits.ts
1 parent 00058b1 commit 861dc29

File tree

3 files changed

+59
-1
lines changed

3 files changed

+59
-1
lines changed

src/http/routes/bucket/getAllBuckets.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { createDefaultSchema } from '../../routes-helper'
44
import { AuthenticatedRequest } from '../../types'
55
import { bucketSchema } from '@storage/schemas'
66
import { ROUTE_OPERATIONS } from '../operations'
7+
import { isPythonClientBefore } from '@storage/limits'
78

89
const successResponseSchema = {
910
type: 'array',
@@ -58,8 +59,15 @@ export default async function routes(fastify: FastifyInstance) {
5859
},
5960
async (request, response) => {
6061
const { limit, offset, sortColumn, sortOrder, search } = request.query
62+
63+
// Detects user agents that support the type property in bucket list response
64+
// storage-py < v0.12.1 throws fatal error if type property is present
65+
// type property added in v0.12.1 -- https://github.com/supabase/storage-py/releases/tag/v0.12.1
66+
const includeBucketType = !isPythonClientBefore(request.headers['user-agent'] || '', '0.12.1')
67+
6168
const results = await request.storage.listBuckets(
62-
'id, name, type, public, owner, created_at, updated_at, file_size_limit, allowed_mime_types',
69+
'id, name, public, owner, created_at, updated_at, file_size_limit, allowed_mime_types' +
70+
(includeBucketType ? ', type' : ''),
6371
{ limit, offset, sortColumn, sortOrder, search }
6472
)
6573

src/storage/limits.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,3 +138,25 @@ export function isUuid(value: string) {
138138
export function isEmptyFolder(object: string) {
139139
return object.endsWith('.emptyFolderPlaceholder')
140140
}
141+
142+
/**
143+
* Checks if the client is supabase-py and before the specified version
144+
*
145+
* @param userAgent user agent header string
146+
* @param version semver to check against, must be in format '0.0.0'
147+
*/
148+
export function isPythonClientBefore(userAgent: string, version: string): boolean {
149+
const [minMajor, minMinor, minPatch] = version.split('.').map(Number)
150+
const match = userAgent.match(/supabase-py\/storage3 v(\d+)\.(\d+)\.(\d+)/i)
151+
if (!match) {
152+
return false
153+
}
154+
155+
const [major, minor, patch] = match.slice(1).map(Number)
156+
157+
if (major < minMajor) return true
158+
if (major > minMajor) return false
159+
if (minor < minMinor) return true
160+
if (minor > minMinor) return false
161+
return patch < minPatch
162+
}

src/test/bucket.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,39 @@ describe('testing GET all buckets', () => {
118118
expect(responseJSON[0]).toMatchObject({
119119
id: expect.any(String),
120120
name: expect.any(String),
121+
type: expect.any(String),
121122
public: expect.any(Boolean),
122123
file_size_limit: null,
123124
allowed_mime_types: null,
124125
})
125126
})
126127

128+
for (const [userAgent, shouldIncludeType] of [
129+
['Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/138.0.0.0 Safari/537.36', true],
130+
['supabase-py/storage3 v0.11.9', false],
131+
['supabase-py/storage3 v0.12.0', false],
132+
['supabase-py/storage3 v0.12.1', true],
133+
['supabase-py/storage3 v0.12.2', true],
134+
['supabase-py/storage3 v0.13.0', true],
135+
['supabase-py/storage3 v1.0.0', true],
136+
]) {
137+
test(`Should ${
138+
shouldIncludeType ? '' : 'NOT '
139+
}include type for ${userAgent} client`, async () => {
140+
const response = await appInstance.inject({
141+
method: 'GET',
142+
url: `/bucket`,
143+
headers: {
144+
authorization: `Bearer ${process.env.AUTHENTICATED_KEY}`,
145+
'user-agent': userAgent as string,
146+
},
147+
})
148+
expect(response.statusCode).toBe(200)
149+
const body = response.json()
150+
expect(body[0].type).toBe(shouldIncludeType ? 'STANDARD' : undefined)
151+
})
152+
}
153+
127154
test('checking RLS: anon user is not able to get all buckets', async () => {
128155
const response = await appInstance.inject({
129156
method: 'GET',
@@ -159,6 +186,7 @@ describe('testing GET all buckets', () => {
159186
expect(responseJSON[0]).toMatchObject({
160187
id: 'bucket4',
161188
name: 'bucket4',
189+
type: expect.any(String),
162190
public: false,
163191
file_size_limit: null,
164192
allowed_mime_types: null,

0 commit comments

Comments
 (0)