Skip to content
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

Support version naming #33

Merged
merged 6 commits into from
Apr 29, 2024
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,6 @@ dist
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

.vscode

20 changes: 20 additions & 0 deletions src/handlers/patch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* 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 { patchVersion } from '../routes/version.js';

export default async function patchHandler({ req, env, daCtx }) {
const { path } = daCtx;

if (path.startsWith('/versionsource')) return patchVersion({ req, env, daCtx });

return undefined;
}
4 changes: 4 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import daResp from './utils/daResp.js';

import headHandler from './handlers/head.js';
import getHandler from './handlers/get.js';
import patchHandler from './handlers/patch.js';
import postHandler from './handlers/post.js';
import deleteHandler from './handlers/delete.js';
import unkownHandler from './handlers/unkown.js';
Expand Down Expand Up @@ -44,6 +45,9 @@ export default {
case 'DELETE':
respObj = await deleteHandler({ req, env, daCtx });
break;
case 'PATCH':
respObj = await patchHandler({ req, env, daCtx });
break;
default:
respObj = unkownHandler();
}
Expand Down
5 changes: 5 additions & 0 deletions src/routes/version.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import { getObjectVersion } from '../storage/version/get.js';
import { listObjectVersions } from '../storage/version/list.js';
import { patchObjectVersion } from '../storage/version/patch.js';
import { postObjectVersion } from '../storage/version/put.js';

export async function getVersionList({ env, daCtx }) {
Expand All @@ -22,6 +23,10 @@ export async function getVersionSource({ env, daCtx, head }) {
return getObjectVersion(env, daCtx, head);
}

export async function patchVersion({ req, env, daCtx }) {
return patchObjectVersion(req, env, daCtx);
}

export async function postVersionSource({ env, daCtx }) {
return postObjectVersion(env, daCtx);
}
3 changes: 2 additions & 1 deletion src/storage/version/list.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +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 { path } = entryResp.metadata;
const { displayname, path } = entryResp.metadata;

if (entryResp.contentLength > 0) {
return {
url: `/versionsource/${org}/${current.metadata.id}/${entry.name}.${entry.ext}`,
users,
timestamp,
path,
displayname,
};
}
return { users, timestamp, path };
Expand Down
50 changes: 50 additions & 0 deletions src/storage/version/patch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* 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 getS3Config from '../utils/config.js';
import getObject from '../object/get.js';
import { putVersion } from './put.js';

function buildInput({
org, key, body, type,
}) {
const Bucket = `${org}-content`;
return {
Bucket, Key: key, Body: body, ContentType: type,
};
}

// Currently only patches the display name into the version
export async function patchObjectVersion(req, env, daCtx) {
const rb = await req.json();
const { org, key } = daCtx;

const config = getS3Config(env);
const update = buildInput(daCtx);
const current = await getObject(env, { org, key: `.da-versions/${key}` });
if (current.status === 404) {
return { status: current.status };
}
const resp = await putVersion(config, {
Bucket: update.Bucket,
Body: current.body,
ID: daCtx.site,
Version: daCtx.name,
Ext: daCtx.ext,
Metadata: {
Users: current.metadata?.users || JSON.stringify([{ email: 'anonymous' }]),
Timestamp: current.metadata?.timestamp || `${Date.now()}`,
Path: current.metadata?.path || daCtx.key,
Displayname: rb.displayname,
},
}, false);
return { status: resp.status };
}
2 changes: 1 addition & 1 deletion src/storage/version/put.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
} from '../utils/version.js';
import getObject from '../object/get.js';

