プリザンターのサーバースクリプトをスケジュール実行する

## バックグラウンドサーバースクリプト

つい先日（12月13日、ver 1.3.50.0）にリリースされたばかりの新機能の一つに、「バックグラウンドサーバースクリプト」があります。
これまでは、サーバースクリプトを実行するには、レコードの保存や画面表示など、ユーザが操作を行う必要がありました。
しかし、「バックグラウンドサーバースクリプト」を利用することで、事前にスケジュールしたタイミングでサーバースクリプトを実行できるようになりました。

## 試してみよう
では早速試してみましょう。
下記の記事でプロセスのボタンを押下することで実行していた「GitHub Issue取得」のスクリプトを下記スケジュールで定期実行するようにしてみたいと思います。

・毎日午前８時に、GitHub Issueを取得するサーバースクリプトを実行する

https://qiita.com/pierusan2010/items/e433756578250611b059

### 事前準備
#### バックグラウンドサーバースクリプトの有効化

- 本機能を利用するには、パラメータファイル[Script.json](https://pleasanter.org/manual/script-json) の`BackgroundServerScript`をtrueに設定しておく必要があります。

```json
{
    "ServerScript": true,
    "BackgroundServerScript": true,
    "DisableServerScriptHttpClient": false,
    "ServerScriptTimeOut": 10000,
    "ServerScriptTimeOutChangeable": false,
    "ServerScriptTimeOutMin": 0,
    "ServerScriptTimeOutMax": 86400000,
    "ServerScriptIncludeDepthLimit": 10
}
```
- また、本機能は[特権ユーザ](https://pleasanter.org/manual/user-management-privileged-users)に指定されたユーザのみが実施可能です。

### 設定手順

<details><summary>バックグラウンドサーバースクリプトによる、サーバースクリプトの定期実行</summary>

- 特権ユーザでログインし、「管理メニュー」から「テナントの管理」を選択します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28805/8a71eeff-6b16-022f-19d7-799d93b03fb1.png)

- 「サーバースクリプト」タブを選択します
- 「新規作成」ボタンをクリックします
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28805/991e615b-a9ec-ee42-956d-81a9220b8168.png)

- 「タイトル」を入力します
- 「実行ユーザ」には、GitHub Issueの登録先のテーブルに書き込み権限を持つユーザを指定します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28805/63f76cf5-ffdb-1c06-e909-4ece54a0f473.png) 

- 「サーバースクリプト」欄には下記のコードを入力します
```js
const targetSiteId = 61838;
const token = {your access token};
const url = "https://api.github.com/repos/pierre3/Implem.Pleasanter/issues?state=open";
httpClient.RequestHeaders.Add('User-Agent', 'Implem');
httpClient.RequestHeaders.Add('Authorization', 'Bearer ' + token);
httpClient.RequestUri = url;
let response = httpClient.Get();
if (httpClient.IsSuccess) {
    model.Body = 'Success!';
    var githubIssues = JSON.parse(response);
    githubIssues.forEach(item => {
        let data = {
            Keys: ["NumA"],
            Title: item.title,
            Body: item.body,
            NumHash: {
                NumA: item.number
            },
            ClassHash: {
                ClassA: item.html_url,
            }
        };
        let ret = items.Upsert(targetSiteId, JSON.stringify(data));
        if (ret === false) {
            model.Body = model.Body + '\nレコードの登録に失敗しました！　IssueNum:' + item.number + '\n';
        }
    });
} else {
    model.Body = 'Error: (' + httpClient.StatusCode + ')' + response;
}
```
:::note
「今すぐ実行」ボタンをクリックすると、入力したコードをその場で実行することができます。ここで入力したスクリプトが正常に動作することを確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28805/346c5ec7-485c-ac70-c257-35cf52cbd66c.png)
:::

- 「スケジュール」の「新規作成」ボタンをクリックし、下記の内容で設定します
  - 名称：毎日8時
  - 期間種別：毎日
  - 毎日 - 時刻：08:00
  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28805/2adc70d0-3433-e981-7df8-a64b4def1184.png)

これでわざわざボタンを押さなくても、定期的に自動でGitHub Issueを取りこんでくれるようになりました。

</details>

## 実行ログをプリザンターのテーブルに記録する

バックグラウンドサーバースクリプトにてスクリプトが実行された際にはSysLogsテーブルにログが記録されますが、現在のところ実行記録をUI上で手軽に確認する手段が用意されていません。

そこで、プリザンター上にバックグラウンドサーバースクリプトの実行記録用のテーブルを用意してそこに実行ログを記録するようにしてみたいと思います。

### 記録用のテーブル
まずは、実行記録用のテーブルを下記の設定で作成します。

