雑食日誌

PythonとJS。ときどきチーム開発

GraphQLを試してみた

こんにちは。ぬまたです。
約半年ぶりの投稿です。空けすぎました。
最近AppSyncを触る機会があったので、基礎となるGraphQLを学びたいと思います。

目次

GraphQLとは

GraphQLはフロントエンドがサーバのデータをいい感じに取得するためのクエリ言語です(怒られそう)。 ざっと調べる限りは以下のような特徴がありました。

  • 読み取りと書き込みが分離している
  • エンドポイントが一つ
  • データ群の処理結果などをパラメータに追加できる
  • Schemaや型を定義する
  • 変数やコメントアウトを使うことができる
  • キャッシュが比較的容易

実際に使ってみないとわからないので、GitHub APIで試してみます。 GitHubAPIのバージョン4からGraphQLを導入しています。

参考: - GitHub API v4

検証環境

GitHubのデベロッパーツールを使用します。

GraphQLはエンドポイントをリソースごとに分けずにパラメータで指定します。 GitHubではリクエストを https://api.github.com/graphql に向け、パラメータにクエリを入れています。

GitHub API を試してみる

ログインしているユーザーのメールアドレス取得

単一のリソースfieldsから指定したキーの値を取得できます。 現在ログインしているユーザのメールアドレスを取得してみます。

GitHubviewer という fieldsにログインユーザー情報が入っています。 クエリ内にパラメータ(email)を指定して取得することができます。

クエリ

{
 viewer {
   email
 }
}

結果

{
 "data": {
   "viewer": {
     "email": "nununu.mono@gmail.com"
   }
 }
}

Dockerの関連トピックを取得

fields は引数を取ることができます。 GitHubのDockerトピックに関連するトピックを取得してみます。

クエリ

query {
 topic(name: "docker") {
   relatedTopics {
     name
   }
 }
}

結果

{
 "data": {
   "topic": {
     "relatedTopics": [
       {
         "name": "docker-container"
       },
       {
         "name": "jhipster"
       },
       {
         "name": "docker-image"
       },
       {
         "name": "database"
       },
       {
         "name": "ruby"
       },
       {
         "name": "composer"
       },
       {
         "name": "nginx"
       },
       {
         "name": "yii"
       }
     ]
   }
 }
}

relatedTopics はオブジェクトのリストになっています。 ネストしたオブジェクトのキー(今回は name )を選択できることが異なる点ですね。

リポジトリ内のPR一覧を取得

nodes を使ってリスト値と欲しいパラメータを取得することができます。 今回はよく使うDjangoRESTFrameworkのPR一覧とそれぞれのコミット数を調べてみます。

クエリ

query { 
 repository(owner: "encode" name: "django-rest-framework") { 
   pullRequests(last: 5 states: OPEN) {
     nodes {
       number
       title
       commits {
         totalCount
       }
     }
   }
 }
}

結果

{
 "data": {
   "repository": {
     "pullRequests": {
       "nodes": [
         {
           "number": 6279,
           "title": "Let SearchFilter subclasses dynamically set search fields",
           "commits": {
             "totalCount": 4
           }
         },
         {
           "number": 6282,
           "title": "Ensure serializer and field validators support both lists and tuples",
           "commits": {
             "totalCount": 2
           }
         },
         {
           "number": 6284,
           "title": "Don't force implement get_queryset",
           "commits": {
             "totalCount": 1
           }
         },
         {
           "number": 6286,
           "title": "permissions must return a boolean to allow &/| operator comparison",
           "commits": {
             "totalCount": 3
           }
         },
         {
           "number": 6288,
           "title": "Fix DjangoObjectPermissionsFilter deprecation note",
           "commits": {
             "totalCount": 2
           }
         }
       ]
     }
   }
 }
}

pullRequests の引数にPRが OPEN であることと、最新の5つを取得するよう指定しています。 DjangoRESTFrameworkのGitHubをみると正しそうです。