async function putVersion(config, {
export async function putVersion(config, {
Bucket, Body, ID, Version, Ext, Metadata,
}, noneMatch = true) {
const client = noneMatch ? ifNoneMatch(config) : createBucketIfMissing(new S3Client(config));
Expand Down
2 changes: 1 addition & 1 deletion src/utils/daResp.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export default function daResp({
}) {
const headers = new Headers();
headers.append('Access-Control-Allow-Origin', '*');
headers.append('Access-Control-Allow-Methods', 'HEAD, GET, PUT, POST, DELETE');
headers.append('Access-Control-Allow-Methods', 'HEAD, GET, PUT, POST, DELETE, PATCH');
headers.append('Access-Control-Allow-Headers', '*');
headers.append('Content-Type', contentType);
if (contentLength) {
Expand Down
173 changes: 173 additions & 0 deletions test/storage/version/patch.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
/*
* 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 Patch', () => {
it('Patch Object unknown', async () => {
const req = { json: async () => JSON.parse('{"displayname": "Some version"}')};
const { patchObjectVersion } = await esmock(
'../../../src/storage/version/patch.js', {
'../../../src/storage/object/get.js': {
default: () => ({ status: 404 }),
}
});

const daCtx = {
org: 'org1',
key: 'mykey',
};

const resp = await patchObjectVersion(req, {}, daCtx);
assert.equal(404, resp.status);
})

it('Patch Object Version', async () => {
const req = { json: async () => JSON.parse('{"displayname": "Some version"}')};
const env = {
S3_DEF_URL: 's3def',
S3_ACCESS_KEY_ID: 'id',
S3_SECRET_ACCESS_KEY: 'secret'
};
const daCtx = {
org: 'org1',
key: 'mykey',
site: 'site1',
name: 'cafe1234',
ext: 'html'
};

const mockS3Client = class S3Client {
constructor(arg) {
this.arg = arg;
}
};

const mockedObject = {
body: 'obj body',
metadata: { timestamp: '999' }
};
const mockGetObject = async (e, c) => {
if (e === env
&& c.org === 'org1'
&& c.key === '.da-versions/mykey' ) {
return mockedObject;
} else {
assert.fail('Unexpected arguments passed to getObject()');
}
};

const pvCalled = [];
const mockPutVersion = (c, d, e) => {
pvCalled.push(c, d, e);
return { status: 200 };
}

const { patchObjectVersion } = await esmock(
'../../../src/storage/version/patch.js', {
'../../../src/storage/object/get.js': {
default: mockGetObject,
},
'../../../src/storage/version/put.js': {
putVersion: mockPutVersion
}
});

const resp = await patchObjectVersion(req, env, daCtx);
const pe = pvCalled[0];
assert.equal('auto', pe.region);
assert.equal('s3def', pe.endpoint);
assert.equal('id', pe.credentials.accessKeyId);
assert.equal('secret', pe.credentials.secretAccessKey);
assert(!pvCalled[2]);

// Check the data sent to putVersion
const de = pvCalled[1];
assert.equal('org1-content', de.Bucket);
assert.equal('obj body', de.Body);
assert.equal('site1', de.ID);
assert.equal('cafe1234', de.Version);
assert.equal('html', de.Ext);
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(200, resp.status);
});

it('Patch Object Version 2', async () => {
const req = { json: async () => JSON.parse('{"displayname": "v999"}')};
const env = {};
const daCtx = {
org: 'org2',
key: 'some/key',
site: 'site1',
name: 'cafe1234',
ext: 'html'
};

const mockS3Client = class S3Client {
constructor(arg) {
this.arg = arg;
}
};

const mockedObject = {
body: 'obj body',
metadata: {
timestamp: '12345',
users: '[{"email":"jbloggs@acme"},{"email":"ablah@halba"}]',
path: 'goobar'
}
};
const mockGetObject = async (e, c) => {
if (e === env
&& c.org === 'org2'
&& c.key === '.da-versions/some/key' ) {
return mockedObject;
} else {
assert.fail('Unexpected arguments passed to getObject()');
}
};

const pvCalled = [];
const mockPutVersion = (c, d, e) => {
pvCalled.push(c, d, e);
return { status: 200 };
}

const { patchObjectVersion } = await esmock(
'../../../src/storage/version/patch.js', {
'../../../src/storage/object/get.js': {
default: mockGetObject,
},
'../../../src/storage/version/put.js': {
putVersion: mockPutVersion
}
});

const resp = await patchObjectVersion(req, env, daCtx);

// Check the data sent to putVersion
const de = pvCalled[1];
assert.equal('org2-content', de.Bucket);
assert.equal('obj body', de.Body);
assert.equal('site1', de.ID);
assert.equal('cafe1234', de.Version);
assert.equal('html', de.Ext);
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(200, resp.status);
});
});
Loading