diff --git a/src/data-source.js b/src/data-source.js index 126815a5..265cbb7d 100644 --- a/src/data-source.js +++ b/src/data-source.js @@ -22,10 +22,11 @@ function dataSource(params) { const { __ow_path: path = '', src = '' } = params; let url = null; if (!path) { - if (!src.startsWith('https://')) { + try { + url = new URL(src); + } catch (e) { return null; } - url = new URL(params.src); // expect the _ow_path to start with /https:// or /https%3a%2f%2f } else if (path.startsWith('/https%3A%2F%2F')) { diff --git a/src/embed.js b/src/embed.js index e9a080d9..b4274401 100644 --- a/src/embed.js +++ b/src/embed.js @@ -24,11 +24,18 @@ function hasParams(list, params) { return empty.length === list.length; } +/** + * Returns the data representation of the resource addressed by url. + * @param {URL} url The url of the resource + * @param {object} params The action params + * @param {Logger} log logger + * @returns {object} an action response with the body containing the data. + */ function embed(url, params, log) { const candidates = matchers .filter((candidate) => hasParams(candidate.required, params)); - const matching = candidates.find((candidate) => candidate.pattern(url)); + const matching = candidates.find((candidate) => candidate.accept(url)); if (!url || !matching) { log.warn(`No matcher found for URL ${url}`); diff --git a/src/index.js b/src/index.js index f23f9cf1..3eaa6ff4 100644 --- a/src/index.js +++ b/src/index.js @@ -33,7 +33,7 @@ async function main(params) { log.debug('QB query', qbquery); const filter = createfilter(qbquery); log.debug('QB filter', filter); - const result = await embed(url.toString(), params, log); + const result = await embed(url, params, log); const { body } = result; delete result.body; diff --git a/src/matchers/excel.js b/src/matchers/excel.js index 4b74f026..bc24ab2b 100644 --- a/src/matchers/excel.js +++ b/src/matchers/excel.js @@ -25,7 +25,6 @@ async function extract(url, params, log = console) { password, log, }); - const item = await drive.getDriveItemFromShareLink(url); const workbook = drive.getWorkbook(item); @@ -53,7 +52,7 @@ async function extract(url, params, log = console) { } catch (e) { log.error(e.message); return { - statusCode: 500, + statusCode: e.statusCode || 500, headers: { 'Content-Type': 'application/json', 'Cache-Control': 'max-age=600', @@ -66,6 +65,6 @@ async function extract(url, params, log = console) { module.exports = { name: 'excel', required: ['AZURE_WORD2MD_CLIENT_ID', 'AZURE_HELIX_USER', 'AZURE_HELIX_PASSWORD'], - pattern: (url) => /^https:\/\/.*\.sharepoint\.com\//.test(url), + accept: (url) => url.protocol === 'onedrive:' || /^https:\/\/.*\.sharepoint\.com\//.test(url), extract, }; diff --git a/src/matchers/feed.js b/src/matchers/feed.js index b2b11a4f..3fbea409 100644 --- a/src/matchers/feed.js +++ b/src/matchers/feed.js @@ -16,14 +16,14 @@ const parser = new Parser(); module.exports = { name: 'feed', required: [], - pattern: (url) => { + accept: (url) => { if (/\/feeds\/|[&?]feed=atom/.test(url)) { return true; } return false; }, extract: async (url) => { - const feed = await parser.parseURL(url); + const feed = await parser.parseURL(url.toString()); return { statusCode: 200, headers: { diff --git a/src/matchers/google.js b/src/matchers/google.js index 1118b6f5..03cc4ecb 100644 --- a/src/matchers/google.js +++ b/src/matchers/google.js @@ -10,7 +10,6 @@ * governing permissions and limitations under the License. */ /* eslint-disable camelcase */ -const URL = require('url'); const { google } = require('googleapis'); const A1 = require('@flighter/a1-notation'); @@ -20,7 +19,7 @@ const A1 = require('@flighter/a1-notation'); let tokenCache = {}; function getId(url) { - return URL.parse(url).pathname.match(/\/d\/(.*)\//)[1]; + return url.pathname.match(/\/d\/(.*)\//)[1]; } function createOAuthClient(options, creds) { @@ -107,6 +106,6 @@ async function extract(url, params, log = console) { module.exports = { name: 'google', required: ['GOOGLE_DOCS2MD_CLIENT_ID', 'GOOGLE_DOCS2MD_CLIENT_SECRET', 'GOOGLE_DOCS2MD_REFRESH_TOKEN'], - pattern: (url) => /^https:\/\/docs\.google\.com\/spreadsheets\/d\/.*/.test(url), + accept: (url) => /^https:\/\/docs\.google\.com\/spreadsheets\/d\/.*/.test(url), extract, }; diff --git a/src/matchers/run-query.js b/src/matchers/run-query.js index 21f0c83c..c9e23797 100644 --- a/src/matchers/run-query.js +++ b/src/matchers/run-query.js @@ -16,11 +16,11 @@ const { utils } = require('@adobe/helix-shared'); async function extract(url, params, log = console) { const host = 'https://adobeioruntime.net'; const path = '/api/v1/web/helix/helix-services/run-query@v2/'; - const query = url.split('/').pop(); + const query = url.toString().split('/').pop(); const resource = `${host}${path}${query}`; const DEFAULT_CACHE = 'max-age=600'; - const results = await fetch(url.startsWith(host) ? url : resource); + const results = await fetch(url.hostname === 'adobeioruntime.net' ? url.toString() : resource); const statusCode = utils.propagateStatusCode(results.status); const logLevel = utils.logLevelForStatusCode(results.status); const cacheControl = results.headers.get('cache-control'); @@ -53,7 +53,7 @@ async function extract(url, params, log = console) { module.exports = { name: 'run-query', required: [], - pattern: (url) => /(^https:\/\/adobeioruntime\.net\/api\/v1\/web\/helix\/helix-services\/run-query@.*)/.test(url) - || /^\/?_query\/run-query\/.*$/.test(new URL(url).pathname), + accept: (url) => /(^https:\/\/adobeioruntime\.net\/api\/v1\/web\/helix\/helix-services\/run-query@.*)/.test(url) + || /^\/?_query\/run-query\/.*$/.test(url.pathname), extract, }; diff --git a/test/data-source.test.js b/test/data-source.test.js index 043074fd..15f1bb0c 100644 --- a/test/data-source.test.js +++ b/test/data-source.test.js @@ -30,13 +30,20 @@ describe('Data Source Tests', () => { null); }); - it('rejects src parameters not starting with https://', () => { + it('rejects src parameters not starting with scheme', () => { assert.equal(dataSource({ - src: '/http://example.com', + src: '/example.com', }), null); }); + it('src parameters allows for different scheme', () => { + assert.equal(dataSource({ + src: 'onedrive://drives/123123/items/234234', + }), + 'onedrive://drives/123123/items/234234'); + }); + it('returns data source for `src` parameter', () => { const params = { src: 'https://adobeioruntime.net/api/v1/web/helix/helix-services/run-query@2.4.11/error500?a=1&b=2', @@ -47,6 +54,16 @@ describe('Data Source Tests', () => { assert.equal(params.__ow_query, 'hlx_limit=1&foo=bar'); }); + it('returns data source for `src` parameter with different scheme', () => { + const params = { + src: 'onedrive://drives/1234/items/5677?a=1&b=2', + hlx_limit: '1', + foo: 'bar', + }; + assert.equal(dataSource(params), 'onedrive://drives/1234/items/5677?a=1&b=2'); + assert.equal(params.__ow_query, 'hlx_limit=1&foo=bar'); + }); + it('returns data source for backward compat path parameter with no query', () => { const params = { __ow_path: '/https://adobeioruntime.net/api/v1/web/helix/helix-services/run-query@2.4.11/error500?a=1&b=2', diff --git a/test/excel.integration.test.js b/test/excel.integration.test.js index f53dbddd..69acb232 100644 --- a/test/excel.integration.test.js +++ b/test/excel.integration.test.js @@ -31,7 +31,7 @@ describe('Excel Integration Test', () => { }); assert.equal(result.statusCode, 200); assert.equal(result.body.length, 3); - }).timeout(10000); + }).timeout(15000); condit('Retrieves Excel Spreadsheet with tables', condit.hasenv('AZURE_WORD2MD_CLIENT_ID', 'AZURE_HELIX_USER', 'AZURE_HELIX_PASSWORD'), async () => { const result = await main({ @@ -46,5 +46,20 @@ describe('Excel Integration Test', () => { }); assert.equal(result.statusCode, 200); assert.equal(result.body.length, 20); - }).timeout(10000); + }).timeout(15000); + + condit('Retrieves Excel Spreadsheet via drive uri', condit.hasenv('AZURE_WORD2MD_CLIENT_ID', 'AZURE_HELIX_USER', 'AZURE_HELIX_PASSWORD'), async () => { + const result = await main({ + __ow_logger: console, + src: 'onedrive:/drives/b!2nFK7YhvL0yLgH3l_6DPvdBErfBlFFRPphB3wsFazXGX6gDR8muPTo89wY6LZLgv/items/01YELWHJW476LYB5AGBBEYZOIIP4TEEB53', + share: 'Edz_l4D0BghJjLkIfyZCB7sBLaBhySyT5An7fPHVS6CFuA', + email: 'helix@adobe.com', + e: 'e5ziwf', + AZURE_WORD2MD_CLIENT_ID: process.env.AZURE_WORD2MD_CLIENT_ID, + AZURE_HELIX_USER: process.env.AZURE_HELIX_USER, + AZURE_HELIX_PASSWORD: process.env.AZURE_HELIX_PASSWORD, + }); + assert.equal(result.statusCode, 200); + assert.equal(result.body.length, 20); + }).timeout(15000); }); diff --git a/test/excel.test.js b/test/excel.test.js index dc77aa46..e1f9b9e2 100644 --- a/test/excel.test.js +++ b/test/excel.test.js @@ -42,30 +42,70 @@ describe('Excel Tests', () => { it('Works for Excel Workbooks with no tables', async () => { const expected = await fs.readJson(path.resolve(__dirname, 'fixtures', 'example-data-sheet1.json')); const result = await extract( - 'https://adobe.sharepoint.com/:x:/r/sites/cg-helix/Shared%20Documents/data-embed-unit-tests/example-data-no-tables.xlsx', + new URL('https://adobe.sharepoint.com/:x:/r/sites/cg-helix/Shared%20Documents/data-embed-unit-tests/example-data-no-tables.xlsx'), {}, ); assert.equal(result.statusCode, 200); assert.deepEqual(result.body, expected); - }).timeout(15000); + }); + + it('Works for Excel Workbooks with onedrive uri', async () => { + const expected = await fs.readJson(path.resolve(__dirname, 'fixtures', 'example-data-sheet1.json')); + const result = await extract( + new URL('onedrive:/drives/my-drive/items/my-item'), + {}, + ); + assert.equal(result.statusCode, 200); + assert.deepEqual(result.body, expected); + }); it('Works for Excel Workbooks with tables', async () => { const expected = await fs.readJson(path.resolve(__dirname, 'fixtures', 'example-data-sheet1.json')); const result = await extract( - 'https://adobe.sharepoint.com/:x:/r/sites/cg-helix/Shared%20Documents/data-embed-unit-tests/example-data.xlsx', + new URL('https://adobe.sharepoint.com/:x:/r/sites/cg-helix/Shared%20Documents/data-embed-unit-tests/example-data.xlsx'), {}, ); assert.equal(result.statusCode, 200); assert.deepEqual(result.body, expected); - }).timeout(15000); + }); + + it('Fails with 404 for non existing Excel workbook', async () => { + const result = await extract( + new URL('https://adobe.sharepoint.com/:x:/r/sites/cg-helix/Shared%20Documents/data-embed-unit-tests/not-exist.xlsx'), + {}, + ); + + assert.equal(result.statusCode, 404); + assert.equal(result.body.length, 0); + }); - it('Fails for invalid Excel workbook', async () => { + it('Fails for non existing Excel workbook with onedrive uri', async () => { const result = await extract( - 'invalid', + new URL('onedrive:/drives/1234/items/5678'), + {}, + ); + + assert.equal(result.statusCode, 404); + assert.equal(result.body.length, 0); + }); + + it('Fails for invalid onedrive items uri', async () => { + const result = await extract( + new URL('onedrive:/drives/1234/root'), + {}, + ); + + assert.equal(result.statusCode, 500); + assert.equal(result.body.length, 0); + }); + + it('Fails for invalid onedrive uri', async () => { + const result = await extract( + new URL('onedrive:/1234'), {}, ); assert.equal(result.statusCode, 500); assert.equal(result.body.length, 0); - }).timeout(15000); + }); }); diff --git a/test/google.test.js b/test/google.test.js index 032ddf8d..2e318ab5 100644 --- a/test/google.test.js +++ b/test/google.test.js @@ -80,7 +80,7 @@ describe('Google Sheets Tests (mocked)', () => { it('Works for mocked Google Sheets', async () => { const result = await extract( - 'https://docs.google.com/spreadsheets/d/1IX0g5P74QnHPR3GW1AMCdTk_-m954A-FKZRT2uOZY7k/edit?ouid=107837958797411838063&usp=sheets_home&ths=true', + new URL('https://docs.google.com/spreadsheets/d/1IX0g5P74QnHPR3GW1AMCdTk_-m954A-FKZRT2uOZY7k/edit?ouid=107837958797411838063&usp=sheets_home&ths=true'), { GOOGLE_DOCS2MD_CLIENT_ID: 'fake', GOOGLE_DOCS2MD_CLIENT_SECRET: 'fake', @@ -95,7 +95,7 @@ describe('Google Sheets Tests (mocked)', () => { it('Fails (in a good way) for mocked Google Sheets', async () => { const result = await extract( - 'https://docs.google.com/spreadsheets/d/1IX0g5P74QnHPR3GW1AMCdTk_-m954A-FKZRT2uOZY7k/edit?ouid=107837958797411838063&usp=sheets_home&ths=true', + new URL('https://docs.google.com/spreadsheets/d/1IX0g5P74QnHPR3GW1AMCdTk_-m954A-FKZRT2uOZY7k/edit?ouid=107837958797411838063&usp=sheets_home&ths=true'), { GOOGLE_DOCS2MD_CLIENT_ID: 'fail', GOOGLE_DOCS2MD_CLIENT_SECRET: 'fake', @@ -108,16 +108,16 @@ describe('Google Sheets Tests (mocked)', () => { }); describe('Google Sheets Tests (online)', () => { - const { extract, pattern } = require('../src/matchers/google'); + const { extract, accept } = require('../src/matchers/google'); it('Test patterns', () => { - assert.ok(pattern('https://docs.google.com/spreadsheets/d/1IX0g5P74QnHPR3GW1AMCdTk_-m954A-FKZRT2uOZY7k/edit?ouid=107837958797411838063&usp=sheets_home&ths=true')); - assert.ok(!pattern('https://docs.google.com/airballoons/d/1IX0g5P74QnHPR3GW1AMCdTk_-m954A-FKZRT2uOZY7k/edit?ouid=107837958797411838063&usp=sheets_home&ths=true')); + assert.ok(accept(new URL('https://docs.google.com/spreadsheets/d/1IX0g5P74QnHPR3GW1AMCdTk_-m954A-FKZRT2uOZY7k/edit?ouid=107837958797411838063&usp=sheets_home&ths=true'))); + assert.ok(!accept(new URL('https://docs.google.com/airballoons/d/1IX0g5P74QnHPR3GW1AMCdTk_-m954A-FKZRT2uOZY7k/edit?ouid=107837958797411838063&usp=sheets_home&ths=true'))); }); it.skip('Works for real Google Sheets', async () => { const result = await extract( - 'https://docs.google.com/spreadsheets/d/1IX0g5P74QnHPR3GW1AMCdTk_-m954A-FKZRT2uOZY7k/edit?ouid=107837958797411838063&usp=sheets_home&ths=true', + new URL('https://docs.google.com/spreadsheets/d/1IX0g5P74QnHPR3GW1AMCdTk_-m954A-FKZRT2uOZY7k/edit?ouid=107837958797411838063&usp=sheets_home&ths=true'), { GOOGLE_DOCS2MD_CLIENT_ID: process.env.GOOGLE_DOCS2MD_CLIENT_ID, GOOGLE_DOCS2MD_CLIENT_SECRET: process.env.GOOGLE_DOCS2MD_CLIENT_SECRET,