Automation PilotのコマンドをAPIから起動（認証方法について）

## この記事の目的
Automation Pilotのコマンドを外部からAPIで起動する方法について確認します。
Automation PilotのAPIはSAP Business Accelerator Hubに載っていますが、認証のためのクレデンシャルの取得方法が特殊だったため、記事にします。

https://api.sap.com/package/SAPCloudPlatformAutomationPilot/rest

## Automation Pilotの認証方法
通常BTPのサービスをAPIで呼び出す場合、①サービスキーを作成し、②サービスキーに記載されたclientid, clientsecretなどを使用してOAuth 2.0のトークンを取得、という流れになります。

一方、Automation Pilotではサービスの中で"Service Account"を作成する必要があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/5d8e7799-bcf4-b0f2-5ee5-fade7f794f69.png)

Service Accountを作成する際に選択できる認証方式は2つあります。
参考：[Service Account | Help Portal](https://help.sap.com/docs/automation-pilot/automation-pilot/service-account)

### Basic Authentication
ユーザID、パスワードでの認証。パスワードは自動で生成され、180日で失効する。同時に2つのパスワードを保持しておくことができる。

### Cloud PKI Client Certificate Authentication
X.509クライアント証明書を使った認証。証明書はBTPのDestination経由で発行される。

## Cloud PKI Client Certificate Authenticationを使ってAPIを呼び出し
以下の構成で、クライアント証明書を使った認証方法でAPIを起動してみます。

![Automation Pilot API.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/3f754c29-3a20-54f6-1b5b-9143ce9db6c5.png)

**ステップ**
1. SAP Cloud PKI インフラストラクチャ証明書の生成
1. Destinationを作成
1. Automation Pilotでサービスアカウントを作成
1. CAPからAutomation PilotのAPIを呼び出し

### 1. SAP Cloud PKI インフラストラクチャ証明書の生成
参考：[Use Destination Certificates | Help Portal](https://help.sap.com/docs/connectivity/sap-btp-connectivity-cf/use-destination-certificates)

#### 1.1. 証明書の作成
BTPコックピットのDestinationsの画面から、**Certificates**をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/f419d53c-1d62-d967-482c-095143762a3b.png)

**Generate Certificate**をクリックして証明書を生成します。（もう一つのボタン：Upload Certificateは手元にある証明書をアップロードする場合に使う）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/821e7b58-00bd-df32-7f9c-19e95c49d69b.png)

以下のように入力して、**Generate Certificate**をクリックします。
（Certificate File Name, Certificate Common Nameには任意の値を設定。Certificate Validity Valueには証明書の有効期限を設定する）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/86b8587c-cd57-864a-f45c-ebd928ddbcfd.png)


### 2. Destinationを作成
Automation PilotのAPIを呼ぶためのDestinationを登録します。

#### 2.1. Automation PilotのURLを確認
Automation Pilotの"API"画面にあるBase URLを確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/bdc4846d-aace-6e41-e1b0-b8f189f855b3.png)

#### 2.2. Destinationを登録
以下の設定でDestinationを登録します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/9dd9cf01-4ac5-8764-09e3-efb2d85f5cc1.png)

|項目  |設定値  |
|---|---|
|Name  |任意の名前  |
|Type  |HTTP  |
|URL|2.1.で確認したBase URL|
|Proxy Type|Internet|
|Authentication|ClientCertificateAuthentication|
|Key Store Location|1.で作成した証明書の名前をドロップダウンから選択|

#### 2.3. Destinationからキーチェーンを取得
Destinationから証明書をダウンロードし、キーチェーンを取得します。取得したキーチェーンはステップ3でAutomation Pilotのサービスアカウントに設定します。

Destinationをダウンロードします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/9bcd08fd-db7f-e475-2e7f-39a476484595.png)

zipファイルがダウンロードされるので、解凍して中に含まれる`.pem`ファイルを取得します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/aa72df8b-dd3a-81c3-0940-4e91fb4b6319.png)

ファイルはbase64でエンコードされているので、デコードします。
```:linux
base64 -d <input.pem> > output.pem
```

デコードされたファイルから以下の部分で囲まれたプライベートキーを削除します。
```
-----BEGIN PRIVATE KEY-----
-----END PRIVATE KEY-----
```

