Skip to content

Commit ef710e3

Browse files
paulpopusjhb-dev
andauthored
chore(storage-s3): add int tests for filename encoding (#14970)
Adds some int tests as a follow up to #14438 --------- Co-authored-by: Jens Becker <info@jhb.software>
1 parent 86855e1 commit ef710e3

6 files changed

Lines changed: 117 additions & 1 deletion

File tree

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import type { CollectionConfig } from 'payload'
2+
3+
export const MediaWithDirectAccess: CollectionConfig = {
4+
slug: 'media-with-direct-access',
5+
upload: {
6+
disableLocalStorage: true,
7+
},
8+
fields: [
9+
{
10+
name: 'alt',
11+
label: 'Alt Text',
12+
type: 'text',
13+
},
14+
],
15+
}

test/storage-s3/config.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@ import path from 'path'
66
import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js'
77
import { devUser } from '../credentials.js'
88
import { Media } from './collections/Media.js'
9+
import { MediaWithDirectAccess } from './collections/MediaWithDirectAccess.js'
910
import { MediaWithDynamicPrefix } from './collections/MediaWithDynamicPrefix.js'
1011
import { MediaWithPrefix } from './collections/MediaWithPrefix.js'
1112
import { MediaWithSignedDownloads } from './collections/MediaWithSignedDownloads.js'
1213
import { Users } from './collections/Users.js'
1314
import {
1415
mediaSlug,
16+
mediaWithDirectAccessSlug,
1517
mediaWithDynamicPrefixSlug,
1618
mediaWithPrefixSlug,
1719
mediaWithSignedDownloadsSlug,
@@ -33,7 +35,14 @@ export default buildConfigWithDefaults({
3335
baseDir: path.resolve(dirname),
3436
},
3537
},
36-
collections: [Media, MediaWithDynamicPrefix, MediaWithPrefix, MediaWithSignedDownloads, Users],
38+
collections: [
39+
Media,
40+
MediaWithDirectAccess,
41+
MediaWithDynamicPrefix,
42+
MediaWithPrefix,
43+
MediaWithSignedDownloads,
44+
Users,
45+
],
3746
onInit: async (payload) => {
3847
await payload.create({
3948
collection: 'users',
@@ -47,6 +56,9 @@ export default buildConfigWithDefaults({
4756
s3Storage({
4857
collections: {
4958
[mediaSlug]: true,
59+
[mediaWithDirectAccessSlug]: {
60+
disablePayloadAccessControl: true,
61+
},
5062
[mediaWithDynamicPrefixSlug]: true,
5163
[mediaWithPrefixSlug]: {
5264
prefix,

test/storage-s3/int.spec.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import type { NextRESTClient } from '../helpers/NextRESTClient.js'
99
import { initPayloadInt } from '../helpers/initPayloadInt.js'
1010
import {
1111
mediaSlug,
12+
mediaWithDirectAccessSlug,
1213
mediaWithDynamicPrefixSlug,
1314
mediaWithPrefixSlug,
1415
mediaWithSignedDownloadsSlug,
@@ -121,6 +122,49 @@ describe('@payloadcms/storage-s3', () => {
121122
expect(response.status).toBe(404)
122123
})
123124

125+
describe('disablePayloadAccessControl', () => {
126+
it('should return direct S3 URL with encoded filename when uploading file with spaces', async () => {
127+
const upload = await payload.create({
128+
collection: mediaWithDirectAccessSlug,
129+
data: {},
130+
filePath: path.resolve(dirname, '../uploads/image with spaces.png'),
131+
})
132+
133+
expect(upload.id).toBeTruthy()
134+
expect(upload.filename).toBe('image with spaces.png')
135+
136+
// When disablePayloadAccessControl is true, URL should point directly to S3
137+
// and the filename should be URL-encoded
138+
expect(upload.url).toContain(process.env.S3_ENDPOINT)
139+
expect(upload.url).toContain(TEST_BUCKET)
140+
expect(upload.url).toContain('image%20with%20spaces.png')
141+
142+
// Verify the file can be fetched using the URL
143+
const response = await fetch(upload.url)
144+
expect(response.status).toBe(200)
145+
expect(response.headers.get('Content-Type')).toBe('image/png')
146+
})
147+
148+
it('should return direct S3 URL without encoding issues for normal filenames', async () => {
149+
const upload = await payload.create({
150+
collection: mediaWithDirectAccessSlug,
151+
data: {},
152+
filePath: path.resolve(dirname, '../uploads/image.png'),
153+
})
154+
155+
expect(upload.id).toBeTruthy()
156+
157+
// URL should point directly to S3
158+
expect(upload.url).toContain(process.env.S3_ENDPOINT)
159+
expect(upload.url).toContain(TEST_BUCKET)
160+
expect(upload.url).toContain('image.png')
161+
162+
// Verify the file can be fetched
163+
const response = await fetch(upload.url)
164+
expect(response.status).toBe(200)
165+
})
166+
})
167+
124168
describe('R2', () => {
125169
it.todo('can upload')
126170
})

test/storage-s3/payload-types.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ export interface Config {
6868
blocks: {};
6969
collections: {
7070
media: Media;
71+
'media-with-direct-access': MediaWithDirectAccess;
7172
'media-with-dynamic-prefix': MediaWithDynamicPrefix;
7273
'media-with-prefix': MediaWithPrefix;
7374
'media-with-signed-downloads': MediaWithSignedDownload;
@@ -80,6 +81,7 @@ export interface Config {
8081
collectionsJoins: {};
8182
collectionsSelect: {
8283
media: MediaSelect<false> | MediaSelect<true>;
84+
'media-with-direct-access': MediaWithDirectAccessSelect<false> | MediaWithDirectAccessSelect<true>;
8385
'media-with-dynamic-prefix': MediaWithDynamicPrefixSelect<false> | MediaWithDynamicPrefixSelect<true>;
8486
'media-with-prefix': MediaWithPrefixSelect<false> | MediaWithPrefixSelect<true>;
8587
'media-with-signed-downloads': MediaWithSignedDownloadsSelect<false> | MediaWithSignedDownloadsSelect<true>;
@@ -92,6 +94,7 @@ export interface Config {
9294
db: {
9395
defaultIDType: string;
9496
};
97+
fallbackLocale: null;
9598
globals: {};
9699
globalsSelect: {};
97100
locale: null;
@@ -158,6 +161,25 @@ export interface Media {
158161
};
159162
};
160163
}
164+
/**
165+
* This interface was referenced by `Config`'s JSON-Schema
166+
* via the `definition` "media-with-direct-access".
167+
*/
168+
export interface MediaWithDirectAccess {
169+
id: string;
170+
alt?: string | null;
171+
updatedAt: string;
172+
createdAt: string;
173+
url?: string | null;
174+
thumbnailURL?: string | null;
175+
filename?: string | null;
176+
mimeType?: string | null;
177+
filesize?: number | null;
178+
width?: number | null;
179+
height?: number | null;
180+
focalX?: number | null;
181+
focalY?: number | null;
182+
}
161183
/**
162184
* This interface was referenced by `Config`'s JSON-Schema
163185
* via the `definition` "media-with-dynamic-prefix".
@@ -267,6 +289,10 @@ export interface PayloadLockedDocument {
267289
relationTo: 'media';
268290
value: string | Media;
269291
} | null)
292+
| ({
293+
relationTo: 'media-with-direct-access';
294+
value: string | MediaWithDirectAccess;
295+
} | null)
270296
| ({
271297
relationTo: 'media-with-dynamic-prefix';
272298
value: string | MediaWithDynamicPrefix;
@@ -367,6 +393,24 @@ export interface MediaSelect<T extends boolean = true> {
367393
};
368394
};
369395
}
396+
/**
397+
* This interface was referenced by `Config`'s JSON-Schema
398+
* via the `definition` "media-with-direct-access_select".
399+
*/
400+
export interface MediaWithDirectAccessSelect<T extends boolean = true> {
401+
alt?: T;
402+
updatedAt?: T;
403+
createdAt?: T;
404+
url?: T;
405+
thumbnailURL?: T;
406+
filename?: T;
407+
mimeType?: T;
408+
filesize?: T;
409+
width?: T;
410+
height?: T;
411+
focalX?: T;
412+
focalY?: T;
413+
}
370414
/**
371415
* This interface was referenced by `Config`'s JSON-Schema
372416
* via the `definition` "media-with-dynamic-prefix_select".

test/storage-s3/shared.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ export const mediaWithDynamicPrefixSlug = 'media-with-dynamic-prefix'
44
export const prefix = 'test-prefix'
55

66
export const mediaWithSignedDownloadsSlug = 'media-with-signed-downloads'
7+
export const mediaWithDirectAccessSlug = 'media-with-direct-access'

test/uploads/image with spaces.png

87.6 KB
Loading

0 commit comments

Comments
 (0)