satoryuの日記

忘れっぽいから覚えてるうちに書いておかないと。

PHPでGoogle Suggest APIを利用するためのパッケージを作って、リリースした。

packagist.org

タイトルの通りなのですが、先日、google_suggest というPHPライブラリをリリースしました。

Googleみたいなサジェスト機能を作りたい!

といった時に是非ご活用ください。

以下は、開発のときの余談です。

続きを読む

easyAuthを使ってAzure FunctionsでTwitter OAuth認証付きのAPIを作る。

Azure Functionsには認証認可を扱うeasyAuthと呼ばれる機能があり、ここでその使い方について紹介する。

Azure Functions は、Azure Web Appがベースとなっていて、だいたいWeb App で出来ることはFunctionsでもできる。 Web Appには、TwitterFacebookなどの外部での認証と連携させる機能を持っており、それを利用し、アプリケーションの認証機能部分を作ることができる。 また、Token Storeを利用することで、認証したユーザーのAccess Tokenも保存し、特別なライブラリを使用することなくそのAccess Tokenを利用することができる。

それらを試すために、Twitter OAuthを利用した認証付きのツィートを投稿するAPIをAzure Functionsアプリを作ってみた。

github.com *1

ちなみに、Vue.jsで作ったフロントを組み合わせたものが、こちら。

takoyaki.netlify.com

もくじ

前提

予めAzure Functionsのアプリが1つあること。 以降、文中ではsamplefuntionとして参照する。

Twitter にアプリケーションを登録

Twitter Developers を開き、アプリケーションを登録する。 登録するCallback URLは、 https://samplefunction.azurewebsites.net/.auth/login/twitter/callback を入れる。 これは、あとで設定するeasyAuthが追加するCallback用のエンドポイントのURLである。

他の必須項目を入力して登録後、発行されたConsumer API KeyとAPI Secret Keyを控えておく。

Azure Functions の認証/認可の設定

Azure Portal から、Azure Functions のブレードを開き、Authentication/Authorization から以下のように設定する。

f:id:satoryu:20190404193101p:plain

easyAuthによる認証

ログイン

https://samplefunction.azurewebsites.net/.auth/login/twitter をブラウザで開くと、TwitterOAuth認証画面にリダイレクトされる。 ここで先程登録したアプリケーションにTwitterアカウントで認証することができる。

ログアウト

https://samplefunction.azurewebsites.net/.auth/logout にアクセスすることで、ログアウトができる。

Access Tokenの利用

認証後に特別なライブラリを利用することなくAccess TokenをFunctionアプリは取得することができる。

具体的には、Twitterで認証した後に、ドメインsamplefunction.azurewebsites.netに対してAppServiceAuthSessionというCookieが発行される。このCookieを付けて、Azure Functionsアプリにリクエストを送ることで認証済みとして扱われる。 そして、アプリケーションへのリクエストに下記のヘッダが追加される。

  • X-MS-TOKEN-TWITTER-ACCESS-TOKEN
  • X-MS-TOKEN-TWITTER-ACCESS-TOKEN-SECRET

これはそれぞれAccess TokenとAccess Token Secretなので、Consumer KeyとConsumer Secretと組み合わせることで、Twitter RESTful APIを認証済みのユーザーとして利用することができる。

実際のコード

実際にこの仕組を利用して、Twitterに投稿するためのFunctionのコードを載せる。

takoyaki-api/index.js at master · satoryu/takoyaki-api · GitHub

const Twitter = require('twitter')

module.exports = async function (context, req) {
    const status = req.body.status
    const twitter = new Twitter({
        consumer_key: process.env.TWITTER_CONSUMER_KEY,
        consumer_secret: process.env.TWITTER_CONSUMER_SECRET,
        access_token_key: req.headers['x-ms-token-twitter-access-token'],
        access_token_secret: req.headers['x-ms-token-twitter-access-token-secret']
    })

    try {
        const tweet = await twitter.post('statuses/update', { status })
        context.bindings.res = { body: tweet }

        context.done()
    } catch(err) {
        context.log.error(err)
        context.bindings.res = {
            status: 500,
            body: err
        }
        context.done(err)
    }
};

ここでは、twitterというパッケージを利用して、Twitter APIへアクセスしている。 それを利用する際に、環境変数からConsumer Key、リクエストに追加された2つのヘッダからAccess Tokenを用いている。 環境変数TWITTER_CONSUMER_KEYTWITTER_CONSUMER_SECRET は予めFunctionアプリのApp Settingsから追加しておく必要がある。

おわりに

easyAuthによる認証とToken Storeを使うことで、OAuthの認証の実装や認証後に得られるAccess Tokenの管理をする必要がなく、DBを準備することもなく、アプリケーションの開発に集中することができる。

easyAuthをローカルの開発環境で起動(もしくは再現)させる方法をまだ見つけられていないので、テストする際には実際にデプロイしなければならないところが課題として残っている。 もしご存知の方がおりましたら教えてください。

参考

開発者の@cgillum のeasyAuthのWikiに詳しく書いてある。

github.com

また、Web App の公式ドキュメントにも認証と認証後のAccess Tokenの扱い方について書いてある。

