Workato カスタムコネクター開発：アクションの出力項目をレシピ作成時にカスタマイズする方法

# はじめに

Workato では、標準ではサポートされていないアプリケーションに対して、自分でコネクターを作成するコネクター SDK があります。

コネクター作成時に、アクションの入出力の項目の名称や型（スキーマ）を設定しますが、コネクターの作成時にはスキーマを確定できない場合があります。
例えば、DBMS などでは、テーブルの型によって、更新時の入力や検索結果の出力の項目は変わってきます。
そのため、レシピ作成時に入出力のスキーマを変更する必要があります。

ここでは、2024 年 3 月現在で利用できる、レシピ作成時に出力用のスキーマを変更するための方法を説明します。

入力用のスキーマについては[こちらの記事](https://qiita.com/ykkoba/items/5ea9107f054370e9f1ce)を参照してください。

# レシピ作成時に出力用のスキーマを変更するには

Workato ではレシピ作成時にスキーマを変更するために、スキーマデザイナーという機能が提供されています。
コネクター作成時にこのスキーマデザイナーを利用することで、**レシピ作成時にスキーマを変更することが可能**となります。

# 出力項目設定時のスキーマデザイナーの利用方法

この記事では、 Qiita の REST API の呼び出しを例として説明します。

### カスタムコネクターの作成

1. Workato の [Tools] > [Connector SDK] で コネクター SDK を起動します。
![connector_sdk.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3755136/56a7fca1-e4ef-fd21-a57c-1daad4311908.png)

1. [New connector] でコネクターを新規作成します。
1. [Get guided from a Workato template] を選択して、[Next] をクリックします。
1. アプリケーション名に「Qiita」を入力して、[Go to editor] をクリックします。

ここでは認証認可にアクセストークンを使用する方法で Qiita へのコネクタを作成し、Get 用のアクションを定義します。
ここに関しての詳細は本記事では省略します。

``` ruby
{
  title: "Qiita Connector",

  connection: {
    fields: [
      {
        name: "token",
        control_type: "string",
        label: "Bearer token",
        optional: false,
      },
    ],

    authorization: {
      type: "custom_auth",

      apply: lambda do |connection|
        headers("Authorization": "Bearer #{connection["token"]}")
      end,
    },

    base_uri: lambda do |connection|
      "https://qiita.com/api/v2/"
    end,
  },

  test: lambda do |connection|
    get("authenticated_user")
  end,

  methods: {},

  object_definitions: {},

  actions: {
    get_action: {
      title: "Get Action",
      description: "Get Action",

      input_fields: lambda do |object_definitions, connection, config_fields|
        []
      end,

      execute: lambda do |connection, input|

      end,

      output_fields: lambda do |object_definitions|
        []
      end,
    },
  },

  triggers: {},

  pick_lists: {},
}
```


### 入力項目の設定
1. Get アクションの入力用のスキーマを object_definitions ブロックで設定します。
ここでは、呼び出し先の URL を定義します。

    ``` ruby
      input_fields: lambda do |object_definitions, connection, config_fields|
        object_definitions["get_action_input"]
      end,
    ```

    ``` ruby
    object_definitions: {
      get_action_input: {
        fields: lambda do |_connection, config_fields|
          [
            {
              name: "url",
              label: "URL",
              optional: false,
            },
          ]
        end
      },
    },
    ```

1. 出力スキーマカスタマイズ用の項目をスキーマデザイナーとして追加します。
出力スキーマの設定もユーザーによる入力にあたるため、入力項目として設定します。
スキーマデザイナーは、control_type に schema-designer を指定することで利用可能です。
入出力の項目定義に影響を与えるため、extends_schema に true を設定して、object_definitions 内で config_fields として参照できるようにします。

    ``` ruby
        [
          {
            name: "url",
            label: "URL",
            optional: false,
          },
          {
            name: "output",
            label: "Output",
            optional: true,
            sticky: true,
            extends_schema: true,
            control_type: "schema-designer",
            sample_data_type: "json_input",
          },
        ]
    ```


1. アクションの出力用のスキーマを object_definitions ブロックで設定します。

    ``` ruby
      output_fields: lambda do |object_definitions|
        object_definitions["action_output"]
      end,
    ```
    出力スキーマの設定があるときのみ評価するように if ブロックを追加します。
    出力項目として body を定義し、種別を object、入力された出力スキーマを properties として設定します。
    スキーマ情報は文字列なので、parse_json で解析します。

    ``` ruby
    action_output: {
      fields: lambda do |_connection, config_fields, object_definitions|
        if config_fields["output"].present?
          output_schema = parse_json(config_fields["output"])
          response_body = { name: "body" }
          response_body[:type] = "object"
          response_body[:properties] = output_schema

          [response_body]
        else
          []
        end
      end,
    },
    ```
    
### アクションの出力

入力された URL に対して、get メソッドを呼びだします。
レスポンスを action_output での記載に合わせて body の値として設定します。

``` ruby
execute: lambda do |connection, input|
    url = input['url']

    {
        "body": get(url)
    }
end,
```

### 動作確認１

ここでは、アクセストークンに紐付いたユーザーを取得します。

[URL] にユーザー情報取得のエンドポイントの authenticated_user を指定します。

![test1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3755136/c5feffc0-8a3b-37d6-fcce-e461593c99c4.png)

出力スキーマ情報はスキーマデザイナーで [Use JSON] を指定して、リファレンスのサンプル出力の JSON を貼り付けて設定します。

![test2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3755136/52608f75-b40c-4901-77a6-f37235598581.png)
![test3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3755136/8b2036d6-fd5d-e254-d3c2-46bbc5d41b59.png)

![test4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3755136/af24c69c-3963-6d51-3635-dbcc0d190766.png)

実行結果を見ると、出力項目名が実際の値ではなく、ラベルになっているため、マッピングがされていることがわかります。

![result1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3755136/139af372-0c7d-1d83-271d-5a3e0cb4f23b.png)

### 動作確認2

次に、タグの一覧を取得します。

[URL] にタグ一覧のエンドポイントの tags を指定します。

出力スキーマ情報はスキーマデザイナーで [Use JSON] を指定して、リファレンスのサンプル出力の JSON を貼り付けて設定します。

![tag1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3755136/81a51c11-e669-3843-f850-78b0234b3d3d.png)


実行結果を見ると、出力項目名がラベルになっておらず、実際の値のため、マッピングに失敗していることがわかります。

![tag2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3755136/6bec6293-ece9-573d-5ab6-22193a4807ee.png)

あらためて出力項目の設定を見ると、array という項目名で配列が定義されていることがわかります。

![tag1_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3755136/d7fa8609-a8d4-eab2-1382-223e13a6132d.png)

get のレスポンスには、array という項目がないため、マッピングに失敗しているようです。

### 出力項目の調整
タグ一覧のようにレスポンスとして直接配列が返ってくるケースのために、出力項目の処理を変更します。
出力スキーマの項目が一つだけで、配列の場合、レスポンスの配列を body として参照できるように設定します。

``` ruby 
    action_output: {
      fields: lambda do |_connection, config_fields, object_definitions|
        if config_fields["output"].present?
          output_schema = parse_json(config_fields["output"])
          response_body = { name: "body" }
            if output_schema.length == 1 and output_schema.dig(0, "type") == "array"
              response_body[:type] = "array"
              response_body[:properties] = output_schema.dig(0, "properties")
            else
              response_body[:type] = "object"
              response_body[:properties] = output_schema
            end

            [response_body]
        else
          []
        end
      end,
    },
```

### 動作確認3 
先程のテストを再度実行します。
実行結果を見ると、出力項目名が実際の値ではなく、ラベルになっているため、マッピングがされていることがわかります。

![tag3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3755136/e6ebfaca-f582-06e2-f742-c996c59d6237.png)


なお、今回の対応では、 {"tags": [{"name": "Ruby", "versions": ["0.0.1"]}]} のように、項目名がついて配列になっているパターンには対応できませんが、項目数を見ているため、出力スキーマの設定時にダミーの項目を追加することで回避可能です。
["a", "b"] のように、レスポンスの配列の中身がオブジェクトでないパターンにも対応していませんが、今回のやり方を応用することで対応できると思います。


# まとめ

まとめると、以下のようになります。

- Workato のカスタムコネクターでレシピ作成時にスキーマを指定したい場合は、スキーマデザイナーが利用できる
- レスポンスとして直接配列が返ってくるパターンに対応するためには出力項目の処理に工夫が必要


# おわりに

カスタムコネクターで出力項目をレシピ作成時に行いたいケースでは、スキーマデザイナーが利用できます。
出力項目にスキーマデザイナーを使う場合は、直接配列が返ってくるケースに工夫が必要になります。

Workatoの導入・導入後の活用などでお困りの場合、Workatoリセラーにご相談する方法もございます。お困りごとがございましたら、認定リセラーの[日立ソリューションズ](https://www.hitachi-solutions.co.jp/rpa/sp/products/workato/?utm_source=sns&utm_medium=qiita&utm_campaign=["62c7358305be7869372a"])へ是非ご相談ください。

# 参考リンク

* 株式会社 日立ソリューションズ
    * [Workato ご紹介サイト](https://www.hitachi-solutions.co.jp/rpa/sp/products/workato/?utm_source=sns&utm_medium=qiita&utm_campaign=["62c7358305be7869372a"])
* Workato 公式サイト
    * [Workato スキーマ](https://docs.workato.com/ja/developing-connectors/sdk/sdk-reference/schema.html)
* Qiita 公式サイト
    * [Qiita API v2ドキュメント](https://qiita.com/api/v2/docs)