ShinobiLayer: SoftLayer API 次の一歩: methodの実行方法と返り値の制御(3)

#はじめに

この記事は、以下の記事の続きです。
http://qiita.com/testnin2/items/016a2d75359b8b9f282b

#プログラミング言語によるフィルタリング
全部返ってきても大した量にならない場合は、いったん全件データを取得した後に、言語(Pythonなど)の機能でフィルタリングを行った方がが柔軟に対応できると思います。多分これが一番楽です。

例：studentで始まるユーザーを取得

```py:filter1.py
import SoftLayer
client = SoftLayer.create_client_from_env()
users = client['Account'].getUsers()
for user in users:
    if user['username'].startswith('student'):
        print(user['username'])
```
```
結果
[root@sing ~]# python filter1.py
student-00
student-01
student-02
student-03
student-04
student-05
student-06
student-07
student-08
student-09
student-10
（以下略）
```

#SoftLayer APIによるフィルタリング（文字列など）

とはいっても、SoftLayer API側でフィルタリングせざるを得ないケースがないわけではありません。たとえば、膨大なticket履歴を全件取得した後にフィルタリングするなんてことは、、、ちょっとありえないでしょう。ただし、SoftLayer APIによるフィルタリングは、デバッグが難しい、ドキュメントの説明があまりない、など色々問題を抱えています。大量データを扱う場合など、仕方がない場面に限って利用するのが良いと思われます。

SoftLayer APIでフィルタリングを実施する場合は、methodの引数に**filter**というキーワードを指定します。こちらもlimitの時と同様に、methodの返り値が配列(array)である必要があります。

以下は、usernameのうち"STUDENT_01"という文字列が、大文字・小文字関係なく含まれるユーザーを取得しています。

```py:filter2.py
import SoftLayer
client = SoftLayer.create_client_from_env()
_filter={
    'users': {                                              #(1)
        'username': {                                       #(2)
            'operation': '_=STUDENT-01'                     #(3)
        }
    }
}
users = client['Account'].getUsers(filter=_filter)

for user in users:
    print(user['username'])
```
```
結果
# python filter2.py
student-01
```

上記サンプルコードを例にすると、filterは以下のルールに従っていそうです。
(1) 配列（array）を記述する。この際、filter対象のmethodからgetXXXXのgetを取り除き、最初を小文字にする。例えば

* getUsers -> ~~getU~~users
* getNetworkVlans-> ~~getN~~networkVlans
* getNextInvoiceTopLevelBillingItems -> ~~getN~~nextInvoiceTopLevelBillingItems

(2) 対象データオブジェクトにてフィルタリング対象のpropertyを指定する。この例だと、`getUser()`で取得したSoftLayer_User_Customerデータオブジェクトのlocal propertyであるusernameを指定しています。

(3)演算子としては、以下の種類のものが存在するようです。

| 演算子 | 意味 |
|:----|------------|
| （何も無し）  | 完全一致 | 
| *=  | Contains (ignoring case) | 
| ^=  | Begins with (ignoring case) | 
| $=  |   Ends with (ignoring_case) | 
| _=  |  Matches (ignoring case) | 
| !=  |   Is not Equal To (case sensitive) | 
| <=  |  Less than or Equal To (case sensitive) | 
| >=  |   Greater than or Equal To (case sensitive) | 
| <   |  Less Than (case sensitive) | 
| >   |   Greater Than (case sensitive) | 
| ~   | Contains (case sensitive) | 
| !~  |  Does not Contain (case sensitive) | 

（参考）https://github.com/softlayer/softlayer-ruby/blob/master/lib/softlayer/ObjectFilter.rb

*なお、filterに対して、複数の条件を付ける方法は幾つか試したのですが分かりませんでした。もし分かったらフィードバック下さい！*


#SoftLayer APIによるフィルタリング(datetime型）

SoftLayerのdatetime型を出力すると、timezoneまで含まれていることが分かります。例：`2015-01-24T22:54:49-06:00` 
この出力結果が、どのtimezoneで表示されるかは、Customer Portalにて
Account -> Users -> "APIを実行するユーザー"を選択し、下記画面から変更することができます。こちらの記事も参考にしてみて下さい。http://sldn.softlayer.com/article/Date-Handling-SoftLayer-API
![timezone.jpg](https://qiita-image-store.s3.amazonaws.com/0/62127/981e55cc-38c3-c3cf-1c24-f815631c9764.jpeg)

datetimeを利用した範囲検索を使ったfilterの例は以下になります。

```py:filter3.py
_filter={
    'loginAttempts': {
        'createDate': {
            'operation': 'betweenDate',
            'options': [
                {'name': 'startDate', 'value': ['01/24/2015 00:00:00']},
                {'name': 'endDate',   'value': ['01/24/2015 23:59:59']}
            ]
        }
    }
}
```

**ただし、filterではtimezoneが指定できず、なおかつこの範囲評価がGMT-6:00(Dallasでのローカル時間）を前提に評価されているようです。**このため、createDateが`2015-01-25T13:54:49+09:00のものも1月24日のイベントとして上記のサンプル例の範囲に引っかかってきてしまうというのが、執筆時点での動きですので気をつけましょう。

#終わりに
SoftLayer APIの仕様はほとんど見つからず、既存のコードを読んだり試行錯誤しながらの結果を記事にしました。「データ型オブジェクト」と「methodの実行方法と返り値の制御」の２つで、だいぶSoftLayer APIに関しては網羅できたのではないかと思います。細かい内容は別途機会があれば記事にしたいとは思いますが、まずはこれを取っ掛かりにどんどんコードを読んだり試していってもらえれば幸いです。