🐓

GAS:200件以上のGoogleグループ(メンバーや設定)の一覧を出力する をリファクタする

2022/09/10に公開1

sawaさんのすんばらしいこちらの記事を参考に、リファクタしてみました。 
https://zenn.dev/saawaa/articles/b03ee184685b64
勝手にネタにしてごめんなさい。
すごく助かりました&勉強になりました。
ありがとうございます!

GAS

/**
 * GoogleWorkSpaceで設定されているグループを取得しシートに書き出す関数
 * 
 * @trigger{
 *  type: インストーラブルトリガー,
 *  user: XXXXX
 *  when: 毎日 午前0-1時
 * }
 * 
 * 参考URL
 * https://zenn.dev/saawaa/articles/b03ee184685b64
*/
function exportGoogleGroup() {
  // console.time('実行時間')
  const ss = SpreadsheetApp.getActiveSpreadsheet();

  //スプレッドシートの1つ目のシートを選択
  const sheet = ss.getSheetByName("example.co.jp");

  //対象ドメイン(example.co.jpの部分を置き換える)
  const domain = 'example.co.jp';

  //シートをクリア
  sheet.clear();

  //ヘッダー追加
  const values = [];
  values.push([
    "メールアドレス",
    "グループ名",
    "説明",
    "メンバー数",
    "オーナー",//追加分
    "マネージャー",//追加分
    "メンバー",
    "オーナーに連絡",
    "メンバーを表示",
    "会話を表示",
    "投稿を公開",
    "メンバーを管理",
    "グループに参加できるユーザー",
    "組織外のメンバーの許可",
  ]);

  //オーナーに連絡の設定状況を日本語表示させる
  const ownercontact = {
    ALL_MANAGERS_CAN_CONTACT: 'グループの管理者',
    ALL_MEMBERS_CAN_CONTACT: 'グループのメンバー',
    ALL_IN_DOMAIN_CAN_CONTACT: '組織全体',
    ANYONE_CAN_CONTACT: '外部'
  };

  //メンバーを表示と会話を表示の設定状況を日本語表示させる
  const access = {
    ALL_OWNERS_CAN_VIEW: 'グループのオーナー',
    ALL_MANAGERS_CAN_VIEW: 'グループの管理者',
    ALL_MEMBERS_CAN_VIEW: 'グループのメンバー',
    ALL_IN_DOMAIN_CAN_VIEW: '組織全体',
    ANYONE_CAN_VIEW: '外部'
  };

  //投稿を公開の設定状況を日本語表示させる
  const post = {
    NONE_CAN_POST: '投稿不可',
    ALL_OWNERS_CAN_POST: 'グループのオーナー',
    ALL_MANAGERS_CAN_POST: 'グループの管理者',
    ALL_MEMBERS_CAN_POST: 'グループのメンバー',
    ALL_IN_DOMAIN_CAN_POST: '組織全体',
    ANYONE_CAN_POST: '外部'
  };

  //メンバーを管理の設定状況を日本語表示させる
  const managemembers = {
    OWNERS_ONLY: 'グループのオーナー',
    OWNERS_AND_MANAGERS: 'グループの管理者',
    ALL_MEMBERS: 'グループのメンバー'
  };

  //グループに参加できるユーザーの設定を日本語表示させる
  const canjoin = {
    ALL_IN_DOMAIN_CAN_JOIN: '組織内のすべてのユーザーが参加できる',
    CAN_REQUEST_TO_JOIN: '組織内のすべてのユーザーがリクエストできる',
    INVITED_CAN_JOIN: '招待されたユーザーのみ'
  };

  //組織外のメンバーの許可の設定を日本語表示させる
  const allow = {
    'false': '不可',
    'true': '可'
  };

  let endFlag = false;
  let token = '';

  const groupsList = AdminDirectory.Groups.list({
    domain: domain,
    pageToken: token
  });

  //while文
  while (!endFlag) {
    try {

      // console.time('実行時間AdminDirectory.Groups')
      //グループ一覧の取得
      // console.timeEnd('実行時間AdminDirectory.Groups')
      // console.log(groupsList)
      // return

      for (let i = 0; i < groupsList.groups.length; i++) {

        //グループの基本情報を取得
        const value = [];
        value.push(groupsList.groups[i].email); //メールアドレス
        value.push(groupsList.groups[i].name); //グループ名
        value.push(groupsList.groups[i].description); //説明
        value.push(groupsList.groups[i].directMembersCount); //メンバー数

        // console.time('実行時間AdminDirectory.Members')
        //グループのメンバー情報の取得
        const members = AdminDirectory.Members.list(groupsList.groups[i].email).members;
        // console.timeEnd('実行時間AdminDirectory.Members')
        // console.log(members)

        //メンバー処理
        if (members) {
          //オーナ-データをセット
          //members.role = "OWNER" ,"MANAGER","MEMBER" このうち、オーナの場合情報取得、いなければ空白を返す
          //オーナーやマネージャは1名以上、2名とか3名の場合もある
          //配列の中のオブジェクト、その中のmembers.role を取りたい

          const owners = members.reduce((emails, member) => {
            if (member.role === "OWNER") {
              return emails += member.email + '\r\n';
            } else {
              return emails += '';
            }
          }, '')
          // console.log(owners);
          value.push(owners)

          const manegers = members.reduce((emails, member) => {
            if (member.role === "MANAGER") {
              return emails += member.email + '\r\n';
            } else {
              return emails += '';
            }
          }, '')
          // console.log(manegers);
          value.push(manegers)


          //メンバーデータをセット
          let memberData = '';
          for (let j = 0; j < members.length; j++) {

            //memberData.push(members[j].email);
            memberData += members[j].email + '\r\n';
          }
          value.push(memberData);
        } else {
          value.push(''); //オーナーなし
          value.push(''); //マネージャーなし
          value.push(''); //メンバーなし
        }

        //グループのプロパティを取得
        const group = AdminGroupsSettings.Groups.get(groupsList.groups[i].email);
        value.push(ownercontact[group.whoCanContactOwner]); //オーナーに連絡   
        value.push(access[group.whoCanViewMembership]); //メンバーを表示
        value.push(access[group.whoCanViewGroup]); //会話を表示
        value.push(post[group.whoCanPostMessage]); //投稿を公開
        value.push(managemembers[group.whoCanModerateMembers]); //メンバーを管理
        value.push(canjoin[group.whoCanJoin]); //グループに参加できるユーザー
        value.push(allow[group.allowExternalMembers]); //組織外のメンバーの許可
        // console.log(value.length)

        // if (value.length == 12) {
        // console.log(value)}
        values.push(value);
      }

      if (groupsList["nextPageToken"] == null) {
        console.log("Group nextPageToken null: " + groupsList["nextPageToken"])
        endFlag = true;
      } else {
        token = groupsList["nextPageToken"];
      }

    } catch (error) {
      console.log(error);
    }
  }

  // console.log(values)
  //取得したデータをスプレッドシートにセットする
  sheet.getRange(1, 1, values.length, values[0].length).setValues(values);

  //垂直方向の配置を指定する(上に配置:top, 真ん中に配置:middle, 下に配置:bottom)
  // sheet.getRange(1, 1, values.length, 12).setVerticalAlignment('middle')
  // console.timeEnd('実行時間')
}

