Skip to content
Open
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
90 changes: 85 additions & 5 deletions acceptance/specs/cdn-render.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ describeAcceptance(
const client = createRestClient()
const bucketName = uniqueBucketName('cdn')
const objectKey = uniqueObjectKey('cdn')
const missingObjectKey = uniqueObjectKey('cdn-missing')

try {
await createRestBucket(bucketName, { isPublic: true })
Expand All @@ -62,20 +61,101 @@ describeAcceptance(
expect(purge.json).toMatchObject({
message: 'success',
})
} finally {
await cleanupRestResources(bucketName, [objectKey], client)
}
})

it('purges object transformations cache', async () => {
const client = createRestClient()
const bucketName = uniqueBucketName('cdn')
const objectKey = uniqueObjectKey('cdn')

const missingPurge = await client.request(
try {
await createRestBucket(bucketName, { isPublic: true })
await uploadRestObject(bucketName, objectKey, 'cdn-purge-transforms-target')

const purge = await client.request<CdnPurgeResponse>(
'DELETE',
`/cdn/${bucketName}/${encodePathSegments(missingObjectKey)}`,
`/cdn/${bucketName}/${encodePathSegments(objectKey)}?transformations=true`,
{
expectedStatus: [400, 404],
expectedStatus: 200,
token: requireServiceKey(),
}
)
expect(missingPurge.json).toBeTruthy()
expect(purge.json).toMatchObject({
message: 'success',
})
} finally {
await cleanupRestResources(bucketName, [objectKey], client)
}
})

it('purges an entire bucket cache', async () => {
const client = createRestClient()
const bucketName = uniqueBucketName('cdn')

try {
await createRestBucket(bucketName, { isPublic: true })

const purge = await client.request<CdnPurgeResponse>('DELETE', `/cdn/${bucketName}`, {
expectedStatus: 200,
token: requireServiceKey(),
})
expect(purge.json).toMatchObject({
message: 'success',
})
} finally {
await cleanupRestResources(bucketName, [], client)
}
})

it('purges bucket transformations cache', async () => {
const client = createRestClient()
const bucketName = uniqueBucketName('cdn')

try {
await createRestBucket(bucketName, { isPublic: true })

const purge = await client.request<CdnPurgeResponse>(
'DELETE',
`/cdn/${bucketName}?transformations=true`,
{
expectedStatus: 200,
token: requireServiceKey(),
}
)
expect(purge.json).toMatchObject({
message: 'success',
})
} finally {
await cleanupRestResources(bucketName, [], client)
}
})

it('purges entire tenant cache', async () => {
const client = createRestClient()

const purge = await client.request<CdnPurgeResponse>('DELETE', '/cdn/', {
expectedStatus: 200,
token: requireServiceKey(),
})
expect(purge.json).toMatchObject({
message: 'success',
})
})

it('purges tenant transformations cache', async () => {
const client = createRestClient()

const purge = await client.request<CdnPurgeResponse>('DELETE', '/cdn?transformations=true', {
expectedStatus: 200,
token: requireServiceKey(),
})
expect(purge.json).toMatchObject({
message: 'success',
})
})
}
)

Expand Down
102 changes: 91 additions & 11 deletions src/http/routes/cdn/purgeCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,44 +8,124 @@ const purgeObjectParamsSchema = {
type: 'object',
properties: {
bucketName: { type: 'string', examples: ['avatars'] },
'*': { type: 'string', examples: ['folder/cat.png'] },
'*': { type: 'string', examples: ['folder/cat.png'], minLength: 1 },
},
required: ['bucketName', '*'],
} as const

const purgeBucketParamsSchema = {
type: 'object',
properties: {
bucketName: { type: 'string', examples: ['avatars'] },
},
required: ['bucketName'],
} as const

const purgeQuerySchema = {
type: 'object',
properties: {
transformations: { type: 'boolean' },
},
} as const

const successResponseSchema = {
type: 'object',
properties: {
message: { type: 'string', examples: ['success'] },
},
}
interface deleteObjectRequestInterface extends AuthenticatedRequest {

interface PurgeObjectRequestInterface extends AuthenticatedRequest {
Params: FromSchema<typeof purgeObjectParamsSchema>
Querystring: FromSchema<typeof purgeQuerySchema>
}

interface PurgeBucketRequestInterface extends AuthenticatedRequest {
Params: FromSchema<typeof purgeBucketParamsSchema>
Querystring: FromSchema<typeof purgeQuerySchema>
}

interface PurgeTenantRequestInterface extends AuthenticatedRequest {
Querystring: FromSchema<typeof purgeQuerySchema>
}

export default async function routes(fastify: FastifyInstance) {
const summary = 'Purge cache for an object'
// Purge tenant cache
fastify.delete<PurgeTenantRequestInterface>(
'/',
{
schema: createDefaultSchema(successResponseSchema, {
querystring: purgeQuerySchema,
summary: 'Purge cache for entire tenant or tenant transformations',
tags: ['cdn'],
}),
config: {
operation: { type: ROUTE_OPERATIONS.PURGE_TENANT_CACHE },
Comment thread
itslenny marked this conversation as resolved.
},
},
async (request, response) => {
const { transformations } = request.query

const schema = createDefaultSchema(successResponseSchema, {
params: purgeObjectParamsSchema,
summary,
tags: ['cdn'],
})
await request.cdnCache.purge({
type: transformations ? 'tenant-transforms' : 'tenant',
tenant: request.tenantId,
})

return response.status(200).send(createResponse('success', '200'))
}
)

// Purge bucket cache
fastify.delete<PurgeBucketRequestInterface>(
'/:bucketName',
{
schema: createDefaultSchema(successResponseSchema, {
params: purgeBucketParamsSchema,
querystring: purgeQuerySchema,
summary: 'Purge cache for an entire bucket or bucket transformations',
tags: ['cdn'],
}),
config: {
operation: { type: ROUTE_OPERATIONS.PURGE_BUCKET_CACHE },
},
},
async (request, response) => {
const { bucketName } = request.params
const { transformations } = request.query

fastify.delete<deleteObjectRequestInterface>(
await request.cdnCache.purge({
type: transformations ? 'bucket-transforms' : 'bucket',
bucket: bucketName,
tenant: request.tenantId,
})

return response.status(200).send(createResponse('success', '200'))
}
)

// Purge object cache
fastify.delete<PurgeObjectRequestInterface>(
'/:bucketName/*',
{
schema,
schema: createDefaultSchema(successResponseSchema, {
params: purgeObjectParamsSchema,
querystring: purgeQuerySchema,
summary: 'Purge cache for an object or object transformations',
tags: ['cdn'],
}),
config: {
operation: { type: ROUTE_OPERATIONS.PURGE_OBJECT_CACHE },
},
},
async (request, response) => {
const { bucketName } = request.params
const objectName = request.params['*']
const { transformations } = request.query

await request.cdnCache.purge({
objectName,
type: transformations ? 'object-transforms' : 'object',
bucket: bucketName,
objectName,
tenant: request.tenantId,
})

Expand Down
2 changes: 2 additions & 0 deletions src/http/routes/operations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ export const ROUTE_OPERATIONS = {

// CDN
PURGE_OBJECT_CACHE: 'storage.cdn.purge_object_cache',
PURGE_BUCKET_CACHE: 'storage.cdn.purge_bucket_cache',
PURGE_TENANT_CACHE: 'storage.cdn.purge_tenant_cache',

// Image Transformation
RENDER_AUTH_IMAGE: 'storage.render.image_authenticated',
Expand Down
Loading