Середовище виконання Apps Script
Бібліотека одна, але її функції працюють не в тих самих місцях. Від того, по який бік межі стоїть функція, залежить, чи вдасться покрити її модульним тестом і чи коштує вона звернення до сервісу.
Два роди функцій
Незалежні від середовища функції — це звичайний JavaScript. Вони приймають значення, повертають значення й ніколи не лізуть у глобальні об'єкти. До цього роду належить усе в lang, net, json, html, time та exception, а також ті частини appsscript, які лише перетворюють рядки й прості об'єкти: розбірники нотації A1, конвертери літер стовпців і порівняння GridRange.
Прив'язані до сервісів функції самі звертаються до глобального сервісу. Вони працюють лише всередині скрипта з виданими правами і в Node не працюють зовсім. Сьогодні це весь список:
Функція | До якого глобального об'єкта звертається |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
Усе інше в appsscript приймає об'єкт сервісу аргументом: appendRows пише в той Sheet, який ви йому дали, а requireSheet розглядає передане значення, а не здобуває його сам. Таким функціям живий об'єкт сервісу потрібен, щоб принести користь, але шукати його вони не йдуть — саме це й дозволяє підмінити їх у тесті.
Пакетність на межі
Кожне звернення до сервісу — це похід туди й назад, і Apps Script зараховує його до ліміту часу виконання. Помічники для запису існують саме тому: appendRows робить один виклик незалежно від кількості рядків, а цикл із appendRow — по виклику на рядок.
Те саме міркування слушне й для перевірок. Перевіряйте форму даних до того, як вони дійдуть до сервісу, а не після:
Range.setValues() відхиляє рваний масив, але збій надходить від сервісу, не повідомляючи, який рядок винен. Перевірка заздалегідь перетворює це на помилку, якою ви керуєте.
Квоти
Apps Script стежить за квотами на скрипт і на акаунт: час виконання, виклики кожного сервісу, тригери, запити URL та інше. Бібліотека їх не підвищує й не рахує — вона лише зменшує кількість звернень. Два наслідки, які варто тримати в голові:
Функція, що не залежить від середовища, не коштує нічого, крім процесорного часу. Переносьте перевірки й перетворення на цей бік межі всюди, де можете.
Функція, прив'язана до сервісу, може впасти з причин, не пов'язаних з аргументами. Якщо
isAdminкидаєAdminDirectoryException, це означає, що розширений сервіс Admin SDK не увімкнено в проєкті, а не що користувач не адміністратор.
Чинні ліміти опубліковано в Quotas for Google Services.
Тестування
Набір тестів працює на Vitest у Node, де глобальних об'єктів Apps Script немає зовсім. Саме тому поділ і важливий:
Незалежні від середовища функції покрито модульними тестами напряму. Їх більшість у
test/, імпорт іде через аліас@/.Прив'язані до сервісів — ні. Виклик у Node падає з
ReferenceErrorнаSpreadsheetAppчи іншому глобальному об'єкті, тому їх перевіряють руками на справжньому проєкті скрипта. Функції, які просто приймаютьSheet, стоять посередині: їх можна прогнати з об'єктом-заглушкою тієї самої форми, що й клас сервісу.
Коли додаєте функцію, перше питання до її будови таке: чи може вона приймати об'єкт сервісу параметром замість того, щоб здобувати його? Якщо може — вона стає тестованою, а код, що викликає, зберігає контроль над кількістю звернень до сервісу.
Як писати для обох боків
Бібліотека розрахована на середовище V8. Дві звички тримають код робочим по обидва боки межі:
Приймайте об'єкти сервісів аргументами.
appendRows(sheet, rows)працює всюди, звідки б не прийшовSheet; функція, яка всередині кличеSpreadsheetApp.getActiveSpreadsheet(), працює лише у прив'язаному скрипті.Відокремлюйте розбір від здобування.
parseA1Notation— чиста робота з рядками, вона запускається в Node і тому покрита тестами;getSheetById— той самий пошук, але виражений через активну таблицю, і тестами не покритий. Явна передача таблиці —getSheetById(id, spreadsheet)— повертає його назад за межу.
Цю документацію згенеровано нейромережею (Claude) на основі вихідного коду бібліотеки.