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
16 changes: 16 additions & 0 deletions src/handlers/unknown.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Copyright 2025 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.
*/

export default function unknownHandler() {
const body = JSON.stringify({ message: 'Unknown method. Please see: https://docs.da.live for more information.' });
return { body, status: 501 };
}
15 changes: 3 additions & 12 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import headHandler from './handlers/head.js';
import getHandler from './handlers/get.js';
import postHandler from './handlers/post.js';
import deleteHandler from './handlers/delete.js';
import unknownHandler from './handlers/unknown.js';

export default {
/**
Expand All @@ -28,17 +29,7 @@ export default {
return daResp({ status: 204 });
}

let daCtx;
try {
daCtx = await getDaCtx(req, env);
} catch (e) {
if (e.message === 'Invalid path') {
return daResp({ status: 400 });
}
console.error('Error computing context', e);
return daResp({ status: 500 });
}

const daCtx = await getDaCtx(req, env);
const { authorized, key } = daCtx;
if (!authorized) {
const status = daCtx.users[0].email === 'anonymous' ? 401 : 403;
Expand Down Expand Up @@ -66,7 +57,7 @@ export default {
respObj = await deleteHandler({ req, env, daCtx });
break;
default:
respObj = { status: 405 };
respObj = unknownHandler();
}

if (!respObj) return daResp({ status: 404 });
Expand Down
7 changes: 0 additions & 7 deletions src/utils/daCtx.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,6 @@ export default async function getDaCtx(req, env) {
const path = parts.filter((part) => part !== '');
const keyBase = path.join('/');

const pnlc = pathname.toLocaleLowerCase();
const validPath = `/${api}/${org}/${keyBase}`;

if (!org || !(pnlc === validPath || pnlc === `${validPath}/`)) {
throw new Error('Invalid path');
}

// Get the final source name
daCtx.filename = path.pop() || '';

Expand Down
21 changes: 21 additions & 0 deletions test/handlers/unknown.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright 2025 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 'node:assert';
import unknownHandler from '../../src/handlers/unknown.js';

describe('unknownHandler', () => {
it('should return unknown response', async () => {
const result = await unknownHandler({});
assert.strictEqual(result.status, 501);
assert.strictEqual(result.body.includes('Unknown method'), true);
});
});
37 changes: 4 additions & 33 deletions test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe('fetch', () => {

it('should return a response object for unknown', async () => {
const resp = await handler.fetch({ url: 'https://www.example.com', method: 'BLAH' }, {});
assert.strictEqual(resp.status, 400);
assert.strictEqual(resp.status, 501);
});

it('should return 401 when not authorized and not logged in', async () => {
Expand All @@ -49,38 +49,9 @@ describe('fetch', () => {
const resp = await hnd.fetch({ method: 'GET' }, {});
assert.strictEqual(resp.status, 403);
});
});

describe('invalid routes', () => {
const fetchStatus = async (path, method) => {
const resp = await handler.fetch({ method, url: `http://www.sample.com${path}` }, {});
return resp.status;
};

const test = async (path, status) => {
const methods = ['GET', 'POST', 'PUT', 'DELETE'];
for (const method of methods) {
// eslint-disable-next-line no-await-in-loop
const s = await fetchStatus(path, method);
assert.strictEqual(s, status);
}
};

it('return 400 for invalid paths', async () => {
await test('/', 400);
await test('/source/owner', 400);
await test('/source//owner/repo/path/file.html', 400);
await test('/source/owner//repo/path/file.html', 400);
await test('/source/owner/repo//path/file.html', 400);
await test('/source/owner/repo/path//file.html', 400);
});

it('return 404 for unknown paths', async () => {
await test('/unknown/owner/repo/path/file.html', 404);
});

it('return 405 for unknown methods', async () => {
const status = await fetchStatus('/source/owner/repo/path/file.html', 'BLAH');
assert.strictEqual(status, 405);
it('return 404 for unknown get route', async () => {
const resp = await handler.fetch({ method: 'GET', url: 'http://www.example.com/' }, {});
assert.strictEqual(resp.status, 404);
});
});
25 changes: 15 additions & 10 deletions test/storage/utils/list.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import assert from 'node:assert';
import sinon from 'sinon';

import getDaCtx from '../../../src/utils/daCtx.js';
import formatList, { listCommand } from '../../../src/storage/utils/list.js';

const MOCK = {
Expand Down Expand Up @@ -60,8 +61,12 @@ const MOCK = {
],
};

const req = new Request('https://example.com/source/adobecom');

const daCtx = getDaCtx(req, {});

describe('Format object list', () => {
const list = formatList(MOCK);
const list = formatList(MOCK, daCtx);

it('should return a true folder / common prefix', () => {
assert.strictEqual(list[0].name, 'blog');
Expand Down Expand Up @@ -93,21 +98,21 @@ describe('Format object list', () => {

it('should handle empty CommonPrefixes', () => {
const emptyMock = { Contents: MOCK.Contents };
const result = formatList(emptyMock);
const result = formatList(emptyMock, daCtx);
assert(Array.isArray(result));
assert(result.length > 0);
});

it('should handle empty Contents', () => {
const emptyMock = { CommonPrefixes: MOCK.CommonPrefixes };
const result = formatList(emptyMock);
const result = formatList(emptyMock, daCtx);
assert(Array.isArray(result));
assert(result.length > 0);
});

it('should handle both empty CommonPrefixes and Contents', () => {
const emptyMock = {};
const result = formatList(emptyMock);
const result = formatList(emptyMock, daCtx);
assert(Array.isArray(result));
assert.strictEqual(result.length, 0);
});
Expand All @@ -119,7 +124,7 @@ describe('Format object list', () => {
{ Prefix: 'normal-folder/' },
],
};
const result = formatList(mockWithExtensionFolder);
const result = formatList(mockWithExtensionFolder, daCtx);
const extensionFolder = result.find((item) => item.name === 'file.jpg');
assert.strictEqual(extensionFolder, undefined);
const normalFolder = result.find((item) => item.name === 'normal-folder');
Expand All @@ -135,7 +140,7 @@ describe('Format object list', () => {
},
],
};
const result = formatList(mockWithComplexFile);
const result = formatList(mockWithComplexFile, daCtx);
assert.strictEqual(result.length, 0);
});

Expand All @@ -148,7 +153,7 @@ describe('Format object list', () => {
},
],
};
const result = formatList(mockWithHiddenFile);
const result = formatList(mockWithHiddenFile, daCtx);
assert.strictEqual(result.length, 0);
});

Expand All @@ -161,7 +166,7 @@ describe('Format object list', () => {
},
],
};
const result = formatList(mockWithProps);
const result = formatList(mockWithProps, daCtx);
const propsItem = result.find((item) => item.name === 'test');
assert(propsItem);
assert.strictEqual(propsItem.ext, undefined);
Expand All @@ -178,7 +183,7 @@ describe('Format object list', () => {
},
],
};
const result = formatList(mockWithBoth);
const result = formatList(mockWithBoth, daCtx);
const testItems = result.filter((item) => item.name === 'test');
assert.strictEqual(testItems.length, 1);
});
Expand All @@ -191,7 +196,7 @@ describe('Format object list', () => {
{ Key: 'beta.html', LastModified: new Date('2025-01-01') },
],
};
const result = formatList(mockForSorting);
const result = formatList(mockForSorting, daCtx);
assert.strictEqual(result[0].name, 'alpha');
assert.strictEqual(result[1].name, 'beta');
assert.strictEqual(result[2].name, 'zebra');
Expand Down