読者です 読者をやめる 読者になる 読者になる

Movable Type 6のData APIで再構築する

Movable Type API Groovy

背景

  • 社内のMovable Typeで複数のブログを束ねて表示するまとめサイトのウェブサイトを作りました。
    • 諸事情あって上位のウェブサイトではなくブログの中にウェブページを作ってる。
    • ウェブページのために、新たに専用のCSSやウィジットを作成している。
  • 事情はよくわからないが、新しく作ったウェブページだけ毎日早朝に壊れる。
    • 壊れるというのは正確ではなく、ブログの方の部品やCSSが適用されておかしくなる感じ。
    • ただし、mt.cgiから手動で再構築すれば元に戻る。
    • どうも全ブログに毎朝再構築が掛かるという未確認情報もあり。

どうするか

確認手順

  • 再構築(Publish: entries)のEndpointはGET https://your-host/your-mt-api.cgi/v1/publish/entriesなので以下のようなリクエストを投げる。
    • {entry_id}は、ブログ記事もウェブページも同じ体型のIDで、mt.cgiの表示オプションから表示項目のIDをチェックすると確認することが出来る。
      • 今回は1記事だけ対象だけどカンマ区切りで複数指定することも可能
    • 再構築はすぐに終わらないので、Response HeaderのX-MT-Next-Phase-URLにセットされるURLにアクセスすると最新ステータスを確認することが出来る。
GET https://your-host/your-mt-api.cgi/v1/publish/entries?ids={entry_id}
  • ただし再構築をするためには認証が必要。
    • cronで実行すること考えているので、Loginページに飛んだりしても困る。よってauthenticationを利用する。EndpointはPOST https://your-host/your-mt-api.cgi/v1/authenticationとなる。
      • Authentication: authenticationメソッドはRequest Bodyに認証情報をセットしてPOSTする。
      • ResponseでaccessTokenが返ってくるので、publishするときにヘッダにセットしてあげる。
  • responseのstatusがCompleteになるまでX-MT-Next-Phase-URLにセットされているURLにアクセスしないと再構築が完了しない。
  • とりあえずAdvanced REST clientを使って出来たので、この手順をコード化する。

ソース

  • JavaScriptのライブラリがあるのだがcronで実行するつもりなので、Groovyで書いてみた。
    • いやただGroovyで書きたかったんですけどね。。。
  • mt_rebuild.groovy

簡単な説明

  • Apache HttpClient使ってます。
@Grab('org.apache.httpcomponents:httpclient:4.3.3')

import org.apache.http.*
import org.apache.http.client.*
import org.apache.http.client.params.*
import org.apache.http.client.methods.*
import org.apache.http.client.entity.*
import org.apache.http.impl.client.*
import org.apache.http.message.*
  • HttpClientの新しいバージョンではRequest Headerの作り方が変わってた。
    • .setDefaultHeaders()というのを使うらしい。
    def uri = "${api_host}/${api_base}/authentication"

    HttpPost post = new HttpPost(uri)

    List<Header> headers = new ArrayList<Header>()
    headers.add(new BasicHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"))
    HttpClient httpclient = HttpClientBuilder.create().setDefaultHeaders(headers).build()
  • POSTのRequest BodyはArrayListにaddしていく。
    List<NameValuePair> params = new ArrayList<NameValuePair>()
    params.add(new BasicNameValuePair("username", "foo_uid"))
    params.add(new BasicNameValuePair("password", "foo_pass"))
    params.add(new BasicNameValuePair("remember", "0"))
    params.add(new BasicNameValuePair("clientId", "mt_rebuild"))
    post.setEntity(new UrlEncodedFormEntity(params))
  • ResponseをJsonSlurper()に食わせて、accessTokenを取得。
        entity = new JsonSlurper().parseText(response.getEntity().getContent().text)
        return entity.get("accessToken")
  • publishではX-MT-Authorizationに取得したaccessTokenをセットして、GET。(GETなんで簡単)
headers.add(new BasicHeader("X-MT-Authorization", "MTAuth accessToken=${accessToken}"))
  • responseのstatusがCompleteになっていない場合は、X-MT-Next-Phase-URLにセットされているURLにアクセスする。
    response = httpclient.execute(get)
    if (response.statusLine.getStatusCode() == 200) {
        entity = new JsonSlurper().parseText(response.getEntity().getContent().text)
        response.getHeaders('X-MT-Next-Phase-URL').each {
            if (entity.get("status") != "Complete") {
                rebuild_next(accessToken, "${api_host}/${api_base}${it.value}")
            }
        }
    }
  • statusがCompleteになるまでX-MT-Next-Phase-URLを呼び出す。
def rebuild_next(accessToken, uri) {
    HttpGet get = new HttpGet(uri)

    List<Header> headers = new ArrayList<Header>()
    headers.add(new BasicHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"))
    headers.add(new BasicHeader("X-MT-Authorization", "MTAuth accessToken=${accessToken}"))
    HttpClient httpclient = HttpClientBuilder.create().setDefaultHeaders(headers).build()

    response = httpclient.execute(get)
    if (response.statusLine.getStatusCode() == 200) {
        entity = new JsonSlurper().parseText(response.getEntity().getContent().text)
        response.getHeaders('X-MT-Next-Phase-URL').each {
            if (entity.get("status") != "Complete") {
                rebuild_next(accessToken, "${api_host}/${api_base}/${it.value}")
            }
        }
    }
}

これで再構築できた。
自由にいじれるサーバばかりではないのでAPIがあるといいですね!