### 3. Automation Pilotでサービスアカウントを作成
Automation Pilotにサービスアカウントを作成し、ステップ2で取得したDestinationのキーチェーンをアップロードします。

Service Accounts画面から**Create**をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/6820f5a5-4ae8-b073-d714-676638e80ecf.png)

以下のように設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/b2633347-c19e-f39b-3084-b7e17514c5f5.png)

|項目  |設定値  |
|---|---|
|Username (suffix)  |任意の名前  |
|Permission  |APIで実行したいことに応じて設定（※） |
|Authentication Type|SAP Cloud PKI Client Certificate|
|Certificate Chain|ステップ2.3.で取得したキーチェーン|

※参考：[Permissions and Roles | Help Portal](https://help.sap.com/docs/automation-pilot/automation-pilot/permissions-and-roles)

### 4. CAPからAutomation PilotのAPIを呼び出し
CAP (Node.js)でAutomation PilotのAPIを呼び出し、コマンドを実行します。
コードは以下のGitリポジトリにあります。

https://github.com/miyasuta/cap-automation-pilot

#### 4.1. Automation PilotのAPI仕様をダウンロードします。
[SAP Business Accelerator Hub](https://api.sap.com/api/ExecutionsAPI/overview)からAPI仕様をJSON形式でダウンロード
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/837f61c3-592e-043e-a9fc-8633c3b9be5b.png)

#### 4.2. CAPプロジェクトにAPI仕様をインポート
プロジェクトのルートディレクトリにダウンロードしたAPI仕様を置いて、以下のコマンドを実行します。
```
cds import <input_file> --as cds
//例
cds import ExecutionsAPI.json --as cds
```

#### 4.3. package.jsonの定義
以下の設定を追加します。"destination"には、ステップ2.で登録したDestinationを設定します。
```json
  "cds": {
    "requires": {
      "auth": "mocked",
      "Executions": {
        "kind": "rest",
        "credentials": {
          "destination": "automationPilot",
          "path": "/api/v1"
        },
        "[production]": {
          "credentials": {
            "destination": "automationPilot",
            "path": "/api/v1"
          }
        }
      }
    }
  }
```
#### 4.4. サービスの定義
srv/automation-pilot.cdsで以下のサービスを定義します。function `triggerCommand`は受け取ったコマンドをAutomation Pilotに渡して実行します。

```
using { Executions_types } from './external/ExecutionsAPI';

service AutomationPilotService {
     function triggerCommand(commandId: String) returns Executions_types.execution;
}
```
返り値の型には、インポートの結果作成されたsrv/external/ExecutionsAPI.cdsで定義されている`Executions_types.execution`を使っています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/b287d831-1582-a240-e918-4918213586b6.png)

#### 4.5. イベントハンドラの実装
srv/automation-pilot.jsでイベントハンドラを実装します。

```javascript
const cds = require('@sap/cds')

module.exports=class AutomationPilotService extends cds.ApplicationService{
    init(){
        this.on('triggerCommand', async(req) => {
            const commandId = req.data.commandId
            const executionApi = await cds.connect.to('Executions')
            const response = await executionApi.send('POST', '/executions', {
                commandId: commandId
            })
            return response
        })
        return super.init()
    }
}
```

#### 4.6. テスト実行
VS CodeのRest Clientから以下のリクエストを送ってみます。

```
POST {{server}}/odata/v4/automation-pilot/triggerCommand
Content-Type: application/json

{
    "commandId": "welcome-T001565R7:AdvancedInputTransformer:1"
}
```
コマンドIDは、`<catalog>:<command>:<version>`の形式で指定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/14365c40-19b3-0fae-07c4-db049b9263c3.png)

以下のようにレスポンスが返ってくれば成功です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/20cff0f7-f1b8-4d95-490e-2a18f4fda69f.png)

Automation Pilot側で実行結果を確認すると、Ownerがサービスアカウントで実行されていました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/0b02aec0-70c2-6a84-e3eb-1a5f5fdda959.png)

## まとめ
- Automation PilotのコマンドをAPI経由で起動するには、サービスアカウントを作成し、その認証情報を使う
- 利用可能な認証方法には、ベーシック認証とクライアント証明書を使った認証がある