nodes はPRのリスト値にあたり、その中でPRのNoやコミット数を指定しています。 commitstotalCount パラメータが入っているので、データを全て取得せずに総数を把握することができますね。 自分でSchemaを定義する場合はクライアントが欲しい値を自由に追加することができます。

特定条件のリポジトリ数の取得

GraphQLのPaginationを使ってクエリ結果の総数を取得できます。 Pythonで書かれているスター数1000以上のリポジトリが何個あるか調べてみます。

クエリ

query {
 search(query: "language: Python stars:>1000", type: REPOSITORY) {
   repositoryCount
}

結果

{
 "data": {
   "search": {
     "repositoryCount": 28
   }
 }
}

searchGitHubで定義されているコネクションです。クエリに加えて読み取り開始位置 after などを定義できるようになっています。

今回は querytype に検索したい条件をして、値に repositoryCount を取ることでリポジトリ数を取得しています。 search の引数を変えて、色々な検索をかけられるのはよいですね。

まとめ

今回は読み取りの Query を中心にGraphQLを試してみました。 GraphQLのメリットはクライアントの負担が減ることなのかなという所感です。

Schemaの定義はクライアントの要求を取り入れつつ、データベース内のモデルの知識が求められそうですね。 その辺りも今後調べたいと思います。

BacklogAPIライブラリを作ってみた

Pythonエンジニア見習いのぬまやんです。

今回はBacklogAPIのクライアントライブラリを作ってみました。
Backlogの課題やプロジェクトをオブジェクトとして扱えるようにしています。

目次

動機

今まで作りかけだった自作のライブラリにエキスパートPythonプログラミングの内容を実践してみたいと思ったことがきっかけです。
エキスパートPythonプログラミングは言語の基本文法だけでなく、パッケージの作り方やテスト、プロジェクトの管理 についてPythonのベストプラクティスをまとめていただいている本になります。

エキスパートPythonプログラミング改訂2版

エキスパートPythonプログラミング改訂2版

作ったもの

作成したライブラリはGitHubにて管理していて、PyPIに公開しています。

pypi.org

使い方

実現したかったことはBacklogのプロジェクトや課題をオブジェクトとして扱うことです。
そのため以下のような操作感にしています。

from backlogapi import BacklogClient

client = BacklogClient(api_key='apiKey', space_name='spaceName'
# Space内の全プロジェクトを取得する
projects = client.project.all()
project1 = projects[0]

# プロジェクト内の課題を取得する
issues = project1.get_issues()

# 取得した課題を削除する
for issue in issues:
    issue.delete()

エキスパートPythonプログラミングを参考にしたところ

PyPIのへのアップロード

今回のライブラリの目標はPyPIにアップロードして、公開することでした。 公開するにはsetup.py, MANIFEST.in, requirements.txtなど準備するものが多いです。
本書にはこの設定方法について詳細や一般的な手段が記されています。

本書内で何箇所か説明されている新しいPyPIはすでに公開されています。 pypi.org
さらに Project DescriptionについてMarkdownの対応も完了しています。 対応方法はsetup.pyのsetup関数に以下を追加するだけです。

long_description_content_type='text/markdown',

コード管理手法

コードの管理として、継続的デプロイが紹介されています。
私はGitHubにてホスティングしていたため、TravisCIを用いて自動テストを行いました。 TravisCIを用いた所感は以下になります。

  • 新規の仮想環境にてテストを行うことができる
  • GitHubとの連携が簡単
  • 設定方法のドキュメントが読みやすい

他にも紹介されているJenkinsやBuildbotについても試していきたいと思います。

テスト駆動開発

テストについては基礎から応用まで幅広く記されています。 具体的にはunittestを用いた基本的なテスト方法から拡張モジュールであるnose, pytestの使い方が説明されています。 今回はBacklogという外部APIを用いたライブラリなので、Mockを作成する方法やtoxを使ったマトリックステストも使ってみました。

本書から引用したツール・ライブラリは以下になります。

作ってみた感想

ベースとなる書籍があったためPyPIでの公開はスムーズにできました。
しかし、ライブラリ自体を使用する側の目線に立って作ることが難しかったです。
現状はまだまだ使いにくいものなので、改善していきたいと思います。 また、使用感を教えていただけると大変嬉しいです。

line-bot-sdk-pythonを使ってみた

はじめに

line-bot-sdk-pythonPythonを用いてLINE botを作りました。 ライブラリの操作で何箇所か詰まってしまったので、備忘録として記載します。

開発環境

ライブラリ

概要

line-bot-sdk-pythonはLINEのMessaging APIのクライアントライブラリであり、Bot開発を簡単に始めることがきます。 デコレータを用いて、イベントごとの処理を分けて記述したり、Signatureから署名を検証する操作を簡単にかけます。

事前準備

初めてLINE Botを作成するときは以下の公式ドキュメントからチャネルの作成まで完了する必要があります。 登録が終わったら、自分のBotの各種設定をコンソール画面から取得することができます。

URL設定

LINEのWebhookを用いるとBotへのメッセージや友達追加などのイベント時にHTTP POSTリクエストを発行できます。 コンソール画面のWebhook URLの項目にPOST先のURLを定義することで設定が完了します。

Djangoではルーティングを用いてURLを設定します。

urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('callback', views.callback)
]

認証

LINE Messaging APIを用いいる場合は以下の二つの定義が必要になります。

  • LINE_ACCESS_SECRET: 署名の検証で使用
  • CHANNEL_ACCESS_TOKEN: 各API通信を行うときに使用

コンソール画面のChannel基本設定から、Channel Secretとアクセストークン を発行します。これらがそれぞれLINE_ACCESS_SECRETとCHANNEL_ACCESS_TOKENに当たります。

Djangoのviews.py上では以下になります。

views.py

from linebot import LineBotApi, WebhookHandler

# 各クライアントライブラリのインスタンス作成
line_bot_api = LineBotApi(channel_access_token=CHANNEL_ACCESS_TOKEN)
handler = WebhookHandler(channel_secret=LINE_ACCESS_SECRET)

handlerの使い方

レスポンスの関数呼び出しとリクエストの署名検証

リクエストごとの関数を呼び出すためにはWebhookHandlerのhandleメソッドを用います。 handleメソッドは署名検証も同時に行います。 引数にはレスポンスのbodyと署名検証のためのsignatureを使います。

views.py

from django.http import HttpResponseForbidden, HttpResponse
from linebot.exceptions import InvalidSignatureError

def callback(request):
    # リクエストヘッダーから署名検証のための値を取得
    signature = request.META['HTTP_X_LINE_SIGNATURE']
    # リクエストボディを取得
    body = request.body.decode('utf-8')
    try:
        # 署名を検証し、問題なければhandleに定義されている関数を呼び出す
        handler.handle(body, signature)
    except InvalidSignatureError:
        # 署名検証で失敗したときは例外をあげる
        HttpResponseForbidden()
    # handleの処理を終えればOK
    return HttpResponse('OK', status=200)

イベントごとに関数を定義

handlerのaddメソッドを用いて、リクエストのイベントごとに実行する関数を記述できます。 例えば、友達追加されたときに「初めまして」というメッセージを送信する処理は以下のようになります。

views.py

# linebot.modelsから処理したいイベントをimport
from linebot.models import (
    FollowEvent, TextSendMessage
)

# addメソッドの引数にはイベントのモデルを入れる
# 関数名は自由
@handler.add(FollowEvent)
def handle_follow(event):
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text='初めまして')
    )

