tohokuaikiのチラシの裏

技術的ネタとか。

ConfluenceのREST APIを作ってみる - POST編

GETメソッドの場合は引数を@QueryParamで受け取ればなんてことなかったのだけど、POSTの場合はちょっと違った。

なお、通信はXMLじゃなくてすべてJSONを使った場合。

AtlassianのDocumentでいうとこの辺りが参考になる。使わなかったけど。

Developing a REST Service Plugin - Atlassian Developers

あと、そもそもこの技術はJavaのjerseyを使っているので、とりあえずREST with Java (JAX-RS) using Jersey - Tutorialあたりを参考にしたりしました。

そもそもどういうRequestを作るべきなのか?

Labelを付けるREST APIを見ながら考察した。するつRequest Headerはこんな感じ。

Accept:application/json, text/javascript, */*; q=0.01
Accept-Encoding:gzip, deflate
Accept-Language:ja,en-US;q=0.8,en;q=0.6
Connection:keep-alive
Content-Length:70
Content-Type:application/json
Cookie:JSESSIONID=xxxxxxxxxxxxxxxxxxxx

普通にjQueryで$.ajaxするとContent-Typeが「application/x-www-form-urlencoded」になるので、そこを変更しなければならない。

また、POSTデータを見てみると、Request Payloadになっていて、

[{"name":"ccc","id":1481094538830},{"name":"dddd","id":1481094538831}]

まんま、JSONの形。

ということで、これをjQueryで使うには

var data = {
    foo: 123,
    bar: 456
};
$.ajax({
    url: "{ConfluenceのBASE URL}/rest/restpath/1.0/restclasspath/{pageId}/restmethod.json",
    type: "post",
    contentType:'application/json',
    data: JSON.stringify(data)
}).success(function (data, status, xhr) {

という感じでContentTypeを指定し、送信データをJSON.stringifyしてやる。

受けるConfluence側

atlassian-plugin.xml

にてRESTの指定をする。決める重要な情報は、restのpathのみ。
先ほどのAjaxで受ける場合はrestpathの部分を以下のように指定する。

<rest key="foo-bar-rest" path="/restpath" version="1.0">
    <description>RESTの説明</description>
</rest>

JavaクラスとRESTのクラスパス

クラスの場所はどこでもいいので、@Pathアノテーションに持つクラスを作る。
これは結構不思議というか、じゃあ先ほどatlassian-plugin.xmlのpathで指定したものとの関連性は無いのか?という感じである。

とりあえず、先ほどのAjaxの場合は

@Path("/restclasspath")
public class Foobar {

という感じでClassを作る。

JavaメソッドとRESTのメソッド

先ほどのAjaxのパスの場合だと

    @POST
    @Path("{pageId}/restmethod")
    @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
    public Response api(@PathParam("pageId") FoobarModel foobarModel) {

というメソッドを作る。メソッド名はapiとしているけどこれは特に意味は無い。わかりやすいのを付ければ何でもいいのではないかと思う。

メソッドの引数としては、メソッドの@Pathアノテーション内で定義したものを、@PathParamでアノテーションされた引数と、POSTされたJSONデータを受け取るModelクラスを指定する。

POST値を受け取るModelクラス
var data = {
    foo: 123,
    bar: 456
};

を受け取りたい場合はこんな感じ。

@XmlRootElement(name = "foobar")
@XmlAccessorType(XmlAccessType.FIELD)
public class foobarModel {

    @JsonIgnore
    @XmlElement(name = "foo")
    private String foo;

    @XmlElement(name = "bar")
    private String barString;

    public foobarModel() {
    }
}

@JsonIgnoreが付いていると、その要素は無くても大丈夫になる。各プロパティに逐一全部つけるのが面倒な場合はクラスにまとめて

@JsonIgnoreProperties(ignoreUnknown=true)
public class foobarModel {
@JsonIgnoreProperties({"foo","bar"})
public class foobarModel {

としてやる。

もし、JSONが下記のようであったら

var data = {
    foo: 123,
    bar: 456
};

プロパティにJsonアノテーションを付ける。

    @JsonProperty("barStr")
    @XmlElement(name = "bar")
    private String barString;

@JsonPropertyがなくても、@XmlElementのnameで指定していればそれを援用してくれる。

このようなオプションを通してもマッピングできない場合は、common.error.jersey.ThrowableExceptionMapper が投げられる。

レスポンスの設定

レスポンスには、OK/NGのステータスとOKの場合はJSONを付けられるので

return Response.ok(new foobarModel).build();

とすると、foobarModelオブジェクトをJSONにしたものを返却できる。

Confluenceのマクロモジュールを作成するときのi18nについて

マクロパラメータとかの名称をi18nにしたいなーって思ったときに今までやったことが無いことに気が付いた。

元資料

Including Information in your Macro for the Macro Browser - Atlassian Developers

の最後の方によると、自動的にi18nの定義を見てくれるらしい

i18nの定義 意味
{pluginKey}.{macroName}.label マクロ名
{pluginKey}.{macroName}.desc マクロの説明文
{pluginKey}.{macroName}.param.{paramName}.label パラメータの名前
{pluginKey}.{macroName}.param.{paramName}.desc パラメータの説明文
{pluginKey}.{macroName}.body.label Macro body label (defaults to 'Body Text' if not provided)
{pluginKey}.{macroName}.body.desc Macro body description

今まで買ったブロードバンドルータのメモ

2001年にYahoo!BBを契約してから、現在まで買ったルータ。基本的に全部NEC Atermブロードバンドルータ。1~2万円くらいで買える家庭用の安いやつ。

無線ルータの寿命は一般的に3年くらいと言われてるらしい。

2002年夏頃 Aterm WBR75H(寿命:9年。結局壊れず)

121ware.com > Aterm/モデム > WARPSTARシリーズ

Yahoo!BBを契約し、引っ越しして家が広くなったのでノートパソコンとMacG4Cubeで使えるように買った。

ものすごい安定してて、約10年間の使用で結局壊れなかった。

ただ、無線がIEEE802.11bにしか対応してなかったので後述のAtermWR8700Nが開いた時に変更した(ので捨てずにまだとってある)。

これ、セットになってる無線LANカードと同じものがルータの中に入ってて

「使い終わったら無線LANカードとしてノートパソコンに付ければお得!」とか思ってたけど結局10年近く使った上にその頃にはノートパソコンには無線チップが標準でつくようになり、そもそもカードスロットというもの自体が消えていた…。

2006年7月 Aterm WR6600H(寿命:親機5年、子機10年)

Aterm Museum:生産終了製品 > AtermWR6600H

仕事の事務所を開いたので、そこで使うように買った。デスクトップPCに無線カードが入ってなかったのでEthernetで接続できる子機が付いているやつ。

こちらは親機が2011年6月29日に突然死。慌ててAmazonで新しいルータを購入した。

2011年6月 Aterm WR8700N(稼働中)

AtermWR8700N(HPモデル) | 製品情報 | AtermStation

上記の理由で親機だけ購入。元からあった子機とはAterm同士なので「らくらく無線スタート」機能で簡単にペアリング完了。この機能はかなり便利。

ただ、突然死してしまったのでLANの設定が飛んでしまい、これは面倒であった。

2016年4月 Aterm WG1200HP(稼働中)

AtermWG1200HP | 製品一覧 | AtermStation

事務所を閉じて自宅で仕事をするようになって3年、自宅のルータを10年選手のWBR75HからWR8700Nに変更し、デスクトップPCにはWR6600HについてたEthernet接続無線子機を使っていたが、この子機が死亡。たまに接続できなくなることが増えて…という衰弱死であった。

ヨドバシカメラに行って、「子機モードのあるルータ」を聞いてみたところこの機種を薦められた。こういう時にパッと意味を理解してくれて的確な商品を進めてくれるのはとても嬉しい。

親機とはAterm同士なので「らくらく無線スタート」機能で簡単にペアリング完了。

さて、そろそろ親機のWR8700Nの挙動があやしくなってきたので、WG1200HPをもう一台買おうかなと思っている所存。

まとめ

一番短命だったものでも5年。なんというか、Nが低いけどAterm強い。

しかし、10年以上Aterm使ってるけどルータの設定画面が全く変わらないのは使い勝手が変わらずうれしい半分もう少し頑張ってくれよと思うのも半分。

BINDにてワイルドカードでSPFレコードを登録する場合

BINDでドメイン配下のホストを一括でワイルドカード指定している場合

こんな場合。

example.com.zone

mail            IN      A       192.168.11.4
*       IN      CNAME   mail

SPFレコードに

mail            IN      A       192.168.11.4
*       IN      CNAME   mail
*     IN      TXT     "v=spf1 ip4:192.168.11.4 ~all"

って書いても上手くいかない。

mail            IN      A       192.168.11.4
*       IN      CNAME   mail
mail     IN      TXT     "v=spf1 ip4:192.168.11.4 ~all"

ってしてやる。

確認

$ sudo rndc reload
$ nslookup
> server localhost
Default server: localhost
Address: 127.0.0.1#53
> set type=txt
> aaa.example.com
Server:         localhost
Address:        127.0.0.1#53

aaa.example.com       canonical name = mail.example.com.
mail.example.com      text = "v=spf1 ip4:192.168.11.4 ~all"

ckanにプラグインをインストールしてみる

続き。ckanのバージョンは2.7

プラグインのインストール

ルート権限でdebパッケージにてインストールしたので、プラグインのインストールにもルート権限が必要。

ということで、ルートになるかsudoで作業。

今回は、https://github.com/ckan/ckanext-showcase をインストールしてみる。

# . /usr/lib/ckan/default/bin/activate
(default)root@vagrant-ubuntu-trusty-64:~# cd /usr/lib/ckan/default/src/ckan/
(default)root@vagrant-ubuntu-trusty-64:/usr/lib/ckan/default/src/ckan# pip install ckanext-showcase

その後、設定ファイルに記述

# grep plugin /etc/ckan/default/production.ini
ckan.plugins = stats text_view image_view recline_view showcase

そして、apacheのリスタート

# service apache2 restart

すると、こんな感じで表示されている。 f:id:tohokuaiki:20161102175127p:plain

Excelで出力するプラグインをインストールしようとして

何かエラー出た。

# pip install ckanext-dgvat_xls
Collecting ckanext-dgvat_xls
/usr/lib/ckan/default/local/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/util/ssl_.py:318: SNIMissingWarning: An HTTPS request has been made, but the SNI (Subject Name Indication) extension to TLS is not available on this platform. This may cause the server to present an incorrect TLS certificate, which can cause validation failures. You can upgrade to a newer version of Python to solve this. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#snimissingwarning.
  SNIMissingWarning
/usr/lib/ckan/default/local/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/util/ssl_.py:122: InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. You can upgrade to a newer version of Python to solve this. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning.
  InsecurePlatformWarning
  Could not find a version that satisfies the requirement ckanext-dgvat_xls (from versions: )
No matching distribution found for ckanext-dgvat_xls

エラー文で出てる、https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings から https://urllib3.readthedocs.io/en/latest/user-guide.html#ssl-py2 に移動して、読んでみると

# pip install urllib3[secure]

すればいいのかな?

これをそのまま実行するとエラーが出たので、devツールを入れる。

# apt-get install -y python-dev

なんかうまくいったっぽい。

けど、直らない。・・・