Skip to content
Merged
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
15 changes: 13 additions & 2 deletions src/storage/version/put.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
PutObjectCommand,
} from '@aws-sdk/client-s3';

import { EMPTY_DOC_SIZE } from '../../utils/constants.js';
import getS3Config from '../utils/config.js';
import {
getUsersForMetadata, ifMatch, ifNoneMatch,
Expand Down Expand Up @@ -114,9 +115,19 @@ export async function putObjectWithVersion(env, daCtx, update, body, guid) {
const pps = current.metadata?.preparsingstore || '0';

// Store the body if preparsingstore is not defined, so a once-off store
const storeBody = !body && pps === '0';
let storeBody = !body && pps === '0';
const Preparsingstore = storeBody ? Timestamp : pps;
const Label = storeBody ? 'Collab Parse' : update.label;
let Label = storeBody ? 'Collab Parse' : update.label;

if (daCtx.method === 'PUT' && current.contentLength > EMPTY_DOC_SIZE && (!update.body || update.body.size <= EMPTY_DOC_SIZE)) {
// we are about to empty the document body
// this should never happen but in some cases it does
// we want then to store a version of the full document as a restore point
// eslint-disable-next-line no-console
console.warn('Empty body, creating a restore point');
storeBody = true;
Label = 'Restore Point';
}

const versionResp = await putVersion(config, {
Bucket: input.Bucket,
Expand Down
4 changes: 4 additions & 0 deletions src/utils/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,7 @@ export const SUPPORTED_TYPES = [
'image/svg+xml',
'video/mp4',
];

// this is the size of the empty document
// <body><header></header><main><div></div></main><footer></footer></body>
export const EMPTY_DOC_SIZE = 83;
1 change: 1 addition & 0 deletions src/utils/daCtx.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export default async function getDaCtx(req, env) {
users,
fullKey,
origin: new URL(req.url).origin,
method: req.method,
};

// Sanitize the remaining path parts
Expand Down
84 changes: 83 additions & 1 deletion test/storage/version/put.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,7 @@ describe('Version Put', () => {
},
});

const resp = await putObjectWithVersion({}, {}, {});
const resp = await putObjectWithVersion({}, { method: 'HEAD' }, {});
assert.equal(1, sentToS3.length);
const input = sentToS3[0].input;
assert.equal('', input.Body, 'Empty body for HEAD');
Expand Down Expand Up @@ -657,6 +657,88 @@ describe('Version Put', () => {
assert(input2.Metadata.Version && input2.Metadata.Version !== 101);
});

it('Test putObjectWithVersion BODY - new BODY is empty creates a restore point', async () => {
const mockGetObject = async () => {
const metadata = {
id: 'idabc',
version: '101',
path: '/qwerty',
timestamp: 1234,
}
return { body: 'Somebody...', metadata, contentLength: 616 };
};

const sentToS3 = [];
const s3Client = {
send: async (c) => {
sentToS3.push(c);
return {
$metadata: {
httpStatusCode: 200
}
};
}
};
const mockS3Client = () => s3Client;

const sentToS3_2 = [];
const s3Client2 = {
send: async (c) => {
sentToS3_2.push(c);
return {
$metadata: {
httpStatusCode: 200
}
};
}
};
const mockS3Client2 = () => s3Client2;

const { putObjectWithVersion } = await esmock('../../../src/storage/version/put.js', {
'../../../src/storage/object/get.js': {
default: mockGetObject
},
'../../../src/storage/utils/version.js': {
ifNoneMatch: mockS3Client,
ifMatch: mockS3Client2
},
});

const update = {
org: 'o1',
body: '',
key: 'mypath',
type: 'test/plain',
}
const ctx = {
org: 'o1',
users: [{ email: 'hi@acme.com' }],
method: 'PUT',
}

const resp = await putObjectWithVersion({}, ctx, update, true);

assert.equal(1, sentToS3.length);
const input = sentToS3[0].input;

assert.equal('Somebody...', input.Body);
assert.equal(616, input.ContentLength);
assert.equal('/qwerty', input.Metadata.Path);
assert.equal(1234, input.Metadata.Timestamp);
assert.equal('[{"email":"anonymous"}]', input.Metadata.Users);
assert.equal('Restore Point', input.Metadata.Label);

assert.equal(1, sentToS3_2.length);
const input2 = sentToS3_2[0].input;
assert.equal('', input2.Body);
assert.equal(0, input2.ContentLength);
assert.equal('test/plain', input2.ContentType);
assert.equal('o1/mypath', input2.Key);
assert.equal('mypath', input2.Metadata.Path);
assert.equal('[{"email":"hi@acme.com"}]', input2.Metadata.Users);
assert(input2.Metadata.Version && input2.Metadata.Version !== 101);
});

it('exception without metadata', async () => {
const s3client1 = {
send: async (c) => {
Expand Down