handler.addメソッドには引数にリクエストのイベントを指定します。 イベントはlinebot.modelsに定義されていて、メッセージやグループ参加などがあります。 関数の引数であるeventはLINE Messaging APIで定義されているリクエストボディになります。

リプライはLineBotApiのメソッドを用いています。 第一引数のevent.reply_tokenはイベントの応答に用いるトークンです。 第二引数にはlinebot.modelsに定義されている返信用のTextSendMessageオブジェクトを渡しています。

handler.addメソッドはメッセージイベントの場合にテキストや画像のようなメッセージの 内容でも処理を分けることができます。

from linebot.models import (
    MessageEvent,
    TextMessage ,ImageMessage, AudioMessage
)

# メッセージイベントの場合の処理
# かつテキストメッセージの場合
@handler.add(MessageEvent, message=TextMessage)
def handle_text_message(event):
    # メッセージでもテキストの場合はオウム返しする
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text=event.message.text)
    )


# 画像、音声メッセージの場合
@handler.add(MessageEvent, message=(ImageMessage, AudioMessage))
def handle_image_audio_message(event):
    content = line_bot_api.get_message_content(event.message.id)
    with open('file', 'w') as f:
        for c in content.iter_content():
            f.write(c)

まとめ

line-bot-sdk-pythonを使ってイベントごとにレスポンスを変える処理を作りました。 処理部分を関数で簡単に分けることができるためとてもシンプルに書くことができます。 この処理を使って複数のAPIを連携したbotを作ったので、後日まとめたいです。

