Redmine REST-APIを利用してcurlからチケット作成

# 環境

- Windows 10
- Redmine 4.1.1.stable
   - インストールには[Bitnami Redmine](https://bitnami.com/stack/redmine/installer)を利用
   - 参考: [Windows に Redmine 環境を簡単に構築する手順 - NJ-CLUCKER](https://nj-clucker.com/easy-way-to-install-redmine/)
- curl 7.55.1 (Windows) libcurl/7.55.1 WinSSL
   - [Windows 10 RS4に追加された「curl」と「tar」コマンド - ASCII.jp](https://ascii.jp/elem/000/001/630/1630041/)

以下、RedmineのURLを http://127.0.0.1/redmine/ とする。

# Redmine REST-API有効化

1. プロジェクトを作成する
1. `管理`->`設定`->`API`[^1]->`RESTによるWebサービスを有効にする`にチェックを入れる

[^1]: http://127.0.0.1/redmine/settings?tab=api

プロジェクトを作成しなくてもREST-APIは有効化できる。
チケットを作成するためにはプロジェクトが必要なので、なければ作っておく。

# APIアクセスキー取得

1. アカウントを作成する
1. `個人設定`[^2]->`APIアクセスキー`->`表示`[^3]

[^2]: http://127.0.0.1/redmine/my/account
[^3]: http://127.0.0.1/redmine/my/api_key

既存のアカウントがあればそちらを使用。
APIキーは`7de23362bf2dfc965dec1413d72bf8daf337681b`のような16進数(?)の羅列となっている。

# チケット作成

```bat
curl -X POST -H "Content-Type: application/json" -H "X-Redmine-API-Key: 7de23362bf2dfc965dec1413d72bf8daf337681b" -d @issue_test.json http://127.0.0.1/redmine/issues.json
```
```issue_test.json
{
	"issue": {
		"project_id": 1,
		"subject": "test1",
		"tracker_id": 1,
		"description": "message"
	}
}
```
```json:response
```

# 画像のアップロード

アップロードするファイルのパスの先頭に`@`を付けることを忘れないこと。`--data-binary @image.png`部分のこと。
`Content-Type: image/png`の方が正しいように思えるが成功しなかった。[^4]

[^4]: 別環境で試したので要確認

```bat
curl -X POST -H "Content-Type: application/octet-stream" -H "X-Redmine-API-Key: 7de23362bf2dfc965dec1413d72bf8daf337681b" -H "Expect:" --data-binary @image.png http://127.0.0.1/redmine/uploads.json?filename=image.png
```
```json:response
```

アップロードに成功したファイルはブラウザで`http://127.0.0.1/redmine/attachments/5`を開くと確認できる。末尾の`5`はレスポンスに含まれる`id`値に置き換えること。
アップロード時のURL指定(`uploads.json?filename=image.png`)の`filename`の指定を忘れるとファイル名がハッシュ値(?)になる。画像のプレビューはできなくなる。

## `Expect`ヘッダ

`-H "Expect:"`を指定しない場合、以下のようなログが出て失敗した。

```html:error-log
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 80 (#0)
> POST /redmine/uploads.json HTTP/1.1
> Host: 127.0.0.1
> User-Agent: curl/7.55.1
> Accept: */*
> Content-Type: application/octet-stream
> X-Redmine-API-Key: 7de23362bf2dfc965dec1413d72bf8daf337681b
> Content-Length: 2717
> Expect: 100-continue
>
* Done waiting for 100-continue
* We are completely uploaded and fine
< HTTP/1.1 502 Proxy Error
< Date: Fri, 29 May 2020 14:19:58 GMT
< Server: Apache
< X-Frame-Options: SAMEORIGIN
< Content-Length: 341
< Connection: close
< Content-Type: text/html; charset=iso-8859-1
<
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>502 Proxy Error</title>
</head><body>
<h1>Proxy Error</h1>
<p>The proxy server received an invalid
response from an upstream server.<br />
The proxy server could not handle the request<p>Reason: <strong>Error reading from remote server</strong></p></p>
</body></html>
* Closing connection 0
```

> [curl の Expect: 100-continue を抑制する - 氾濫原](https://lowreal.net/2018/07/19/1)
> curl は POST や PUT でリクエストボディの長さが長いなどの特定条件になると、まず Expect: 100-continue をつけてリクエストを送り、サーバ側の対応を待ってからリクエストボディを改めて送るという行儀が良い実装になっている。
> 
> しかし、特に IoT っぽい機器では Expect: 100-continue に対応していないものもあるので、抑制したい場合がでてくる。そういうときは以下のようにする。
> 
> curl -H "Expect:" -d ... url
> 
>  空の Expect ヘッダを指定することで上記のような挙動をしなくなり、最初からリクエストをフルで送りつけるようになる。 


# チケットへの画像添付

`token`の値は画像アップロード時のレスポンスに含まれている。
`uploads`は配列であることに注意。

```bat
curl -X POST -H "Content-Type: application/json" -H "X-Redmine-API-Key: 7de23362bf2dfc965dec1413d72bf8daf337681b" -d @redmine_attach.json http://127.0.0.1/redmine/issues.json
```
```redmine_attach.json
{
	"issue": {
		"project_id": 1,
		"subject": "test1",
		"tracker_id": 1,
		"description": "message",
		"uploads": [
			{
				"token": "10.dc2d8a87ce35ced5892bd5a68adaf82e9a6cf1d51bfb4afeae813980ccd44a1c",
				"filename": "image.png",
				"content_type": "image/png"
			}
		]
	}
}
```
```json:response
```

# TIPS

## Proxyを無効にする

### --proxy ""

```bat
--proxy ""
```

または

```bat
-x ""
```

### --no-proxy

特定IP(ホスト)のみ無効

```bat
--no-proxy "127.0.0.1"
```

ポート番号まで指定しないとうまく行かないことがあった。


```bat
--no-proxy "127.0.0.1:80"
```

# 参考

- Redmine公式
    - [Using the REST API with cURL](https://www.redmine.org/projects/redmine/wiki/Rest_api_with_curl)
    - [Redmine API](https://www.redmine.org/projects/redmine/wiki/Rest_api)
         - [Attaching files](https://www.redmine.org/projects/redmine/wiki/Rest_api#Attaching-files)
- [curl + 4 通りの方法で Redmine の API 認証を通してみる](https://qiita.com/tily/items/529375545de2c83d56ad)
- [Redmineをちょっと便利に! プログラミング無しで使ってみるREST API - SlideShare](https://www.slideshare.net/g_maeda/redmine-rest-api)
- [Redmine REST API - r-labs](https://www.r-labs.org/projects/r-labs/wiki/Redmine_REST_API) _redmine.org の Redmine API (version 53) の日本語訳_
- [Redmineへ新規チケットを登録する|運用自動化プラットフォーム Kompira](https://www.kompira.jp/column/post_to_redmine/)
- [RubyからREST API経由でRedmineに添付ファイルを登録する(XML,JSON使用) - Symfoware](https://symfoware.blog.fc2.com/blog-entry-1359.html)
- [よく使うcurlコマンドのオプション - Qiita](https://qiita.com/ryuichi1208/items/e4e1b27ff7d54a66dcd9)
- [curlコマンドでデータを送るときに、ファイルに保存されたデータを使う - Qiita](https://qiita.com/aki3061/items/b4f4bc5f4ef3fa015c5f)