docs.microsoft.com

*1:なぜタコ焼きにしたのかはよく覚えてない。「つぶやき」をもじっただけ。

WebpackでApplication Insightのコードを埋め込む。

Microsoft Azureが提供するサービスの1つにApplication Insightsがある。 これはアプリケーションのメトリクスを収集、集計するための機能を提供してくれるサービスで、お手軽に導入できて便利なのでよく使っている。

公式ドキュメントでは、HTMLにコードを埋め込む方法を書いているが、JavaScriptなどをWebpackで一元管理したい場合については触れられていない。 ということで、そのやり方を調べてみた。

目次

  • HTMLにタグを埋め込む方法
  • Webpack で埋め込む方法
    • パッケージのインストール
    • Application Insightsを埋め込む
    • リリースの時だけ埋め込むようにする
続きを読む

プライベートリポジトリで提供しているgemを使ったアプリをHerokuにデプロイする。

何らかの理由で外部に公開はできないgemがあって、それを使ったRubyアプリケーションをデプロイする場合にどうしたらいいのか調べてみた。

何らかの理由というのは、まだ開発途中で試験的に使ってみたいとか、ビジネスに特化した機能であって外に公開できないなど、いくつかあると思う。複数のRubyアプリケーションを扱っている会社なんかだと、共通する機能をgemにしたりするだろうから、ありえるシナリオだと思う。

もくじ

TL;DR

  • GitHubのプライベートリポジトリにアクセスできるパーソナルアクセストークンを発行
  • ローカル環境では、bundle config github.com *********:x-oauth-basic*********はパーソナルアクセストークン) でクレデンシャルを設定
  • デプロイ先の実行環境では、export BUNDLE_GITHUB__COM=*********:x-oauth-basic などで環境変数でクレデンシャルを設定

Gitリポジトリで公開されているgemを使う

bunlderでは、rubygems.org やパッケージ管理サービスで配布しているものだけでなく、gitやgithub上のgitリポジトリで公開されているgemも使用することができる。

qiita.com Bundler: How to install gems from git repositories

Gemfile内で、gemgitオプションを付けることで

source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

# gem "rails"

gem 'sample_private_gem', git: 'https://github.com/satoryu/sample_private_gem.git'
gem "rack", "~> 2.0"

プライベートリポジトリで公開されているgemを使おうとするとどうなるか

ここで登録されているsample_private_gemGitHub上のプライベートリポジトリで公開されている場合、デプロイすると何が起こるのか。

試しに、Heroku上にデプロイしてみると、途中で該当のgemを取得できずにデプロイが失敗する。

$ git push heroku master
Enumerating objects: 7, done.
Counting objects: 100% (7/7), done.
Delta compression using up to 4 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 699 bytes | 699.00 KiB/s, done.
Total 4 (delta 0), reused 0 (delta 0)
remote: Compressing source files... done.
remote: Building source:
remote:
remote: -----> Ruby app detected
remote: -----> Compiling Ruby/Rack
remote: -----> Using Ruby version: ruby-2.5.3
remote: -----> Installing dependencies using bundler 2.0.1
remote:        Running: bundle install --without development:test --path vendor/bundle --binstubs vendor/bundle/bin -j4 --deployment
remote:        Fetching gem metadata from https://rubygems.org/..............
remote:        Fetching https://github.com/satoryu/sample_private_gem.git
remote:        fatal: could not read Username for 'https://github.com': No such device or address
remote:
remote:        Retrying `git clone 'https://github.com/satoryu/sample_private_gem.git' "/tmp/build_0902676fab2649d6da106b713a8423d7/vendor/bundle/ruby/2.5.0/cache/bundler/git/sample_private_gem-b3cd9b512dc81bc540e01f090efba0b37ff88c98" --bare --no-hardlinks --quiet` due to error (2/4): Bundler::Source::Git::GitCommandError Git error: command `git clone 'https://github.com/satoryu/sample_private_gem.git' "/tmp/build_0902676fab2649d6da106b713a8423d7/vendor/bundle/ruby/2.5.0/cache/bundler/git/sample_private_gem-b3cd9b512dc81bc540e01f090efba0b37ff88c98" --bare --no-hardlinks --quiet` in directory /tmp/build_0902676fab2649d6da106b713a8423d7 has failed.
remote:        fatal: could not read Username for 'https://github.com': No such device or address

プライベートリポジトリのgemを使うための設定

bundler の設定でプライベートリポジトリにアクセスする際のクレデンシャルを指定することができる。

bundler.io

このように、GitHubのユーザー名とパスワードを登録することでアクセスできる。 ローカルでの開発環境で設定する際は使用しているGitHubユーザーのアカウントとパスワードで良いが、デプロイ先の環境でそれを使うのはセキュリティ上良くないので、GitHubのパーソナルアクセストークンを使う方が良いと思う。

GitHubのパーソナルアクセストークンを発行

今回はプライベートリポジトリにアクセスするため、発行するときのスコープをrepo 全体にする。 下記のコマンド中の*********は、ここで発行したパーソナルアクセストークンに置き換えて使用する。

ローカル環境での設定

