Redmine REST APIでハマったこと

# 背景
今年 `Redmine REST API` を使って下記のようなことをする機会がありました。

* 進捗管理をExcelからRedmineにmigration
* CIのリグレッションテストでFailしたテストケースをRedmineに自動起票

# 目的
今まで `Redmine REST API` は使ったことがなく、実装しながら学んでいきました。その中でハマったことを中心に皆さんに共有して、僕のはハマった罠を回避して頂きたいと思います。

# 前提
* javaをそこそこ理解している
* Redmineの機能をそこそこ理解している

# 環境
```cl:Redmine&nbsp;Version
Environment:
  Redmine version                3.4.6.stable
```

```cl:java&nbsp;Version
java version "1.8.0_221"
Java(TM) SE Runtime Environment (build 1.8.0_221-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.221-b11, mixed mode)
```

```xml:pom.xml
        <dependency>
            <groupId>com.taskadapter</groupId>
            <artifactId>redmine-java-api</artifactId>
            <version>4.0.0.preview.1</version>
        </dependency>
```

# ハマったこと
## 1. ユーザーに付与された権限にAPIの機能も制限される
Redmine REST APIを使うために最初に `RedmineManager`クラスを生成します。このときに `API KEY`というものを使用します。

```java
RedmineManager manager = RedmineManagerFactory.createWithApiKey(REDMINE_URL, API_KEY);
```
この `API KEY`はRedmineのユーザー一人一人に付与されます。当時、自分のユーザー権限は管理者権限ではありませんでした。APIのメソッドによってはRedmineのシステム管理者権限が必要なものがあります。

```java:全ユーザーを取得
    /**
     * Load list of users from the server.
     * <p><strong>This operation requires "Redmine Administrator" permission.</strong>
     * <p>
     * This method calls Redmine with "include = memberships,groups" parameter.
     *
     * @return list of User objects
     * @throws RedmineAuthenticationException invalid or no API access key is used with the server, which
     *                                 requires authorization. Check the constructor arguments.
     * @throws NotFoundException
     * @throws RedmineException
     */
    public List<User> getUsers() throws RedmineException {
        return transport.getObjectsList(User.class, new BasicNameValuePair(
                "include", "memberships,groups"));
    }
```

柔軟に利用したい場合、システム管理者権限になることをお勧めします。

## 2. `Transport` が必要
`redmine-java-api`のversion4以降から取り込まれた概念のようです。`RedmineManager`クラスから取得できます。

```java
RedmineManager redmineManager = RedmineManagerFactory.createWithApiKey(REDMINE_URL, API_KEY);
Transport transport = redmineManager.getTransport();
```

`Transport`がないとチケット作成・更新時に「Transportが未設定だぞ！」という例外がスローされます。なので作成・更新前に `Issue`のインスタンスに `Transport`を設定してあげてください。

```java:新規チケット作成
// Transportをセット
Issue issue = new Issue(manager.getTransport());
// （中略）
issue = issue.create();
```

```java:既存チケット更新
Issue issue = manager.getIssueManager().getIssueById(fetchedIssue.getTicketId());
// Transportをセット
issue.setTransport(manager.getTransport());
// （中略）
issue.update();
```

## 3. `Params`の使い方
既存チケットに更新をかけるとき、「この条件にあったチケット」を取得したいときがあります。そのときは `Params` を使うことで「この条件」を表現できます。

```java:チケットの題名が"actual.getFileName"に一致するという条件
Params().add("set_filter", "1")
        .add("f[]", "subject")
        .add("op[subject]", "=")
        .add("v[subject][]", actual.getFileName());
```
ハマったことは下記3点です。

### 題名以外（例：担当者）ときのキー値は？
ドキュメントを見つけられなかったので、力技で特定してました。チケット一覧のフィルタで希望の条件を設定するとURLのパラメータとしてフィルタ条件が表示されます。パラメータをデコードするとキーの値を特定できます。
![flow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352052/4dbea5ad-e453-3b05-0e3c-fc8c4f9628f5.png)

### 一致以外（例：一致しない、含む）のときは？
上の方法で解析。

### チケット全部取得したいから条件なしで取得したら500件しか取得できない
現状不明。方法をご存じの方がいたらご教示ください。

## 4. 既存チケットのカスタムフィールドを更新するときは一度clearする
一度既存チケットのカスタムフィールドを取得して、値を更新する。そのあとに既存チケットのカスタムフィールドを `clearCustomFields` してから追加しないとうまく更新されませんでした。

```java
// この処理で既存チケットからカスタムフィールドを取得して更新して、返却している
Collection<CustomField> customFieldCollection = setEachCustomField(issue, fetchedIssue);
issue.clearCustomFields();
issue.addCustomFields(customFieldCollection);
```

## 5. 日付型のカスタムフィールドに値を設定するときのフォーマット
`yyyy-MM-dd`一択です。これ以外はparseエラーになります。

以上（思いついたら追加していきます。）

# 参考
* [Redmineとは](http://redmine.jp/overview/)
* [Redmine REST API](https://github.com/taskadapter/redmine-java-api)