- テーブル名：バックグラウンド実行ログ
- テーブル種別：記録テーブル

|項目名|項目種別|用途|
|:-|:-|:-|
|更新者|Updator|バックグラウンドサーバースクリプトの実行ユーザ|
|更新日時|UpdatedTime|ログの記録日時|
|ScriptName|Title|スクリプト名|
|LogType|ClassA|ログ種別（Start/End/Error）|
|メッセージ|Body|実行結果やエラーメッセージを表示する|

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28805/dfe4d5e4-2119-45b6-748a-d5c8752ea267.png)


### ログ記録用のクラスを定義

次に、下記の関数を持つログ記録用のクラス（MyLogger）を用意します。
- writeLog関数: 実行記録用テーブルにログレコードを追加します
- executeScript関数: 引数funcに指定した処理を実行します。  
  - 実行前にLogType='Start'のログを追加します
  - 実行後にLogType='End'のログを追加します
  - エラー発生時にはLogType='Error'のログを追加します

```js
class MyLogger {
    static writeLog(logType, scriptName, message) {
        const logSiteId = 61857; //ログ記録用テーブルのサイトID
        let item = items.NewResult();
        item.Title= scriptName;
        item.ClassA = logType;
        item.Body = message;
        items.Create(logSiteId, item);
    }
    static executeScript(scriptName, func) {
        this.writeLog('Start', scriptName);
        try {
            let ret = func();
            this.writeLog('End', scriptName, ret);
        } catch (e) {
            this.writeLog('Error', scriptName, e.stack);
        }
    }
}
```

「テナントの管理」-「サーバースクリプト」タブで「新規作成」をクリックし、上記のコードを「サーバースクリプト」欄に設定します。
その際、「共有」チェックボックスにチェックを付けておきます。「スケジュール」の指定は行いません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28805/82980668-7a03-d72b-a130-67a1bd108c18.png)

:::note
「共有」にチェックを付けたスクリプトには、全てのスクリプト内で利用可能な共通の定義などを記載しておくことができます。
:::


### ログ記録用の関数を使って処理を実行する

スケジュール実行するサーバースクリプトは全て、下記の様に`MyLogger.executeScript()`関数を通すようにします。

```js
MyLogger.executeScript('scriptNme', function() {
   //スケジュール実行したい処理を記載
   return '処理完了時のメッセージ';
}
```

- 第一引数に「スクリプト名」を渡します
- 第二引数にスケジュール実行したい処理を記述した関数を渡します
- 戻り値にはLogType='End'時に記録するメッセージを返します

これで準備OKです。

#### 使用例

例として、GitHub Issue取得用のスクリプトに加え、ダミーのスクリプトを２つ用意して実行結果を見てみたいと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28805/42dfdb17-bce5-2dca-bf4e-593b4a7c905e.png)

- スクリプト1（GitHub Issue取得用）
```js
MyLogger.executeScript('GitHub Issue取得', function() {
    const targetSiteId = 61838;
    const token = {your access token};
    const url = "https://api.github.com/repos/pierre3/Implem.Pleasanter/issues?state=open";
    httpClient.RequestHeaders.Add('User-Agent', 'Implem');
    httpClient.RequestHeaders.Add('Authorization', 'Bearer ' + token);
    httpClient.RequestUri = url;
    let response = httpClient.Get();
    if (httpClient.IsSuccess) {
        model.Body = 'Success!';
        var githubIssues = JSON.parse(response);
        githubIssues.forEach(item => {
            let data = {
                Keys: ["NumA"],
                Title: item.title,
                Body: item.body,
                NumHash: {
                    NumA: item.number
                },
                ClassHash: {
                    ClassA: item.html_url,
                }
            };
            let ret = items.Upsert(targetSiteId, JSON.stringify(data));
            if (ret === false) {
                model.Body = model.Body + '\nレコードの登録に失敗しました！　IssueNum:' + item.number + '\n';
            }
        });
    } else {
        model.Body = 'Error: (' + httpClient.StatusCode + ')' + response;
    }
    return model.Body;
});
```

- スクリプト2
```js
MyLogger.executeScript('月次バッチ', function() {
   return '20件処理しました';
});
```

- スクリプト３（エラー発生）
```js
MyLogger.executeScript('売上集計', function() {
   throw new Error('集計に必要なデータがありません');
});
```

#### 実行結果
設定したスクリプトを実行後、実行記録用テーブルを確認してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28805/4b01dca2-c8d2-bad9-1b3d-a45973924e4f.png)

これで
- どのスクリプトが
- 何時実行されて
- どのような結果となったか

が確認できるようになりました。
Startの時間とEndの時間の差分を取れば処理の所要時間も把握することができます。