【gRPC】 Unknown field name '' in initialization map entryエラーの発生原因と対処法

gRPC（ruby）で以下のようなエラーが発生した場合の原因と対処法。

> "error": "13 INTERNAL: Unknown field name '文字列' in initialization map entry.\\n\\n/app/grpc/lib/helpers/article_helper.rb:222:

初期化時に**指定した名前のフィールドが存在しない**というエラー。

丁寧に該当するエラーが発生したファイル名と行を教えてくれている。


<br>
##field nameとは？
フィールド名とはprotocol buffers（.protoファイル）で定義した要素を指している。

```:.proto
message <メーセージ名> {
  <型> <フィールド名> = <フィールド番号>;
  .
  .
  .
}
```

エラーは.protoファイルに渡したフィールド名が存在しないことを伝えている。



<br>
##発生状況と対処法

.rbに記述したサーバーコードで.protoファイルのクラス生成時に、存在しないフィールド名を渡したため。

```ruby:エラーが発生したコード
PJ::Model::ArticleItem.new("rowspan")`
```

名前空間PJ::ModelのクラスArticleItemのインスタンスを作成し、引数に"rowspan"を渡している。

この記述と.protoの対応は以下となる。

- ArticleItemがmessage名
- 引数で渡したrowspanがフィールド名

.protoファイルの指定したmessageで指定したフィールドを設定していないために発生したエラー。


<br>

```.proto:.protoファイル
//名前空間の設定
package pj.model;

message ArticleItem {
  ArticleItemTdTh td_th = 1;
}

message ArticleItemTdTh {
  int32 rowspan = 1;
  int32 colspan = 2;
}
```

該当するメッセージ名`ArticleItem`で使えるフィールド名は`td_th`。


###対処法
引数で渡すフィールド名を`td_th`に変更すればいい。

```ruby:
PJ::Model::ArticleItem.new("td_th")
```

以上でエラーが解消する。



<br>
##（補足）発生状況詳細
実際のコードはもう少し複雑なので、以下は参考。

```ruby:.rb
##エラーが発生した処理
def convert_article_item(article_item:)
    
    #変数item_typeの宣言
    item_type = article_item['item_type']

    ##rowspan & colspanの設定
    #item_typeが"TYPE_TD"かつ、値rowspanとcolspanが存在するなら
    #変数rowspanとcolspanに代入
    if item_type == "TYPE_TD" and (rowspan = article_item['rowspan'] and colspan = article_item['colspan'])

      #convert_item_td_thメソッドにキーワード引数で変数rowspanとcolspanを渡す。
      #実行結果を article_item_paramsに代入
      article_item_params['td_th'] = convert_item_td_th(rowspan: rowspan, colspan: colspan) 
    
    #item_typeの条件は複数存在
    elsif item_type = ....
    .
    .
    .
    
    end
    
   #メッセージ名ArticleItemに変数article_item_paramsに代入した値をフィールド名として渡す
   PJ::Model::ArticleItem.new(article_item_params)
end


def convert_item_td_th(rowspan:, colspan:)
    td_th_params = {}
    td_th_params['rowspan'] = rowspan if rowspan.present?
    td_th_params['colspan'] = colspan if colspan.present?
    
    PJ::Model::ArticleItemTdTh.new(td_th_params)
end
```

`item_type`に複数のパターンが存在する。

`TYPE_TD`かつ、`article_item['rowspan']`, `article_item['colspan']`が存在する時に、

`convert_item_td_th`メソッドを実行する。
キーワード引数として`rowspan`, `colspan`を渡す。

`td_th_paramas = {'rowspan' = rowspan, 'colspan' = colspan}`をフィールド名として、メッセージ`ArticleItemTdTh`に渡す。





```.proto:.proto
package pj.model;

message ArticleItem {
  //列挙型でTYPE_の種類だけフィールドを用意
  enum Type {
    TYPE_INVALID = 0;
    TYPE_DIV = 1;
    TYPE_CODE = 2;
    TYPE_TYPOGRAPHY = 9;
    TYPE_IMAGE = 10;
    TYPE_HTML = 11;
    TYPE_TEXT = 12;
    TYPE_LI = 14;
    TYPE_DT = 15;
    TYPE_DD = 16;
    TYPE_UL = 17;
    TYPE_OL = 18;
    TYPE_DL = 19;
    TYPE_TABLE = 20;
    TYPE_TR = 21;
    TYPE_TH = 22;
    TYPE_TD = 23;
    TYPE_TOC = 24; 
  }

  //該当するTYPE_をフィールドtypeに渡す。
  Type type = 3;

  //今回使っているのはここのArticleItemTdTh
  oneof type_details {
    ItemTypography typography = 10;
    ArticleItemImage image = 11;
    ArticleItemCode code = 12;
    ArticleItemHtml html = 13;
    ArticleItemToc toc = 14;
    ArticleItemTable table = 16;
    ArticleItemTdTh td_th = 17;
  }
}

//ArticleItemTdThの型とフィールド
message ArticleItemTdTh {
  int32 rowspan = 1;
  int32 colspan = 2;
}
```

`ArticleItemTdTh.new('td_th')`が、`Oneof`の`ArticleItemTdTh td_th`に対応する。

型`ArticleItemTdTh`はmessageの中で他のmessageを型として呼び出せる仕様を利用している。

`td_th = {rowspan:数値 , colspan:数値}`の形のデータが受け渡される。