コード例

views.py

from django.http import HttpResponseForbidden, HttpResponse

from linebot.exceptions import InvalidSignatureError
from linebot.models import (
    MessageEvent, TextMessage, FollowEvent, UnfollowEvent,
    TextSendMessage, ImageMessage, AudioMessage
)

from linebot import LineBotApi, WebhookHandler


# 各クライアントライブラリのインスタンス作成
line_bot_api = LineBotApi(channel_access_token=CHANNEL_ACCESS_TOKEN)
handler = WebhookHandler(channel_secret=LINE_ACCESS_SECRET)


def callback(request):
    # signatureの取得
    signature = request.META['HTTP_X_LINE_SIGNATURE']
    body = request.body.decode('utf-8')
    try:
        # 署名の検証を行い、成功した場合にhandleされたメソッドを呼び出す
        handler.handle(body, signature)
    except InvalidSignatureError:
        return HttpResponseForbidden()
    return HttpResponse('OK')



# フォローイベントの場合の処理
@handler.add(FollowEvent)
def handle_follow(event):
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text='初めまして')
    )


# メッセージイベントの場合の処理
@handler.add(MessageEvent, message=TextMessage)
def handle_text_message(event):
    # メッセージでもテキストの場合はオウム返しする
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text=event.message.text)
    )

@handler.add(MessageEvent, message=(ImageMessage, AudioMessage))
def handle_image_audio_message(event):
    # 画像と音源の場合は保存する
    content = line_bot_api.get_message_content(event.message.id)
    with open('file', 'w') as f:
        for c in content.iter_content():
            f.write(c)

参考

Django備忘録: 複数チェックボックス

複数のチェックボックスから値を受け取る方法

複数のチェックボックスやテキストから値を得る方法がわからなかったので調べてみました。 request.POSTからkey-valueで取得しようとすると一つのinputタグしか取得できないため、POSTのメソッドを使用しなければなりません。

HTML

サンプルのHTMLは以下になります。nameが同一で、valueが異なるときを想定しています。

<input type="checkbox" name="test" value="value1" />
<input type="checkbox" name="test" value="value2" />

viewでの受け取り方

今回は関数ベースのviewが前提です。

>>> values = request.POST.getlist('test')  # testはcheckboxのinputタグのname
>>> print(values)
['value1', 'value2']

getlistメソッドについて

request.POSTおよびrequest.GETオブジェクトはdjango.http.QueryDictクラスのインスタンスでした。 そのQueryDictは 辞書型のサブクラスであり、通常の辞書のメソッドに加えてgetlist, lists, urlencodeなどのメソッドも追加されていることがわかりました。 このQueryDictのメソッドを用いてリストを得ることができます。

