バージョン
Python 3.11.6
何が起きたか
外部モジュールを使わずにNotion APIを叩こうと以下のようなコードを書いていました。
分かりやすくしてます.py
from json import dumps, loads
from urllib import request
endpoint = "https://api.notion.com/v1/pages/**"
headers = {
"Authorization": "Bearer **",
"Notion-Version": "2022-06-28",
"Content-Type": "application/json",
}
payload = {
"properties": {
"リリース日": {
"date": {
"start": "2023-12-01"
}
}
}
}
req = request.Request(endpoint, dumps(payload).encode("utf-8"), headers, method="PATCH")
try:
with request.urlopen(req) as res_:
res = loads(res_.read().decode("utf-8"))
...
except Exception as err:
...
このコードはしばらくの間なにごともなく動いていたのですが、ある日ログにこのようなメッセージを見つけます。
HTTP Error 400: Bad Request
なんもわからん。
とりあえずExceptionに何か入ってるだろうと思い中身を漁ってみてもBad Request
以上の情報が見つからず…。
APIリファレンスを見るとどうやらレスポンスのbodyにオブジェクトが入っているらしいということが分かったので、試しにcurlから同様のリクエストを送ってみました。
リクエスト
curl 'https://api.notion.com/v1/pages/**' \
-X PATCH \
-H 'Notion-Version: 2022-06-28' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer **' \
-d '{"properties":{"リリース日":{"date":{"start":"2023-12-01"}}}}'
すると…
レスポンス
{
"object": "error",
"status": 400,
"code": "validation_error",
"message": "リリース日 is not a property that exists.",
"request_id": "**"
}
めちゃくちゃ書いてありました。
あとはこれをurlopen()
のどこかから見つけることが出来ればOKです。
どうしたのか
結論から言えば無理でした。
urlopenやHTTPErrorの仕様を確かめにいったのですが、やはりurlopen()
がステータスコード4**や5**を受け取った場合に送出するHTTPError
にはbodyの内容は渡されていないようです。
というわけで諸々の条件を考えた結果、今回はsubprocess経由でcurlを呼び出すことにしました。
curl使う.py
from json import dumps, loads
from subprocess import run
endpoint = "https://api.notion.com/v1/pages/**"
headers = {
"Authorization": "Bearer **",
"Notion-Version": "2022-06-28",
"Content-Type": "application/json",
}
payload = {
"properties": {
"リリース日": {
"date": {
"start": "2023-12-01"
}
}
}
}
proc = run(
["curl", "-Ssi", endpoint, "-X", "PATCH"]
+ [opt for hd in [f"{k}: {v}" for (k, v) in headers.items()] for opt in ["-H", hd]]
+ ["-d", dumps(payload)],
capture_output=True,
encoding="utf-8",
)
h, b = proc.stdout.split("\n\n")
res = {
headers: h.split("\n")
body: loads(b)
}
...
当然実行環境にcurlがインストールされている必要があるぶん汎用性は落ちますが選択肢のひとつに持っておいて損はなさそうだなと思います。