Как разработать веб-приложение Google Apps Script с разными этапами / разными учетными записями GSuite?

Мне было интересно, как иметь разные этапы (dev. Prod) при разработке веб-приложения Google Apps Script. Afaik нет ничего из коробки, особенно если вы хотите иметь одну учетную запись G Suite для разработчиков, а другую для prod.

Единственное решение, которое я придумал, - это использовать Clasp и переключаться между двумя GSuite. Аккаунты с clasp logout / login

Мои вопросы:

  • Работает ли это, или есть ли ссылка на одну учетную запись GSuite, которая ломается при переходе на другую учетную запись?
  • Есть ли лучший подход?

Думаю, лучше пояснить это на примере:

У меня есть настройка проекта Google App Script с застежкой на домене test.mydomain.com с пользователем [email protected]

Теперь мне нужно передать это с помощью застежки в другой проект (с тем же именем) в другом домене prod.mydomain.com. Этот проект доступен другому пользователю, у которого есть учетные данные.

Проблема в том, что когда я теперь вхожу в систему с пользователем в застежке, мне нужно сначала создать проект, верно? Но проект уже существует. Так как я могу с этим справиться?


person zlZimon    schedule 29.06.2020    source источник
comment
Очевидно, вам просто нужно создать 2 разных проекта застежки.   -  person Kos    schedule 29.06.2020
comment
zlZimon, не могли бы вы пояснить, почему использование другой учетной записи для производства означает? Если веб-приложение запускается от имени владельца, оно всегда будет запускаться таким образом, и вы можете отправить его из любой учетной записи, у которой есть доступ. В противном случае он будет работать с правами доступа пользователя, и вы по-прежнему сможете отправлять сообщения из любой учетной записи. Если вы не хотите исчерпывать прод. квоты аккаунта - вы можете авторизоваться локально. Мне честно любопытно - не могли бы вы подробнее рассказать о своем сценарии использования?   -  person Oleg Valter    schedule 29.06.2020


Ответы (1)


Это работает?

Да, просто убедитесь, что у учетной записи есть права на редактирование, чтобы можно было отправлять сообщения (но я уверен, что вы это знаете).

Есть ли лучший подход?

Вот что-то вроде (если вы неявно это имели в виду), если процитировать выпуск № 42. проекта застежки GitHub:

Новый флаг:

clasp login --local Сохраняет .clasprc.json локально, но использует глобальные учетные данные clasp.

clasp login # global для всех проектов
clasp login --creds creds.json # ваши собственные кредиты
clasp login --local # локальный проект. Полезно для нескольких учетных записей Google.
clasp login --creds creds.json --local # own creds, local

Таким образом, технически вы можете использовать несколько учетных записей, но, в конце концов, все сводится к вашей методике login / logout.


Пересматривая обсуждение переключения между проектами скриптов приложений с помощью CLASP, я закончил тем, что написал базовый служебный скрипт для переключения между проектами скриптов приложений, на которые нужно нажимать (без зависимостей, но если вы хотите управлять флагами в стиле, ознакомьтесь с популярным Yargs):

const fs = require('fs').promises;
const { promisify } = require("util");
const { spawn } = require("child_process");

const rl = require("readline");

const promiseAns = () => {
    const dummy = rl.createInterface({
        input: process.stdin
    });

    dummy.question[promisify.custom] = function (query) {
        return new Promise((resolve) => this.question( query, resolve));
    };

    return promisify(dummy.question);
};



/**
 * @summary asks to confirm and exits if ok
 * @param {import("readline").Interface} cons 
 * @param {string} init
 */
const checkExit = async (cons, init) =>{ 

    if ( !/exit/.test(init) ) {
        return;
    }

    const question = promiseAns();

    const ans = await question.bind(cons)(`Are you sure (Y|n)?\n`);

    if (/^Y(?:es)?/i.test(ans)) {
        process.exit();
    }
}

/**
 * @summary repeat question until matches
 * @param {import("readline").Interface} cons 
 * @param {string} query 
 * @param {(ans: string) => boolean} condition 
 * @param {(ans: string) => any} onSuccess 
 */
const askUntil = (cons, query, condition, onSuccess) => cons.question(query, async (ans) => {

    await checkExit(cons, ans);

    if (!condition(ans)) {
        return askUntil(cons, query, condition, onSuccess);
    }

    onSuccess(ans);
});

