-
Notifications
You must be signed in to change notification settings - Fork 1
feat(source): delete moves to trash #170
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 13 commits
fe9d002
1cbfdc2
73ae591
5457497
cc20b84
c3fee05
141a505
fd0a5b7
c903e01
53ce6b1
4ee29d1
f01ff51
f920383
0e82e1a
5b2a03a
ab932b3
18589bd
c00a35b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,30 +12,82 @@ | |
| import { Response } from '@adobe/fetch'; | ||
| import { HelixStorage } from '@adobe/helix-shared-storage'; | ||
| import { createErrorResponse } from '../contentbus/utils.js'; | ||
| import { deleteFolder } from './folder.js'; | ||
| import { getS3KeyFromInfo } from './utils.js'; | ||
| import { RequestInfo } from '../support/RequestInfo.js'; | ||
| import { StatusCodeError } from '../support/StatusCodeError.js'; | ||
| import { CopyOptions, copyDocument, copyFolder } from './source-client.js'; | ||
| import { getDocPathFromS3Key, getS3Key, getS3KeyFromInfo } from './s3-path-utils.js'; | ||
|
|
||
| /** | ||
| * Delete from the source bus. | ||
| * Trash a folder by moving all of its contents to the trash in the same folder structure. | ||
| * If the trash already contains a folder with this name, a base-36 encoded timestamp is appended. | ||
| * | ||
| * @param {import('../support/AdminContext').AdminContext} context context | ||
| * @param {import('../support/RequestInfo').RequestInfo} info request info | ||
| * @return {Promise<Response>} response | ||
| * @returns {Promise<Response>} response, status 204 if successful. | ||
| */ | ||
| async function trashFolder(context, info) { | ||
| const bucket = HelixStorage.fromContext(context).sourceBus(); | ||
|
|
||
| const destDir = `/.trash/${info.rawPath.split('/').at(-2)}`; | ||
|
|
||
| // Ensure that there is no folder in the trash with this name yet | ||
| const listResp = await bucket.list(`${getS3Key(info.org, info.site, destDir)}/`, { shallow: true }); | ||
| const destPath = listResp.length > 0 ? `${destDir}-${Date.now().toString(36)}/` : `${destDir}/`; | ||
|
|
||
| const srcKey = getS3Key(info.org, info.site, info.rawPath); | ||
| const newInfo = RequestInfo.clone(info, { path: destPath }); | ||
| const copyOpts = (sKey) => ({ addMetadata: { 'doc-path': getDocPathFromS3Key(sKey) } }); | ||
|
|
||
| try { | ||
| const resp = await copyFolder(context, new CopyOptions({ | ||
| src: srcKey, info: newInfo, move: true, fnOpts: copyOpts, collOpts: { collision: 'unique' }, | ||
| })); | ||
| if (resp.length > 0) { | ||
| return new Response('', { status: 204 }); | ||
| } | ||
| throw new StatusCodeError('Trashing of folder failed', 500); | ||
| } catch (e) { | ||
| const opts = { e, log: context.log }; | ||
| opts.status = e.$metadata?.httpStatusCode; | ||
| return createErrorResponse(opts); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Delete from the source bus, which means moving it to the trash. Both | ||
| * documents and folders are supported. The trashed documents gets an extra | ||
| * metadata field 'doc-path' which is the path where it was deleted from. | ||
| * | ||
| * @param {import('../support/AdminContext').AdminContext} context context | ||
| * @param {import('../support/RequestInfo').RequestInfo} info request info | ||
| * @return {Promise<Response>} response, status 204 if successful. | ||
| */ | ||
| export async function deleteSource(context, info) { | ||
| if (info.rawPath.endsWith('/')) { | ||
| return deleteFolder(context, info); | ||
| return trashFolder(context, info); | ||
| } | ||
| const { log } = context; | ||
|
|
||
| const bucket = HelixStorage.fromContext(context).sourceBus(); | ||
| const key = getS3KeyFromInfo(info); | ||
| // Trash a document. | ||
| const docName = info.rawPath.split('/').pop(); | ||
| const srcKey = getS3KeyFromInfo(info); | ||
| const newInfo = RequestInfo.clone(info, { path: `/.trash/${docName}` }); | ||
| const copyOpts = { | ||
| addMetadata: { | ||
| 'doc-path': info.resourcePath, | ||
| }, | ||
| }; | ||
| const copyOptions = new CopyOptions({ | ||
| src: srcKey, info: newInfo, move: true, opts: copyOpts, collOpts: { collision: 'unique' }, | ||
| }); | ||
|
|
||
| try { | ||
| const resp = await bucket.remove(key); | ||
| return new Response('', { status: resp.$metadata?.httpStatusCode }); | ||
| const resp = await copyDocument(context, copyOptions); | ||
| if (resp.length !== 1) { | ||
| throw new StatusCodeError('Trashing of document failed', 500); | ||
| } | ||
| return new Response('', { status: 204 }); | ||
| } catch (e) { | ||
| const opts = { e, log }; | ||
| const opts = { e, log: context.log }; | ||
| opts.status = e.$metadata?.httpStatusCode; | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @dominique-pfister since delete now doesn't do
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think so: AFAICS, |
||
| return createErrorResponse(opts); | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this desired? why don't you include the path somehow? otherwise, deleting common files, like
index.htmlwill end up overwriting.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is similar to how a Trash works in an OS environment, e.g. on Mac OS individually deleted files end up in the root of the trash, if there is already a file with that name, they get a unique suffix:
We do a similar thing. So files are not overwritten, but if one with the name already exists, it will get a unique suffix (alpha sortable to get the order in which they are deleted):