esa に API でファイルをアップロードする

[esa](https://esa.io) は [API](https://docs.esa.io/posts/102) が用意されているが、添付するファイルをアップロードする API はない。

昔のツイートですが、

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">まだドキュメントがなくて申し訳ないのですが、 <a href="https://t.co/PfsQRMNGHp">https://t.co/PfsQRMNGHp</a> の辺りを参考にしていただくと良いかもしれません。(esaのAPIを使ってS3のダイレクトアップロード用のメタデータを取得して、それを元にS3にアップロードするような形です)</p>&mdash; Atsuo Fukaya (@fukayatsu) <a href="https://twitter.com/fukayatsu/status/988670793184772096?ref_src=twsrc%5Etfw">April 24, 2018</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

とのことなので、やってみました。

## コマンドラインでお試し

### ダイレクトアップロード用のメタデータを取得する

メタデータを取得 API

- POST https://api.esa.io/v1/teams/:team_name/attachments/policies
  - type : Content-Type
  - name : ファイル名
  - size : ファイルサイズ

これを使ってアップロードしたいファイル用のメタデータを取得する。

    $ TEAM=yh1224
    $ ACCESS_TOKEN=xxxx
    $ FILE_NAME=test.png
    $ curl -X POST \
        -H "Authorization: Bearer ${ACCESS_TOKEN}" \
        -d "type=image/png" \
        -d "name=${FILE_NAME}" \
        -d "size=$(stat -f%z ${FILE_NAME})" \
        "https://api.esa.io/v1/teams/${TEAM}/attachments/policies" | jq
    {
        "attachment": {
            "endpoint": "https://xxxx.s3.ap-northeast-1.amazonaws.com",
            "url": "https://xxxx.s3.ap-northeast-1.amazonaws.com/uploads/production/attachments/XXXXX/YYYY/MM/DD/XXXXX/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.png"
        },
        "form": {
            "AWSAccessKeyId": "XXXXXXXXXXXXXXXXXXXX",
            "signature": "XXXXXXXXXXXXXXXXXXXXXXXXXXXX",
            "policy": "XXXXXXXXXXXXXXXXXXXXXXXXXXXX",
            "key": "uploads/production/attachments/XXXXX/YYYY/MM/DD/XXXXX/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.png",
            "Content-Type": "image/png",
            "Cache-Control": "max-age=31557600, public",
            "Content-Disposition": "filename*=UTF-8''test.png",
            "acl": "public-read"
        }
    }

### アップロード

[POST Object - Amazon S3 REST API](https://docs.aws.amazon.com/en_pv/AmazonS3/latest/API/RESTObjectPOST.html)

返ってきた `form` パラメタに画像ファイル(`file`パラメタ)を追加して、`attachment.endpoint` の URL に POST すれば良い。アップロードした画像の URL は、`attachment.url` でアクセスできる。

    $ curl -X POST \
        -H "Authorization: Bearer ${ACCESS_TOKEN}" \
        -d "type=image/png" \
        -d "name=${FILE_NAME}" \
        -d "size=$(stat -f%z ${FILE_NAME})" \
        "https://api.esa.io/v1/teams/${TEAM}/attachments/policies" \
        > policy.log
    $ curl -v \
        -F "file=@${FILE_NAME}" \
        -F "AWSAccessKeyId=$(jq -r '.form.AWSAccessKeyId' policy.log)" \
        -F "signature=$(jq -r '.form.signature' policy.log)" \
        -F "policy=$(jq -r '.form.policy' policy.log)" \
        -F "key=$(jq -r '.form.key' policy.log)" \
        -F "Content-Type=$(jq -r '.form."Content-Type"' policy.log)" \
        -F "Cache-Control=$(jq -r '.form."Cache-Control"' policy.log)" \
        -F "Content-Disposition=$(jq -r '.form."Content-Disposition"' policy.log)" \
        -F "acl=$(jq -r '.form.acl' policy.log)" \
        $(jq -r '.attachment.endpoint' policy.log)

できた。

## コード例

- [TypeScript](https://github.com/yh1224/marksync/blob/79d68389aac2b5f3f42403792520f12a6778adcf/src/marksync/remote/esa/api.ts#L259)
- [Kotlin](https://github.com/yh1224/marksync/blob/803ccd7f9fdbf87955fceeb8712ff68814aa1067/src/main/kotlin/marksync/remote/esa/EsaApiClient.kt#L192)