qiita post
localにあるorgファイルをapiでqiitaにあげられないかというのがお題.
ハマったけどダメ.Bad requestしか返ってこなくって(07/22 10:00ごろ).その直後にcurlからのを見つけて,全てうまくいく.
完動,最小版は,
require "net/https"
require "json"
lines = File.readlines("README.md")
qiita = 'https://qiita.com/'
path = 'api/v2/items'
uri = URI.parse(qiita+path)
http_req = Net::HTTP.new(uri.host, uri.port)
http_req.use_ssl = uri.scheme === "https"
params = {
"body": "# テスト", #lines.join,
"private": true,
"title": "テスト",
"tags":[
{
"name": "hoge",
"versions": []
}
]
}
ACCESS_TOKEN = ENV["QIITA_WRITE_TOKEN"]
headers = {"Authorization" => "Bearer #{ACCESS_TOKEN}",
"Content-Type" => "application/json"}
res = http_req.post(uri.path, params.to_json, headers)
p res.message
p res.body
p res.response
結論は,「やっぱりtag」でした.やれやれ.動くまでにweb APIとのconnectionの流儀でだいぶ悩んだんで,それについても書いておきます.
コツは,
- 短いの: 必要最小限の「やりとり」を用意する.
- interactive: 通信の様子を見るために,ターミナルで直接やりとりする.curlを使え.
- 確認: どんな通信が来てるかを直接見る, webrickを立ち上げてlocalと通信する
- net/https, json: libのおきてにお任せがいいよね.
- faraday: 慣れるとfaradayなんかは綺麗にかけて,出力もリッチでいいけど,'Bad request'の本質を解決してくれるわけではない.net/httpでいい.
です.順を追って解説します.
curlから
唯一成功したのが,これ.
curl -v -X POST "https://qiita.com/api/v2/items" \
-H "Authorization: Bearer ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"body\": \"# テスト\",\"private\": true,\"title\": \"テスト\",\"tags\":[{\"name\": \"hoge\",\"versions\": []}]}"
めちゃくちゃ簡単なんやけど...できた.https://qiita.com/daddygongon/private/01b62c9f716573b18e61
この-dの後の後ろのごちゃごちゃに気が付いたんがえらい.ここが本質でした.
tag
Qiita API v2を利用してcurlで投稿してみた に記述があるけど,tagが不可欠,しかもややこい.
ACCESS_TOKEN
group/user, read/writeそれぞれで違うtokenが発行される.この影響があったかも.
localでの検証
ruby webrick.rbで立ち上げて,http://localhost:8000/ で確認.qiitaに投稿する代わりにlocalに投げた場合,
require "net/https"
require "json"
url = 'http://localhost:8000/'
path = 'api/v2/items'
uri = URI.parse(url+path)
http_req = Net::HTTP.new(uri.host, uri.port)
params = {
"body": "# test",
"private": true,
"title": "test",
"tags":[
"hoge"
]
}
ACCESS_TOKEN = 'ACCESS_TOKEN'
post_req = Net::HTTP::Post.new(uri.path)
post_req["Authorization"] = "Bearer #{ACCESS_TOKEN}"
post_req["Content-Type"] = "application/json"
post_req.body = params.to_json
res = http_req.request(post_req)
p res.message
p res.body
p res.response
ruby post_local.rb 380ms
"OK "
"<html><body>\nPOST /api/v2/items HTTP/1.1\r\n<br>{"body":"# test","private":true,"title":"test","tags":["hoge"]}\n</body></html>\n"
#<Net::HTTPOK 200 OK readbody=true>
と返って来ます.webrick側での表示は
> ruby webrick.rb
[2020-07-23 07:32:04] INFO WEBrick 1.4.2
[2020-07-23 07:32:04] INFO ruby 2.6.5 (2019-10-01) [x86_64-darwin17]
[2020-07-23 07:32:04] INFO WEBrick::HTTPServer#start: pid=8161 port=8000
========== 2020-07-23 07:32:08 +0900 ==========
POST /api/v2/items HTTP/1.1
Accept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Accept: */*
User-Agent: Ruby
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json
Connection: close
Host: localhost:8000
Content-Length: 63
{"body":"# test","private":true,"title":"test","tags":["hoge"]}
です.
net/https, json
net/http, https
net/httpとかの流儀は,
です.net/httpsとかはrubyではrequireを変えるでけではなくて,
http.use_ssl = true
だけは入れいます.でもあとは,普通に.
json
jsonが返って来てなくて,これがなんなのかわからなかった.多分,nginxがweb serverでそのdefaultでerrorが返ってくるときはhtmlみたい.さらに型判定が通ってrequestが上にあげられるとjsonが返ってくる.
うまくいくようになると,
p res.message
JSON.parse(res.body).each do |key, cont|
if key == 'rendered_body' or key == 'body'
puts "%20s brabrabra..." % key
next
end
print "%20s %s\n" % [key, cont]
end
p JSON.parse(res.body)["url"]
などとして,
"Created"
rendered_body brabrabra...
body brabrabra...
coediting false
comments_count 0
created_at 2020-07-24T12:43:03+09:00
group
id 277d42b24f195ce09471
likes_count 0
private true
reactions_count 0
tags [{"name"=>"hoge", "versions"=>[]}]
title テスト
updated_at 2020-07-24T12:43:03+09:00
url https://qiita.com/daddygongon/private/277d42b24f195ce09471
user {"description"=>"Ruby, VASP, Maple, boundary, nucleation, Al, Ti, Mg, SiC, Si", "facebook_id"=>"", "followees_count"=>7, "followers_count"=>7, "github_login_name"=>"daddygongon", "id"=>"daddygongon", "items_count"=>30, "linkedin_id"=>"", "location"=>"Japan", "name"=>"", "organization"=>"Kwansei Gakuin University", "permanent_id"=>151211, "profile_image_url"=>"https://s3-ap-northeast-1.amazonaws.com/qiita-image-store/0/151211/1b7c18530785a67592309af94197e19e74c6aba2/x_large.png?1584337585", "team_only"=>false, "twitter_screen_name"=>nil, "website_url"=>""}
page_views_count
"https://qiita.com/daddygongon/private/277d42b24f195ce09471"
とできます.
Faraday
faradayは綺麗で,出力もリッチだけど本質ではない.httpsに投げるとredirectが返ってくるだけ.でbad requestが治ったわけではない.
#+name: post_faraday.rb
require "net/http"
require "json"
lines = File.readlines("README.md")
qiita = 'https://qiita.com/'
path = 'api/v2/items'
uri = URI.parse(qiita+path)
require 'faraday'
require 'json'
param = {
body: '#sample', coediting: false, gist: false, private: true,
tags: [], title: 'sample', tweet: false
}
url = 'http://qiita.com'
conn = Faraday.new(url: url) do |builder|
builder.request :url_encoded
builder.response :logger
builder.adapter :net_http #Faraday.default_adapter
end
ACCESS_TOKEN = ENV["QIITA_ACCESS_TOKEN"]
response = conn.post do |request|
request.url '/api/v2/items'
request.headers = {
'Authorization' => "Bearer #{ACCESS_TOKEN}",
'Content-Type' => 'application/json'
}
request.body = JSON.generate(param)
end
p response
puts JSON.pretty_generate(JSON.parse(response.body))
#json = JSON.parser.new(response.body)
auto化
orgから一挙に行きたいですよね.keyは
で,
emacs README.org --batch -l ~/.emacs.d/site_lisp/ox-qmd -f org-qmd-export-to-markdown --kill
と-lでelファイルを指定して,その中にある関数を機能させる.
それとともに,tagやtitleをorgから取ってくる.以下は,全てを自動化したversion.
def get_title_tags(src)
conts = File.read(src)
title = conts.match(/\#\+(TITLE|title|Title): (.+)/)[2] || "テスト"
m = []
tags = if m = conts.match(/\#\+(TAG|tag|Tag|tags|TAGS|Tags): (.+)/)
m[2].split(',').inject([]) do |l, c|
l << {name: c.strip, versions: []}
end
else
[{ name: "hoge", versions: [] }]
end
p tags
return title,tags
end
src = ARGV[0] || 'README.org'
title, tags = get_title_tags(src)
p title
p tags
system "emacs #{src} --batch -l ~/.emacs.d/site_lisp/ox-qmd -f org-qmd-export-to-markdown --kill"
params = {
"body": lines.join, #"# テスト",
"private": true,
"title": title,
"tags": tags
}
あと,teamsに投げる.これはurlとaccess_token
qiita = 'https://nishitani.qiita.com/'
ACCESS_TOKEN = ENV['QIITA_TEAM_WRITE_TOKEN']
を少しいじるだけでできる.
- source ~/git_hub/ruby_docs/qiita/qiita_post/README.org