【C＃】RedmineRESTAPIでCustom Field付きのIssueをPOSTする

# この記事について
この記事では、C＃でRedmine REST APIを使い、Custom Field付きのIssueをPOST（Create）する方法について記載します。
Redmineそのものや、RedmineのIssueにおけるCustom Fieldについての説明は特にいたしません。

Redmine REST APIの公式サイトはこちら：
https://www.redmine.org/projects/redmine/wiki/rest_api

# 背景
C＃でRedmine REST APIを使ってIssueをPOSTするにあたって、
Custom Fieldが付く場合のPOSTの仕方がどこにもなかったため、
備忘録を兼ねてC＃を使って当APIを叩く人向けに記載します。

RedmineとC＃に関しては、redmine-net-apiというライブラリが存在しますが、
Custom Fieldに値をセットしてIssueをPOSTするにあたり、
公式のQ&A通りに書いても動かない[^1]
＆
当ライブラリを使ってのセットの仕方が複雑かつ直感的でない[^2]
と感じたため、
このライブラリを使わず、最初からプロパティを設定し、
作ったIssueの情報をJSONにしてPOSTする方法を取りました。

redmine-net-apiについてはこちら：
https://github.com/zapadi/redmine-net-api/wiki

[^1]:https://github.com/zapadi/redmine-net-api/issues/283
[^2]:custom field内のIdのプロパティが{get; protected set;}になっている関係で、継承が絡んできて非常に面倒

# 使用環境
Visual Studio 2022

# コード
プログラム本体は下記。

```c#:redmine_post.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Http;
using System.Text.Json;


namespace redmine_post
{
    internal class Program
    {

        static void Main(string[]args)
        {
            /*
            APIの接続情報に関しては適宜設定
            */
            string RedmineUrl = "redmineのURL";
            string ApiKey = "redmineのAPIキー";
            var postUrl = RedmineUrl + "/issues.json?key=" + ApiKey;

            Issue issue = new Issue()
            {
                /*
                IDなどの基礎的な情報に関しては適宜設定
                */
                project_id = 1,
                tracker_id = 2,
                status_id = 3,
                priority_id = 4,
                subject = "題名",
                description = "内容",
                custom_fields = new List<Custom_Field>()
                {
                    /*
                    セットする内容は(idごとのvalue, カスタムフィールドのid)
                    */
                    new Custom_Field("カスタムフィールドの値1", 1),
                    new Custom_Field("カスタムフィールドの値2", 2),
                    new Custom_Field("カスタムフィールドの値3", 3)
                }
            };

            Dictionary<string, Issue> issues = new Dictionary<string, Issue>()
            {
                {"issue", issue}
            };

            /*
            作った{"issue": issue}型の辞書をJSONにシリアライズ
            */
            var jsonIssues = JsonSerializer.Serialize(issues);

            using (var client = new HttpClient())
            {
                var content = new StringContent(jsonIssues,Encoding.UTF8,"application/json");
                var response = client.PostAsync(postUrl,content).Result;
                var postContent = response.Content;
                var contntString = postContent.ReadAsStringAsync().Result;
                Console.WriteLine(response);
                Console.WriteLine(contentString);
            }
        }
    }
}
```

Issueのプロパティは別途クラスとして作成。

```c#:Issues.cs
using System.Collections.Generic;

namespace redmine_post
{
    public class Issue
    {
        public int project_id { get; set; }
        public int tracker_id { get; set; }
        public int status_id { get; set; }
        public int priority_id { get; set; }
        public string subject { get; set; }
        public string description { get; set; }

        public IList<Custom_Field> custom_fields { get; set;}
    }

    public class Custom_Field
    {
        public string value {get; set;}
        public int id {get; set;}
        /*
        プロパティであれば頭は大文字にするのが通例だと思うが、
        APIの仕様上、全て小文字でないとcustom fieldとして認識してくれないのでこの措置
        */

        public Custom_Field(string values, int ids)
        {
            value = values;

            id = ids;
        }

    public class Custom_Fields
        {
            public IList<Custom_Field> custom_fields { get; set;}
        }
    }
}
```

# おわりに
Issuesの中で足りない要素があれば適宜付け足して使ってください。