Skip to content
This repository was archived by the owner on Feb 25, 2022. It is now read-only.
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
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@ Furthermore, it is possible to limit the result set using `hlx_p.limit` and page

For more, see the [API documentation](docs/API.md).

## Working with Excel and Google Sheets

- The sheet inside an Excel workbook or Google spreadsheet can be addressed using the `sheet` parameter.
- Only sheets having the `helix-` prefix can be addressed.
- If the workbook or spreadsheet does not have any `helix-` prefixed sheets, the first sheet is returned.
- By default, the _used range_ of the selected sheet is returned.
- For excel, A table can be addressed using the `table` request parameter, which can be a table name or an index. For example, `table=Table1` will return the table with the name `Table1`, `table=1` will return the second table in the sheet.

## Development

### Deploying Helix Data Embed
Expand Down
55 changes: 47 additions & 8 deletions src/matchers/excel.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ async function extract(url, params, log = console) {
AZURE_WORD2MD_CLIENT_ID: clientId,
AZURE_HELIX_USER: username,
AZURE_HELIX_PASSWORD: password,
sheet,
table,
} = params;

try {
Expand All @@ -28,17 +30,54 @@ async function extract(url, params, log = console) {
const item = await drive.getDriveItemFromShareLink(url);
const workbook = drive.getWorkbook(item);

const worksheetNames = await workbook.getWorksheetNames();
const worksheetName = worksheetNames[0];
const worksheet = workbook.worksheet(worksheetName);
const tableNames = await worksheet.getTableNames();
// if a sheet is specified, use it
let worksheetName;
if (sheet) {
worksheetName = `helix-${sheet}`;
} else {
// get the sheet names and check if any of them starts with `helix-`
const sheetNames = await workbook.getWorksheetNames();
const hasHelixSheets = !!sheetNames.find((n) => n.startsWith('helix-'));
if (hasHelixSheets) {
if (sheetNames.indexOf('helix-default') < 0) {
// no helix-default -> not found
log.info('Workbook has helix sheets but no helix-default');
return {
statusCode: 404,
headers: {
'cache-control': 'no-store, private, must-revalidate',
},
body: [],
};
}
worksheetName = 'helix-default';
} else {
[worksheetName] = sheetNames;
log.info(`Workbook has no helix sheets. using first one: ${worksheetName}`);
}
}

const worksheet = workbook.worksheet(encodeURIComponent(worksheetName));
const body = await (async () => {
if (!tableNames.length) {
log.info(`worksheet ${worksheetName} has no tables, getting range instead`);
if (!table) {
log.info(`returning used-range for ${worksheetName}.`);
return worksheet.usedRange().getRowsAsObjects();
}
log.info(`fetching table data for worksheet ${worksheetName} with name ${tableNames[0]}`);
return worksheet.table(tableNames[0]).getRowsAsObjects();
// if table is a number, get the names
let tableName = table;
if (/^\d+$/.test(table)) {
const tableNum = Number.parseInt(table, 10);
const tableNames = await worksheet.getTableNames();
if (tableNum < 0 || tableNum >= tableNames.length) {
log.info(`table index out of range: 0 >= ${tableNum} < ${tableNames.length}`);
const error = new Error('index out of range');
error.statusCode = 404;
throw error;
}
tableName = tableNames[tableNum];
}
log.info(`fetching table data for worksheet ${worksheetName} with name ${tableName}`);
return worksheet.table(encodeURIComponent(tableName)).getRowsAsObjects();
})();
log.info(`returning ${body.length} rows.`);
return {
Expand Down
37 changes: 29 additions & 8 deletions src/matchers/google.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,26 +41,47 @@ async function extract(url, params, log = console) {
GOOGLE_DOCS2MD_CLIENT_ID: clientId,
GOOGLE_DOCS2MD_CLIENT_SECRET: clientSecret,
GOOGLE_DOCS2MD_REFRESH_TOKEN: refresh_token,
sheet,
} = params;

try {
const spreadsheetId = getId(url);

const auth = createOAuthClient({ clientId, clientSecret }, { refresh_token });

const sheets = google.sheets({ version: 'v4', auth });

const { data } = await sheets.spreadsheets.get({
spreadsheetId,
});

const sheet1 = data.sheets[0].properties;

const range = `${sheet1.title}!${new A1({
let sheetTitle;
if (sheet) {
sheetTitle = `helix-${sheet}`;
} else {
const hasHelixSheets = !!data.sheets.find((s) => s.properties.title.startsWith('helix-'));
if (hasHelixSheets) {
sheetTitle = 'helix-default';
} else {
sheetTitle = data.sheets[0].properties.title;
log.info(`Workbook has no helix sheets. using first one: ${sheetTitle}`);
}
}

const sheet1 = data.sheets.find((s) => s.properties.title === sheetTitle);
if (!sheet1) {
log.info(`no sheet found with name ${sheetTitle}`);
return {
statusCode: 404,
headers: {
'cache-control': 'no-store, private, must-revalidate',
},
body: '',
};
}

const range = `${sheet1.properties.title}!${new A1({
colStart: 1,
rowStart: 1,
nRows: sheet1.gridProperties.rowCount,
nCols: sheet1.gridProperties.columnCount,
nRows: sheet1.properties.gridProperties.rowCount,
nCols: sheet1.properties.gridProperties.columnCount,
})}`;

const values = await sheets.spreadsheets.values.get({
Expand Down
105 changes: 87 additions & 18 deletions test/excel.integration.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,49 +17,118 @@ const { main } = require('../src/index');

require('dotenv').config();

const condition = condit.hasenv('AZURE_WORD2MD_CLIENT_ID', 'AZURE_HELIX_USER', 'AZURE_HELIX_PASSWORD');

const DATA_COUNTRIES = [{ Country: 'Japan', Code: 'JP', Number: 3 },
{ Country: 'Germany', Code: 'DE', Number: 5 },
{ Country: 'USA', Code: 'US', Number: 7 },
{ Country: 'Switzerland', Code: 'CH', Number: 27 },
{ Country: 'France', Code: 'FR', Number: 99 },
{ Country: 'Australia', Code: 'AUS', Number: 12 }];

describe('Excel Integration Test', () => {
condit('Retrieves Excel Spreadsheet without tables', condit.hasenv('AZURE_WORD2MD_CLIENT_ID', 'AZURE_HELIX_USER', 'AZURE_HELIX_PASSWORD'), async () => {
condit('Excel Spreadsheet without helix-default sheet returns 404', condition, async () => {
const result = await main({
__ow_logger: console,
src: 'https://adobe.sharepoint.com/:x:/r/sites/cg-helix/Shared%20Documents/data-embed-unit-tests/data-embed-no-default.xlsx?d=wf80aa1d65efb4e41bd16ba3ca0a4564b&csf=1&web=1&e=9WnXzf',
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, 404);
}).timeout(15000);

condit('Excel Spreadsheet without helix-default but sheet params', condition, async () => {
const result = await main({
__ow_logger: console,
src: 'https://adobe.sharepoint.com/:x:/r/sites/cg-helix/Shared%20Documents/data-embed-unit-tests/data-embed-no-default.xlsx?d=wf80aa1d65efb4e41bd16ba3ca0a4564b&csf=1&web=1&e=9WnXzf',
sheet: 'countries',
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.deepEqual(result.body, DATA_COUNTRIES);
}).timeout(15000);

condit('Excel Spreadsheet without helix returns first sheet', condition, async () => {
const result = await main({
__ow_logger: console,
src: 'https://adobe.sharepoint.com/:x:/r/sites/cg-helix/Shared%20Documents/data-embed-unit-tests/data-embed-no-helix.xlsx?d=w88ace0003b6847a48b6bb84b79a5b72b&csf=1&web=1&e=Sg8lKt',
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.deepEqual(result.body, [{ Country: 'Japan', Code: 'JP', Number: 81 }]);
}).timeout(15000);

condit('Excel Spreadsheet without helix-default but sheet params Unicode', condition, async () => {
const result = await main({
__ow_logger: console,
src: 'https://adobe.sharepoint.com/:x:/r/sites/cg-helix/Shared%20Documents/data-embed-unit-tests/example-data.xlsx?d=w6911fff4a52a4b3fb80560d8785adfa3&csf=1&web=1&e=fkkA2a',
sheet: '日本',
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.deepEqual(result.body, [{
Code: 'JP',
Country: 'Japan',
Number: 3,
}]);
}).timeout(15000);

condit('Retrieves Excel Spreadsheet with helix-default (range)', condition, async () => {
const result = await main({
__ow_logger: console,
src: 'https://adobe.sharepoint.com/:x:/r/sites/cg-helix/Shared%20Documents/data-embed-unit-tests/example-data.xlsx?d=w6911fff4a52a4b3fb80560d8785adfa3&csf=1&web=1&e=fkkA2a',
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.deepEqual(result.body, DATA_COUNTRIES);
}).timeout(15000);

condit('Retrieves Excel Spreadsheet with tables and table name', condition, async () => {
const result = await main({
__ow_logger: console,
__ow_path: '/https://adobe-my.sharepoint.com/personal/trieloff_adobe_com/_layouts/15/guestaccess.aspx',
share: 'Edoi88tLKLpDsKzSfL-pcJYB2lIo7UKooYWnjm3w2WRrsA',
email: 'helix@adobe.com',
e: 'tD623x',
src: 'https://adobe.sharepoint.com/:x:/r/sites/cg-helix/Shared%20Documents/data-embed-unit-tests/example-data.xlsx?d=w6911fff4a52a4b3fb80560d8785adfa3&csf=1&web=1&e=fkkA2a',
sheet: 'tables',
table: 'Table1',
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, 3);
assert.deepEqual(result.body, [{ A: 112, B: 224, C: 135 }, { A: 2244, B: 234, C: 53 }]);
}).timeout(15000);

condit('Retrieves Excel Spreadsheet with tables', condit.hasenv('AZURE_WORD2MD_CLIENT_ID', 'AZURE_HELIX_USER', 'AZURE_HELIX_PASSWORD'), async () => {
condit('Retrieves Excel Spreadsheet with tables and table index', condition, async () => {
const result = await main({
__ow_logger: console,
__ow_path: '/https://adobe-my.sharepoint.com/personal/trieloff_adobe_com/_layouts/15/guestaccess.aspx',
share: 'Edz_l4D0BghJjLkIfyZCB7sBLaBhySyT5An7fPHVS6CFuA',
email: 'helix@adobe.com',
e: 'e5ziwf',
src: 'https://adobe.sharepoint.com/:x:/r/sites/cg-helix/Shared%20Documents/data-embed-unit-tests/example-data.xlsx?d=w6911fff4a52a4b3fb80560d8785adfa3&csf=1&web=1&e=fkkA2a',
sheet: 'tables',
table: 1,
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);
assert.deepEqual(result.body, [{ X: 111, Y: 222, Z: 333 }, { X: 444, Y: 555, Z: 666 }]);
}).timeout(15000);

condit('Retrieves Excel Spreadsheet via drive uri', condit.hasenv('AZURE_WORD2MD_CLIENT_ID', 'AZURE_HELIX_USER', 'AZURE_HELIX_PASSWORD'), async () => {
condit('Retrieves Excel Spreadsheet via drive uri', condition, 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',
src: 'onedrive:/drives/b!DyVXacYnlkm_17hZL307Me9vzRzaKwZCpVMBYbPOKaVT_gD5WmlHRbC-PCpiwGPx/items/012VWERI7U74IWSKVFH5F3QBLA3B4FVX5D',
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);
assert.deepEqual(result.body, DATA_COUNTRIES);
}).timeout(15000);
});
Loading