Skip to content

Commit

Permalink
GH-6 Version Labels (#38)
Browse files Browse the repository at this point in the history
Support passing label on version POST/PUT
  • Loading branch information
bosschaert authored May 3, 2024
1 parent 8eabefd commit 725b972
Show file tree
Hide file tree
Showing 8 changed files with 221 additions and 15 deletions.
2 changes: 1 addition & 1 deletion src/handlers/post.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export default async function postHandler({ req, env, daCtx }) {

if (path.startsWith('/source')) return postSource({ req, env, daCtx });
if (path.startsWith('/config')) return postConfig({ req, env, daCtx });
if (path.startsWith('/versionsource')) return postVersionSource({ env, daCtx });
if (path.startsWith('/versionsource')) return postVersionSource({ req, env, daCtx });
if (path.startsWith('/copy')) return copyHandler({ req, env, daCtx });
if (path.startsWith('/rename')) return renameHandler({ req, env, daCtx });

Expand Down
2 changes: 1 addition & 1 deletion src/routes/source.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ async function invalidateCollab(api, url, env) {
}

export async function deleteSource({ req, env, daCtx }) {
await postObjectVersion(env, daCtx);
await postObjectVersion(req, env, daCtx);
const resp = await deleteObject(env, daCtx);

if (resp.status === 204) {
Expand Down
4 changes: 2 additions & 2 deletions src/routes/version.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@ export async function patchVersion({ req, env, daCtx }) {
return patchObjectVersion(req, env, daCtx);
}

export async function postVersionSource({ env, daCtx }) {
return postObjectVersion(env, daCtx);
export async function postVersionSource({ req, env, daCtx }) {
return postObjectVersion(req, env, daCtx);
}
4 changes: 2 additions & 2 deletions src/storage/version/list.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@ export async function listObjectVersions(env, { org, key }) {
}, true);
const timestamp = parseInt(entryResp.metadata.timestamp || '0', 10);
const users = JSON.parse(entryResp.metadata.users || '[{"email":"anonymous"}]');
const { displayname, path } = entryResp.metadata;
const { label, path } = entryResp.metadata;

if (entryResp.contentLength > 0) {
return {
url: `/versionsource/${org}/${current.metadata.id}/${entry.name}.${entry.ext}`,
users,
timestamp,
path,
displayname,
label,
};
}
return { users, timestamp, path };
Expand Down
4 changes: 2 additions & 2 deletions src/storage/version/patch.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ function buildInput({
};
}

// Currently only patches the display name into the version
// Currently only patches the label into the version
export async function patchObjectVersion(req, env, daCtx) {
const rb = await req.json();
const { org, key } = daCtx;
Expand All @@ -43,7 +43,7 @@ export async function patchObjectVersion(req, env, daCtx) {
Users: current.metadata?.users || JSON.stringify([{ email: 'anonymous' }]),
Timestamp: current.metadata?.timestamp || `${Date.now()}`,
Path: current.metadata?.path || daCtx.key,
Displayname: rb.displayname,
Label: rb.label || current.metadata?.label,
},
}, false);
return { status: resp.status };
Expand Down
20 changes: 19 additions & 1 deletion src/storage/version/put.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,30 @@ function buildInput({
};
}

export async function postObjectVersion(env, daCtx) {
export async function postObjectVersion(req, env, daCtx) {
let reqJSON;
try {
reqJSON = await req.json();
} catch (e) {
// no label
}

const config = getS3Config(env);
const update = buildInput(daCtx);
const current = await getObject(env, daCtx);
if (current.status === 404 || !current.metadata?.id || !current.metadata?.version) {
return 404;
}

let existingVersion;
if (reqJSON?.label === undefined) {
existingVersion = await getObject(env, {
org: daCtx.org,
key: `.da-versions/${current.metadata.id}/${current.metadata.version}.${daCtx.ext}`,
});
}
const label = reqJSON?.label || existingVersion?.metadata?.label;

const resp = await putVersion(config, {
Bucket: update.Bucket,
Body: current.body,
Expand All @@ -62,6 +79,7 @@ export async function postObjectVersion(env, daCtx) {
Users: current.metadata?.users || JSON.stringify([{ email: 'anonymous' }]),
Timestamp: current.metadata?.timestamp || `${Date.now()}`,
Path: current.metadata?.path || daCtx.key,
Label: label,
},
}, false);
return { status: resp.status === 200 ? 201 : resp.status };
Expand Down
12 changes: 6 additions & 6 deletions test/storage/version/patch.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import esmock from 'esmock';

describe('Version Patch', () => {
it('Patch Object unknown', async () => {
const req = { json: async () => JSON.parse('{"displayname": "Some version"}')};
const req = { json: async () => JSON.parse('{"label": "Some version"}')};
const { patchObjectVersion } = await esmock(
'../../../src/storage/version/patch.js', {
'../../../src/storage/object/get.js': {
Expand All @@ -32,7 +32,7 @@ describe('Version Patch', () => {
})

it('Patch Object Version', async () => {
const req = { json: async () => JSON.parse('{"displayname": "Some version"}')};
const req = { json: async () => JSON.parse('{}')};
const env = {
S3_DEF_URL: 's3def',
S3_ACCESS_KEY_ID: 'id',
Expand All @@ -54,7 +54,7 @@ describe('Version Patch', () => {

const mockedObject = {
body: 'obj body',
metadata: { timestamp: '999' }
metadata: { timestamp: '999', label: 'prev label' }
};
const mockGetObject = async (e, c) => {
if (e === env
Expand Down Expand Up @@ -100,12 +100,12 @@ describe('Version Patch', () => {
assert.equal('[{"email":"anonymous"}]', de.Metadata.Users);
assert.equal('999', de.Metadata.Timestamp);
assert.equal('mykey', de.Metadata.Path);
assert.equal('Some version', de.Metadata.Displayname);
assert.equal('prev label', de.Metadata.Label);
assert.equal(200, resp.status);
});

it('Patch Object Version 2', async () => {
const req = { json: async () => JSON.parse('{"displayname": "v999"}')};
const req = { json: async () => JSON.parse('{"label": "v999"}')};
const env = {};
const daCtx = {
org: 'org2',
Expand Down Expand Up @@ -167,7 +167,7 @@ describe('Version Patch', () => {
assert.equal('[{"email":"jbloggs@acme"},{"email":"ablah@halba"}]', de.Metadata.Users);
assert.equal('12345', de.Metadata.Timestamp);
assert.equal('goobar', de.Metadata.Path);
assert.equal('v999', de.Metadata.Displayname);
assert.equal('v999', de.Metadata.Label);
assert.equal(200, resp.status);
});
});
188 changes: 188 additions & 0 deletions test/storage/version/put.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
/*
* Copyright 2024 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
import assert from 'assert';
import esmock from 'esmock';

describe('Version Put', () => {
it('Post Object Version', async () => {
const mockGetObject = async () => {
const metadata = {
id: 'id',
version: '123'
}
return { metadata };
};

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

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

const dn = { label: 'my label' };
const req = {
json: async () => dn
};
const env = {};
const daCtx = {
org: 'myorg',
key: '/a/b/c',
ext: 'html'
};

const resp = await postObjectVersion(req, env, daCtx);
assert.equal(201, resp.status);

assert.equal(1, sentToS3.length);
const input = sentToS3[0].input;
assert.equal('myorg-content', input.Bucket);
assert.equal('.da-versions/id/123.html', input.Key);
assert.equal('[{"email":"anonymous"}]', input.Metadata.Users);
assert.equal('my label', input.Metadata.Label);
assert(input.Metadata.Timestamp > (Date.now() - 2000)); // Less than 2 seconds old
assert.equal('/a/b/c', input.Metadata.Path);
});

it('Post Object Version 2', async () => {
const mockGetObject = async () => {
const metadata = {
label: 'old label',
id: 'idx',
version: '456',
path: '/y/z',
timestamp: 999,
users: '[{"email":"[email protected]"}]',
}
return { metadata };
};

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

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

const dn = { label: 'my label' };
const req = {};
const env = {};
const daCtx = {
org: 'someorg',
key: '/a/b/c',
ext: 'html'
};

const resp = await postObjectVersion(req, env, daCtx);
assert.equal(202, resp.status);

assert.equal(1, sentToS3.length);
const input = sentToS3[0].input;
assert.equal('someorg-content', input.Bucket);
assert.equal('.da-versions/idx/456.html', input.Key);
assert.equal('[{"email":"[email protected]"}]', input.Metadata.Users);
assert.equal('old label', input.Metadata.Label);
assert.equal(999, input.Metadata.Timestamp);
assert.equal('/y/z', input.Metadata.Path);
});

it('Post Object Version where Label already exists', async () => {
const mockGetObject = async (e, x) => {
if (x.key === '.da-versions/idx/456.myext') {
const mdver = {
label: 'existing label',
};
return { metadata: mdver };
}
const metadata = {
id: 'idx',
version: '456',
path: '/y/z',
timestamp: 999,
users: '[{"email":"[email protected]"},{"email":"[email protected]"}]',
}
return { metadata };
};

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

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

const dn = { label: 'my label' };
const req = {};
const env = {};
const daCtx = {
org: 'someorg',
key: '/a/b/c',
ext: 'myext'
};

const resp = await postObjectVersion(req, env, daCtx);
assert.equal(201, resp.status);

assert.equal(1, sentToS3.length);
const input = sentToS3[0].input;
assert.equal('someorg-content', input.Bucket);
assert.equal('.da-versions/idx/456.myext', input.Key);
assert.equal('[{"email":"[email protected]"},{"email":"[email protected]"}]', input.Metadata.Users);
assert.equal('existing label', input.Metadata.Label);
assert.equal('/y/z', input.Metadata.Path);
});
});

0 comments on commit 725b972

Please sign in to comment.