diff --git a/apps/server/src/api-data/excel/excel.service.ts b/apps/server/src/api-data/excel/excel.service.ts index 04000b05b..e571302d0 100644 --- a/apps/server/src/api-data/excel/excel.service.ts +++ b/apps/server/src/api-data/excel/excel.service.ts @@ -51,7 +51,7 @@ export function generateRundownPreview(options: ImportMap): { rundown: OntimeRun } // clear the data - excelData = undefined; + excelData = xlsx.utils.book_new(); return { rundown, customFields }; } diff --git a/apps/server/src/services/sheet-service/SheetService.ts b/apps/server/src/services/sheet-service/SheetService.ts index f675e1a1a..edff169e3 100644 --- a/apps/server/src/services/sheet-service/SheetService.ts +++ b/apps/server/src/services/sheet-service/SheetService.ts @@ -74,10 +74,11 @@ export function revoke(): ReturnType { */ export function handleClientSecret(clientSecret: string): ClientSecret { const clientSecretObject = JSON.parse(clientSecret); - const isValid = validateClientSecret(clientSecretObject); - if (!isValid) { - throw new Error('Client secret invalid'); + try { + validateClientSecret(clientSecretObject); + } catch (error) { + throw new Error(`Client secret is invalid: ${error}`); } return clientSecretObject; diff --git a/apps/server/src/services/sheet-service/sheetUtils.ts b/apps/server/src/services/sheet-service/sheetUtils.ts index 5142446e7..e15110bef 100644 --- a/apps/server/src/services/sheet-service/sheetUtils.ts +++ b/apps/server/src/services/sheet-service/sheetUtils.ts @@ -1,7 +1,8 @@ -import { isOntimeBlock, isOntimeEvent, OntimeRundownEntry } from 'ontime-types'; +import { isOntimeBlock, isOntimeEvent, OntimeEvent, OntimeRundownEntry } from 'ontime-types'; import { millisToString } from 'ontime-utils'; import { sheets_v4 } from '@googleapis/sheets'; +import { isObject } from '../../utils/assert.js'; // we expect client secret file to contain the following keys const requiredClientKeys = [ @@ -26,10 +27,21 @@ export type ClientSecret = { /** * Guard validates a given client secrets file * @param clientSecret - * @returns + * @throws */ export function validateClientSecret(clientSecret: object): clientSecret is ClientSecret { - return requiredClientKeys.every((key) => Object.keys(clientSecret['installed']).includes(key)); + if (!('installed' in clientSecret)) { + throw new Error('Missing "installed" object'); + } + + const { installed } = clientSecret; + isObject(installed); + + if (requiredClientKeys.every((key) => Object.keys(installed).includes(key))) { + return; + } + + throw new Error('Missing keys in "installed" object'); } /** @@ -65,18 +77,18 @@ export function getA1Notation(row: number, column: number): string { * @param {OntimeRundownEntry} event * @param {number} index - index of the event * @param {number} worksheetId - * @param {any} metadata - object with all the cell positions of the title of each attribute + * @param {object} metadata - object with all the cell positions of the title of each attribute * @returns {sheets_v4.Schema} - list of update requests */ export function cellRequestFromEvent( event: OntimeRundownEntry, index: number, worksheetId: number, - metadata, + metadata: object, ): sheets_v4.Schema$Request { const rowData = Object.entries(metadata) .filter(([_, value]) => value !== undefined) - .sort(([_a, a], [_b, b]) => a['col'] - b['col']) as [string, { col: number; row: number }][]; + .sort(([_a, a], [_b, b]) => a['col'] - b['col']) as [keyof OntimeEvent | 'blank', { col: number; row: number }][]; const titleCol = rowData[0][1].col; @@ -113,7 +125,7 @@ export function cellRequestFromEvent( }; } -function getCellData(key: string, event: OntimeRundownEntry) { +function getCellData(key: keyof OntimeEvent | 'blank', event: OntimeRundownEntry) { if (isOntimeEvent(event)) { if (key === 'blank') { return {}; @@ -126,14 +138,13 @@ function getCellData(key: string, event: OntimeRundownEntry) { return { userEnteredValue: { stringValue: event.custom[customKey] } }; } - const dataType = typeof event[key]; - if (dataType === 'number') { + if (typeof event[key] === 'number') { return { userEnteredValue: { stringValue: millisToString(event[key]) } }; } - if (dataType === 'string') { + if (typeof event[key] === 'string') { return { userEnteredValue: { stringValue: event[key] } }; } - if (dataType === 'boolean') { + if (typeof event[key] === 'boolean') { return { userEnteredValue: { boolValue: event[key] } }; } } diff --git a/apps/server/src/utils/__tests__/oscArgParser.test.ts b/apps/server/src/utils/__tests__/oscArgParser.test.ts index 06fceee41..2293579d5 100644 --- a/apps/server/src/utils/__tests__/oscArgParser.test.ts +++ b/apps/server/src/utils/__tests__/oscArgParser.test.ts @@ -15,19 +15,19 @@ describe('test stringToOSCArgs()', () => { it('empty is nothing', () => { const test = undefined; - const expected = []; + const expected: any[] = []; expect(stringToOSCArgs(test)).toStrictEqual(expected); }); it('empty is nothing', () => { const test = ''; - const expected = []; + const expected: any[] = []; expect(stringToOSCArgs(test)).toStrictEqual(expected); }); it('1 space is nothing', () => { const test = ' '; - const expected = []; + const expected: any[] = []; expect(stringToOSCArgs(test)).toStrictEqual(expected); }); diff --git a/apps/server/src/utils/__tests__/parser.test.ts b/apps/server/src/utils/__tests__/parser.test.ts index 779d9e400..651837f18 100644 --- a/apps/server/src/utils/__tests__/parser.test.ts +++ b/apps/server/src/utils/__tests__/parser.test.ts @@ -550,7 +550,7 @@ describe('test aliases import', () => { pathAndParams: 'testpathAndParams', }, ], - } as DatabaseModel; + } as unknown as DatabaseModel; const parsed = parseUrlPresets(testData); expect(parsed.length).toBe(1); @@ -602,7 +602,7 @@ describe('test views import', () => { app: 'ontime', version: '2.0.0', }, - } as DatabaseModel; + } as unknown as DatabaseModel; const parsed = parseViewSettings(testData); expect(parsed).toStrictEqual(dbModel.viewSettings); });