クレデンシャルの設定の方法は、ホスト単位、リポジトリ単位の2通りある。

ホスト単位

今後も同様なgemが増えるのであれば、予めGitリポジトリのホスト(今回はGitHub)に対して設定しておくと良さそう。

bundle config github.com *********:x-oauth-basic

リポジトリ単位

bundle config https://github.com/satoryu/sample_private_gem.git *********:x-oauth-basic

デプロイ先の実行環境

bundleコマンドが実行できるのであれば、ローカル環境と同じように設定すると良い。 しかし、HerokuなどPaaSでは、ホスト上でのシェルが実行できないため、代わりに環境変数を用いて同様の設定をする。

例えば、Herokuの場合、herokuコマンドを使って、アプリの環境変数を指定できる。

$ heroku config:set BUNDLE_GITHUB__COM=*************************:x-oauth-basic
Setting BUNDLE_GITHUB__COM and restarting ⬢ sample-private-app... done, v5
BUNDLE_GITHUB__COM: *************************:x-oauth-basic

おまけ

HerokuみたいにGitリポジトリへのプッシュをトリガーにしてデプロイする環境だと、再度デプロイするために無理やり何か変更する必要があると思ってたけど、gitは空のコミットを作ることができるので、それを使うといいらしい。

git commit --allow-empty -m 'Redeploy'

stackoverflow.com

「Small Factory 4.0」を読んだ。

ちょっとだけIoTに興味があって、たまたま見つけたので読んでみたら面白かった。

町工場の業務改善のためのIoTサービスとそれを用いたコンサルティングを展開しているiSmart Technologies社社長自身が、どのように事業を始めて、発展させていったかについて書いてあった。 使用している技術については細かくは書いてなかったが、単に自社製品について紹介するのではなく、対象顧客を考えての製品づくりや、製品を導入するだけでなく教育についてが書かれていて、物語として面白い。 エンジニアであった社長だけに、自身で秋葉原へセンサー部品を探して、色々試していたりと、そういうものづくりが好きな人が読むとワクワクするんじゃないだろうか。

面白かったところを箇条書きにしてみる。

  • 大手メーカーが出すIoT製品が、費用含めて導入コストも高くなかなか広まらない状況の中、いかにして既存の工場、それも特に町工場や中小企業に入れてもらいやすいかを考え抜いていて、安価でシンプルな製品ができている。
  • 新しいことに積極的に取り組む企業文化づくりは、ハードウェアが絡むと難しそうな気がしてたけど、そうでもなさそう。
  • 製品を入れてもらって、顧客企業内で広めるための施策についても考えられていて、「導入する」ことが目的ではなく、結果的にサイクルタイムなど工場自体がカイゼンされていることを目指している。 

実物とか、実際に導入している工場とか見てみたいなぁ、なんて思った。

Vue.js で開発を始める手順をまとめてみる。

フロントエンド開発に疎い自分とサヨナラしようと昨年末くらいからVue.jsの勉強をしている。 ドキュメントやチュートリアルを見て、自分で考えた小さいアプリケーションを作りながら覚えていこうとしている。 何個か作ってみたけれど、毎度初回に何のパッケージをいれて、どうセットアップするかで時間を取られるので、ここいらで少し自分なりにまとめてみる。

もくじ

  • もくじ
  • TL;DR
  • リポジトリを作る
  • NPMの準備
  • Webpack
    • インストール
    • ビルド
    • HTMLを生成する
  • Vue.js
    • インストール
    • 簡単なVue.jsのアプリを作る。
    • 単一ファイルコンポーネントを使えるようにする。
  • おわりに

ここで紹介している手順は下記の環境を想定しています。

TL;DR

出来上がったものをGitHubで公開しているので、お時間が無い方はこちらをどうぞ。

github.com

続きを読む

Laravel で一部だけ違うロケールのテストデータを生成する。

例えば、こんなプロジェクトがあるとする

日本向け*1のサービスをLaraelで開発していて、Fakerを使ってテストデータを生成している。 ある時、ユーザー名に英語表記*2をサービス内で保持する必要が出てきた。

どうやるか

Faker はFactoryパターンで実装されていて、createメソッドにロケールを渡すことで、指定されたロケールに合致するジェネレーターを返してくれる。

なので、一時的に欲しいロケールのジェネレーターを生成することで実現できる。 具体的には、デフォルトのロケール'ja'の環境でUserモデルのテストデータを生成するUserFactoryがあるとき、

<?php

use Faker\Generator as Faker;
use App\User;

$factory->define(User::class, function (Faker $faker) {
    $faker_en = \Faker\Factory::create('en_US');

    return [
        'name' => $faker->name,
        'firstName' => $faker_en->firstName,
        'lastName' => $faker_en->lastName,
        'email' => $faker->unique()->safeEmail
   ];
});

と、$faker_en を生成することで異なるロケールのデータを生成できる。

$faker->nameが生成した日本語名と$faker_enで生成した氏名が全然合致しないのは仕方がないが、そこに依存する機能でなければ十分使えるやり方だと思う。

*1:今回たまたまそうなだけで、どこでもいい

*2:これもどこの言語でもいい