コンフルページをcopyしたら


### 背景・状況
現チームでは、日常的なスクラムイベントで利用するコンフルページが１スプリントで新規作成する。
イメージ的には、こいう感じです。
前スプリントのページをコピーし、タイトルを当日の日付を書き換えるという作業だった。

上記手順は定形作業であるが地味にめんどくさい作業だった。
固定作業となっているコンフルの作成タスクを軽減する目的で、自動化ソースが公開してくれていたため、この場で共有させてください。


### できること

基本的には下記のGASを定期実行しています。

コードを読んでもらうのが早いですが、やっているところは
1. 日時でGASを実行（トリガー）
2. 営業日かどうか判断してAPI投げるかを判断
3. コンフルAPIを投げて、作成したいページの前回のコンフルページを検索（本文とタイトルを取得）
4. 作成したい当日のコンフルページを作成。作成の際にbodyに本文やタイトルを入れてページを作成


簡単に言うと、翌週のMTG用ページを実行週のページをコピーする形で作成

例：
1. 2023年2月20日の夜にGasのMain関数が動く
2. 営業日なので、コンフルページ作成用のコードを動かす
3. 今週分である「2023-02-20週PO/SMデイリー」のページを検索し本文とタイトルを取得
4. 日付をインクリメントして「2023-02-27週PO/SMデイリー」というページを作成するAPIを投げる


起動時間の登録は画面左側の時計のアイコン”トリガー”より設定が可能
![agile.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/471610/1ed77208-ff93-7bc8-b545-46ce6032876f.png)


```
// 指定された日が営業日か（営業日 = 「土日でない」「祝日カレンダーに予定がない」）
// 営業日 = true
function isWorkday (targetDate) {

  // targetDate の曜日を確認、月曜以外は休む (false)
  var rest_or_work = ["REST","mon","REST","REST","REST","REST","REST"]; // 日〜土
  var youbi_num = targetDate.getDay() //取得した日付の曜日が0~6の数字で返却される
  if ( rest_or_work [youbi_num] == "REST" ) { //rest_or_workのリストは月曜日以外はRESTとしている
    console.log("実行しません")
    return false;
  }; 

  // // 祝日カレンダーを確認する
  // var calJpHolidayUrl = "ja.japanese#holiday@group.v.calendar.google.com";
  // var calJpHoliday = CalendarApp.getCalendarById (calJpHolidayUrl);
  // if (calJpHoliday.getEventsForDay (targetDate).length != 0) {
  //   // その日に予定がなにか入っている = 祝祭日 = 営業日じゃない (false)
  //   return false;
  // } ;

  // 全て当てはまらなければ営業日 (True)
  return true;
}

// main
function main () {

  var today = new Date ();

  // 実行日であれば実行
  if (isWorkday (today) == true) {
  post_content();
  console.log("実行します")
  }
}
```
```
const API_BASE_URL = "https://API_BASE_URL.atlassian.net/wiki";
// // API情報を定義
// // APIで表示及び更新したいコンフルのワークスペース
var user_id = {Googleアカウント};
var token = {Google Token};
// コンテンツ作成
function post_content() {
  var page_title = "週 PO/SMデイリー"
  var stringtoday = getToday().toString(); 
  var next_monday = getSevenDaysLater();
  var id = get_contentid(page_title,stringtoday);
  console.log("コピー元ページのid", id);
  var body = get_contentbody(page_title,stringtoday);
  var response = create_contents(id,next_monday,page_title,body);
 return;
}
// コンテンツID取得
function get_contentid(page_title,stringtoday) {
  var options = {
    'headers': create_headers(user_id, token),
    'method': 'get',
    'muteHttpExceptions': true,
  };

  var title =  "\"" + stringtoday + page_title + "\"";
  console.log(title);
  var space = "\"AUTHRESTORED\"";
  var api_url = "https://spacename.atlassian.net/wiki/rest/api/content/search?expand=ancestors&cql=space="+space+ "+and+title=";
  var urlEncoded = encodeURI(api_url)+encodeURIComponent(title);
  var response = UrlFetchApp.fetch(urlEncoded, options).getContentText('UTF-8');
  //console.log(response);
  var content_json = JSON.parse(response);

  var length = Object.keys(content_json.results[0].ancestors).length;
  console.log(content_json.results[0].ancestors[length - 1].id)
  return content_json.results[0].ancestors[length - 1].id;
}
// コンテンツボディ取得
function get_contentbody(page_title,stringtoday) {
    
 var options = {
    'headers': create_headers(user_id, token),
    'method': 'get',
    'muteHttpExceptions': true,
 };

 var sprint = "SP#";
 var title =  "\"" + stringtoday + page_title + "\""; 
 console.log("取得したページタイトル", title)
 var space = “\”{コンフル該当スペース名}\””;

 var api_url = "https://spacename.atlassian.net/wiki/rest/api/content/search?expand=body.storage&cql=space="+space+ "+and+title=";
 var urlEncoded = encodeURI(api_url)+encodeURIComponent(title)+encodeURI("&?expand=body.storage");
 var response = UrlFetchApp.fetch(urlEncoded, options).getContentText('UTF-8');
 console.log(api_url+title+"&?expand=body.storage");
 console.log(urlEncoded);
 console.log(response);
 var content_json = JSON.parse(response);
 console.log(content_json.results[0]);
  return content_json.results[0].body;
}
//コンテンツ作成
function create_contents(id,next_monday,page_title,body){
  var payload = {
    'type': 'page',
    'space': { 'key': "コンフル該当スペース名" },
    'ancestors': [{ 'id': id }],
    'title': next_monday + page_title,
    'body': body,
    "metadata": {
            "properties": {
                "editor": {
                    "key": "editor",
                    "value": "v2"
                }
            }
          }
  };
  payload = JSON.stringify(payload);
  var options = {
    'headers': create_headers(user_id, token),
    'payload': payload,
    'muteHttpExceptions': true,
    'method': 'post'
  };
  var expand_string = "body.storage";
  var api_url = API_BASE_URL + '/rest/api/content/' + '?expand=' + expand_string;
  console.log(api_url);
  var response = UrlFetchApp.fetch(api_url, options).getContentText('UTF-8');
  content_json = JSON.parse(response);
  return content_json;
}
/* 認証ヘッダー作成関数 */
function create_headers(user_id, token) {
  return {
    'content-type': 'application/json',
    'Authorization': 'Basic ' + Utilities.base64Encode(user_id + ':' + token)
  };
}

// 日付取得関数
function getToday() {
  var dt = new Date();
  var d1 = Utilities.formatDate(dt, 'Asia/Tokyo', 'yyyy-MM-dd');
  return d1;
  
}

// 一週間後の日付を取得(週初めの日付取得なので来週月曜日の日付を取得)
function getSevenDaysLater() {
  var dt = new Date();
  dt.setDate(dt.getDate() + 7);
  var d2 = Utilities.formatDate(dt, 'Asia/Tokyo', 'yyyy-MM-dd');
  return d2
}

```