参考にした公式ドキュメントを貼付します。 https://docs.djangoproject.com/ja/2.0/ref/request-response/#querydict-objects

独学プログラマーを読んでみた

この本を買ったきっかけ

きっかけは第一章を本屋で読んだことでした。私は現在未経験からプログラマーを目指している一員です。プログラミングの勉強を初めて一年くらいがたち、本格的にプログラマーへの転職を考えていました。

そんな中で目にした本書のイントロダクションは印象的でした。特に著者がモチベーションを維持するためのテクニックは衝撃的で、全く思いつかない上に真似できません。未経験からプロになるために必要なことがわかると思い、購入しました。

概要

本書は六部構成になっており、プログラミングの基本から、各ツールの使い方など広く浅い内容であると思います。

本書の対象者

本書はプログラミング経験のない方をメインターゲットとしています。そのため、普段からガリガリ書いているプログラマーの方には少し物足りないかもしれないです。

良かったところ

各章にチャレンジ問題がある

読んだあとにどのくらい身についてるかをチェックできるので助かります。

日本語版の注釈

実際にコードを書くときに日本語を扱う上でのギャップはなく、原著のように読むことができました。 また、各詳細サイトも日本語版のリンクが添付してあり、参考になりました。

データ構造・アルゴリズム

プログラミングの入門本ではあまり触れられない内容に見えますが、本書では紹介されています。 この本で学ぶというようりも、課題の一つとして認識するためという印象です。

Part5

いざ言語の文法や使い方を覚えた状態から"仕事"にするまでに何が必要か書いてあります。 紹介されているのは欧米流ですが、参考になることはとても多いです。

総評

もっと早く手に取れたら良かったです。Pythonの入門本というよりもプログラマーの入門本だと思っています。 これからプログラマーへの道を考えている人にはおすすめの一冊です。

独学プログラマー Python言語の基本から仕事のやり方まで

独学プログラマー Python言語の基本から仕事のやり方まで

HTTP入門

こんにちは、ぬまやんです。最近Goプログラミング実践入門を購入しました。この本を元にWebアプリやHTTPについて勉強した内容をまとめてみます。多数見当違いな記述もあると思いますが、ご指摘お願いいたします。

1. HTTPの定義

  • クライアント(ユーザー側)とサーバー(サービス提供側)をリクエスト/レスポンスの1回のやりとりで完結させる通信

例: twiiter
ユーザーがツイート検索することでリクエストがサーバーに送信され、サーバーがレスポンスを返して、検索結果をブラウザに表示

  • HTTP1.Xではリクエスト/レスポンスはテキストベースにて行われていたが、HTTP2.0になるとバイナリ

1.1 HTTPリクエス

  • リクエストは以下の構成からなる
  • リクエストヘッダの後にはメッセージがなくても空行を必ず挟むこと
GET /example.com/hatena.html HTTP/1.3
User-Agent: Mozilla/5.0

(メッセージ本体)
  1. リクエスト行(メソッド URI バージョン)
  2. リクエストヘッダ (0行以上)
  3. 空行
  4. メッセージ本体(省略可能)

リクエストメソッド

  • リクエストメソッドはWebアプリに対して、処理してほしい要求内容を表す
  • 主要なメソッドと要求内容を記載する
    • GET: 指定するリソースの返却
    • POST: メッセージ本体のデータをリソースに送信(送信後の処理内容はサーバーが決定)
    • PUT: メッセージ本体のデータをリソースに置き換え
    • DELETE: リソースの削除
  • POSTとPUTは2回同様のメソッドを送信した時にサーバの状態が変更されるか否かが異なる
    • POSTは2回サーバに送信した時にサーバ状態が変更される場合がある
    • PUTは2回サーバに送信した時、2回目はサーバの状態を変更しない