改変した点

主な改変点は次の通りです。

  1. var宣言をconst・letに置き換えました。
  2. letはそれを使用している箇所の近くに置きました。
  3. const groupsList = AdminDirectory.Groups.list  は反復処理の外に置きました。
  4. sheet.getRangeの第4引数を数字決めうちにせず、values[0].lengthとしました。
  5. グループのオーナとマネージャの情報も取るようにしました(これをやろうと思って色々手を出してしまった)

reduce

mapとかreduce苦手なんですよね。
reduceよりfilter mapや flatMap が見やすいという話もあり。
理解しやすいのを使ったら良いのではとのアドバイスでした。
taitoさんありがとう!

// 以下すべて同じ

  // filterしてmap
members.filter(m => m.role === "OWNER").map(m => m.email);

  // flatMap (mapしてflat()する)
members.flatMap(m => m.role == "OWNER" ? m.email : []);

  // mapしてfilter
members.map(m => m.role == "OWNER" ? m.email : null).filter(Boolean);

  // reduce
members.reduce((acc, m) => m.role === "OWNER" ? acc.concat(m.email) : acc, []);

未解決

未解決の事項は次の通りです。もう疲れたのでこの辺で勘弁してください。

  1. API複数回呼び出してるのをどうにかしたい。   const group = AdminGroupsSettings.Groups.get(groupsList.groups[i].email);
  2. value.push がいっぱいあるので、なんか上手くできそうな気がする。
  3. if/else でネストが深くなってる。

Discussion

ミッツミッツ

使わせてもらいました!
一覧に出てきて感動しました〜。
使っていないと思われるグループの洗い出しが簡単になります。
ありがとうございました。ヽ(`・ω・´)ノ