/**
 * @summary makes readline interface
 */
const makeRL = () => {

    const cons = rl.createInterface({
        input: process.stdin,
        output: process.stdout,
    });

    cons.on("line", (ln) => console.log(ln));

    return cons;
};

process.on("unhandledRejection", (err) => {
    console.log(err);
    process.exit();
});

const APP_CONFIG = {

    paths: {

        dotclasp: "./.clasp.json",

        ids: "./ids.txt"

    }

};

(async (configuration) => {

    const cons = makeRL();

    const { paths: { dotclasp, ids } } = configuration;

    const text = await fs.readFile(ids, { encoding: "utf-8" });

    const lines = text.split(/\r?\n/);

    const relevant = lines.filter((line) => /^(\w+)\s+\w+/.test(line));

    const lookup = {};

    relevant.forEach((lbl) => {
        const [label, id] = lbl.split(/\s+/);
        lookup[label] = id;
    });

    const config = require(dotclasp);

    const [label] = Object.entries(lookup).find(([, id]) => config.scriptId === id);

    cons.emit("line", `Currently selected: ${label}`);

    const { argv } = process;

    const push = argv.includes("--push");

    askUntil(cons, `Select project (${Object.keys(lookup).join(" | ")})\n`, (ans) => lookup[ans], async (ans) => {

        config.scriptId = lookup[ans];

        try {
            await fs.writeFile(dotclasp, JSON.stringify(config), { encoding: "utf-8" });
            cons.emit("line", `switched to ${ans}`);
        }
        catch {
            cons.emit("line", `failed to switch to ${ans}`);
        }

        if (!push) {
            process.exit();
        }

        const cp = spawn(`clasp push --force`, {
            stdio: "inherit",
            shell: true
        });

        cp.on("error", ({ message }) => {
            cons.write(message);
            process.exit();
        });

        cp.on("exit", () => {
            cons.emit("line", `pushed to ${ans}`);
            process.exit();
        });

    });

})(APP_CONFIG);

пробный запуск

person Oleg Valter    schedule 29.06.2020
comment
Ну, когда я пытаюсь это сделать, он не находит scriptId. Поэтому в дополнение к вашему подходу мне нужно вручную изменить ScriptId в .clasp.json - person zlZimon; 02.07.2020
comment
Привет @zIZimon - хм, это требуется только в том случае, если вы используете несколько проектов. Если это произойдет в одном проекте с несколькими учетными записями, как указано в вашем вопросе, моя ставка будет на ошибку в застежке - person Oleg Valter; 04.07.2020
comment
Ну да, один проект GCP, который находится в среде разработки, и один проект GCP, который находится в среде prod. Как еще можно это получить? - person zlZimon; 05.07.2020
comment
@zlZimon - нет, два GCP в порядке, мне было интересно узнать о двух проектах скриптов приложений Google - поскольку для получения не удалось найти scriptId, вы не должны совместно использовать доступ между двумя учетными записями к одному и тому же проекту скрипта. Если все же поделятся - то это похоже на ошибку - person Oleg Valter; 05.07.2020
comment
Мое плохое, мое объяснение было неверным. У меня есть одна кодовая база (локально), где я использую clasp, и 2 проекта GCP, где один - dev, а другой - prod. Afaik one Apps Script Project можно связать только с одним проектом GCP. Поэтому мне нужен один проект скрипта приложений для каждого. Итак, когда я нажимаю на первый и хочу перейти на второй, мне также нужно изменить scriptId, чтобы это сделать, верно? - person zlZimon; 05.07.2020
comment
@zlZimon - ах, спасибо за объяснение - теперь в этом больше смысла! Да, я думаю, что (в настоящее время) нет никакого способа обойти это - если вам нужно изменить проекты сценариев приложений Google, тогда да, вам придется каждый раз менять scriptIds (так я делаю это и для одного из клиентов). Можно написать небольшой сценарий загрузчика, который будет переключать идентификаторы в зависимости от загруженных учетных данных, но я думаю, это больше проблем, чем того стоит. Вы можете открыть для этого запрос функции, кстати, это может быть что-то, что команда разработчиков захочет разработать. - person Oleg Valter; 05.07.2020