リクエストヘッダ

  • リクエストに付随する情報が表示される
  • 主要なヘッダを記載する
    • Content-Length: メッセージ本体のバイト数
    • Host: サーバ名とポート番号
    • User-Agent: クライアント情報

1.2 HTTPレスポンス

  • レスポンスの構成をリクエスト同様表示する
  • リクエスト同様にヘッダの後には必ず空行を含める
200 OK
Date: 05 Nov 2017 10:02:59 GMT
Content-Length: 712300

<!DOCTYPE html> (リクエストHTML)
  1. ステータス
  2. レスポンスへッダ (0行以上)
  3. 空行
  4. HTML

レスポンスステータス

  • レスポンスはステータスによって結果を要約している
  • 以下に主要レスポンスを記す
    • 1XX: サーバがリクエストを受信し、処理中である状態
    • 2XX: クライアントのリクエストを元に処理が成功した状態
    • 3XX: サーバがリクエストを処理したが、クライアントからの追加情報を待っている状態
    • 4XX: クライアントエラーであり、リクエストを見直す必要がある(404 Not Foundなど)
    • 5XX: サーバエラーであり、サーバ側の不具合のためリトライすると成功する可能性がある

レスポンスヘッダ

  • リクエストヘッダと同様な記法でリクエスト結果の情報が記載されている
  • 以下に主要なヘッダを記載する
    • Allow: サーバのサポートしているリクエストメソッドを記載
    • Server: ドメイン
    • Date: 日時

2. Webアプリケーション

  • WebアプリケーションはHTTPを用いて以下の動作を行うシステム

    1. HTTPを介してリクエストメッセージを受信
    2. リクエストから処理を行う
    3. 結果を元にHTMLとレスポンスを生成してクライアントに送信
  • これらの処理を行うために、Webアプリケーション内にはハンドラとテンプレートエンジンの二つの構成がある

2.1 ハンドラ

  • クライアントからのリクエストを受信し、処理を行うもの
  • 一般的にハンドラをHTTPメッセージ処理を行うコントローラとアプリケーション処理を行うモデルに分割する

2.2 テンプレートエンジン

  • クライアントに返却するHTMLを作成するもの
  • 静的テンプレートとアクティブテンプレートに分かれる
    • 静的テンプレート: HTMLにあらかじめ入力項目を設けて、リクエストを元に埋め込んでいくもの
    • アクティブテンプレート: 条件式や繰り返しを用いてHTMLを動的に生成するもの(PHPはアクティブテンプレートから始まった)

感想

  • HTTPの仕組みのシンプルさが直感的でとてもわかりやすかったです
  • 常に広く普及する仕組みにわかりやすさは欠かせないものであると実感しました

僕のワンダフル・ライフ

はじめに

最近気になっていたナミヤ雑貨店の奇蹟と僕のワンダフル・ライフのどちらを見ようか悩み、 ホームドラマでさっぱりしたい気分だったのでわんこを観てきました。

あらすじ

ある一匹(?)の犬が何度も生まれ変わり、命の恩人との再会を目指すお話です。

感想

細かいところが気にならなくなるようなファンタジーですね。 何回か泣いてしまいました。

もしかしたら賢い犬は過去何回か転生してきているんじゃないかと思ってしまいます。 はじめベイリーだった頃は無邪気で遊ぶのが大好きな犬だったのに、警察犬になり人を救っていく姿はめちゃめちゃかっこいいです。 イーサン(アダルト)の元に帰って来てハンナ(アダルト)を引き合わせたのはグッジョブでしたね。 イーサンの幸せを慮った行動でした。

お気に入りはマヤの元にいた頃でお互いに心を理解し合えるほどの関係が眩しかった分、転生の時は切なかったですね。 初めての恋も切なく、相手の犬の気持ちも聞きたかったです。

犬がどれだけ人を思い愛してくれるかがわかるお話でした。 犬や猫を飼う時はできるだけ一緒にいて遊んでたくさん思い出を作りたいです。