diff --git a/src/handlers/unknown.js b/src/handlers/unknown.js new file mode 100644 index 00000000..dbe19289 --- /dev/null +++ b/src/handlers/unknown.js @@ -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 }; +} diff --git a/src/index.js b/src/index.js index a848d2b0..20cfb66b 100644 --- a/src/index.js +++ b/src/index.js @@ -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 { /** @@ -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; @@ -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 }); diff --git a/src/utils/daCtx.js b/src/utils/daCtx.js index b4f8640d..0e35887f 100644 --- a/src/utils/daCtx.js +++ b/src/utils/daCtx.js @@ -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() || ''; diff --git a/test/handlers/unknown.test.js b/test/handlers/unknown.test.js new file mode 100644 index 00000000..cee3586d --- /dev/null +++ b/test/handlers/unknown.test.js @@ -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); + }); +}); diff --git a/test/index.test.js b/test/index.test.js index e6c5f2e2..b86ce7a6 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -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 () => { @@ -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); }); }); diff --git a/test/storage/utils/list.test.js b/test/storage/utils/list.test.js index daef4255..f4f3919e 100644 --- a/test/storage/utils/list.test.js +++ b/test/storage/utils/list.test.js @@ -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 = { @@ -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'); @@ -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); }); @@ -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'); @@ -135,7 +140,7 @@ describe('Format object list', () => { }, ], }; - const result = formatList(mockWithComplexFile); + const result = formatList(mockWithComplexFile, daCtx); assert.strictEqual(result.length, 0); }); @@ -148,7 +153,7 @@ describe('Format object list', () => { }, ], }; - const result = formatList(mockWithHiddenFile); + const result = formatList(mockWithHiddenFile, daCtx); assert.strictEqual(result.length, 0); }); @@ -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); @@ -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); }); @@ -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');