「WHYから始めよ! 」を読んでみて思うこと
はじめに
「WHYから始めよ」を読んだきっかけはThe Agile Guild の定例会で、なぜコミュニティに参加しているのかという問いを投げかけられたことであった。私は「価値のあるものを生み出したいから」と答えたものの、きちんと整理できていなかったため、もやもやしていた。 そこで以前紹介されたこの書籍を手に取った。
本記事では組織の一人として感じたこととエンジニアの一人として感じたこと、二つをまとめていく。 (注: 書籍についてはあまりふれません)
- 作者: サイモン・シネック,栗木さつき
- 出版社/メーカー: 日本経済新聞出版社
- 発売日: 2012/01/25
- メディア: 単行本
- 購入: 1人 クリック: 31回
- この商品を含むブログ (8件) を見る
書籍について
本書籍はTEDで公開された講演内容をもとにまとめられている。
ログミーにもまとめられているので、英語に弱い私でも理解できた。
本記事で気になったらぜひ読んで、見てほしい。
組織の一人として
WHYがない現場
以前より仕事で採用に関わることが増えてきた。
そこで気づいた問題は誰を採用するのか、この人は採用基準に満たしているのかを判断できる軸がないことである。
各々の中では採用基準があり、面接する上ですり合わせも行う。しかし、企業に明確なWHYがあれば面接官の間で基準が大きくブレることはないと思う。
似たような問題はあらゆるところでみられる。
受託開発のプロジェクトでいえば、エンジニアがなぜプロダクトを開発しているのかわかならい。
これは顧客の中のWHYを明確にし、何を作れば良いかを下ろしていく活動が不足しているためである。
評価の場合は、自分に付けられた評価の納得感がえられにくくなる。
基準が明確ではないため、評価基準が上司に強く依存してしまうからだ。
これらからWHYは企業の中で一つの基準となるものであると考えられる。
WHYがないと各所で下す決断、作る製品つまりWHATがぶれてしまい一貫性がなくなってしまうと感じた。
WHYを得るためにはどうすれば良いか
例え、組織のトップが明晰なWHYを持っていたとしても組織が大きく、WHYを伝えるHOWがなければ社員に広がらない。 そもそもCEOにWHYがなければ得ることはできない。
WHYがない状況で、エンジニアチームにできることはあるのだろうか。 私は自分たちでWHYを見つけ、広げていくしかないと思う。明確なWHYを見つけ、興味のある人に伝えていきたい。
エンジニアの一人として
エンジニアの役割
WHYからWHATまでの流れをプロダクト開発に当てはめて考えてみる。
WHYはマーケターや顧客(受託開発の場合)のようなプロダクトオーナーが考え、プロダクトバックログとして具体化する。エンジニアはプロダクトバックログをもとに開発して、WHYを実現するWHATの役割になる。
そのためエンジニアは正確かつ素早くプロダクトを実現することにコミットする。役割が明確で集中できるが、WHATに特化することがプロダクトのために最大限の価値を出しているのだろうか。
プロダクトの限界
私は(自分含めて)エンジニアがWHATに特化してしまうとタスク消化に追われやすくなるのではないかと感じる。 この場合消化以上のことをしないとプロダクトバックログがプロダクトの成長の上限となってしまう。
そもそもプロダクト開発は開始当初において不確実性が高く、不十分な情報の中かから作るものを決めている。 そのため、開発が進むにつれてわかることが多く、最初に策定したプロダクトバックログを変えていく必要が生まれることがある。
スクラムではそのためにスプリントレビューをしてプロダクトオーナーが修正していくものであると思っていたが、それでもプロダクトオーナーの裁量が上限となってしまう。 本当に価値あるものを作るためにはエンジニアもWHYの視点に立って開発することが求められるのではないか。
WHYと向き合う開発
そこで、ある案件でエンジニアとしての役割を持ちつつ、自分にWHYの視点を入れようとした。
よりよい開発になるとわかっているが、実践しようと思うととても難しい。そもそも書籍内にもある通り使う頭の機能が異なるように感じる。
私が試したときは時間を区切って頭を切り替える必要があった。 開発しているときは詳細の目で一切のブレも許さず正確に手を動かしていく。 ユーザー目線に立つときは俯瞰の目でユーザーの思考を想起させながら作っているものを触り、欲しい機能や修正したいポイントをプロダクトバックログに積んでいく。 この開発を全員とは言わず、一部エンジニアが取り組むだけでプロダクトは大きく変わるのではないかと思うし取り組んでいきたい。
まとめ
- 「WHYからはじめよ!」をよんだ
- 組織にWHYがないとどうなるかを思考してみた
- エンジニアとしてWHYの視点を持つことの重要性を感じた
DjangoのORMでちょっとしたSQLをかいた
この記事はDjango Advent Calendar 2018 15日目の記事です。
はじめに
最近色々な勉強会でORMの是非について話を聞きます。
もともと自分はDjangoで単純なSQLしか書いたことがなかったのできちんと議論できませんでした。
そこで、今回はクエリ中心にちょっとしたSQLをDjangoのORMで実装してみます。
動作環境
Dockerについて
SQLを試してみるにあたって、ローカルのPCのデータベースをセットアップすることは面倒です。
そのため、データベースやPythonのランタイムはDocker Composeを使ってセットアップしています。
実際のDockerfileは以下
FROM python:3.7 ENV PYTHONUNBUFFERED 1 ENV DOCKERIZE_VERSION v0.6.1 RUN wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz && \ tar -C /usr/local/bin -xzvf dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz && \ rm dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz WORKDIR /code/ COPY Pipfile* ./ RUN pip install pipenv && \ pipenv install --system --deploy ADD . /code
Docker Compose でマイグレーションと初期データの設定を行なっています。
version: "3" services: db: image: postgres:10 environment: POSTGRES_DB: $POSTGRES_DB POSTGRES_PASSWORD: $POSTGRES_PASSWORD env_file: - .env backend: build: . command: bash -c "dockerize -wait tcp://db:5432 && pipenv install --system --dev && python3 manage.py migrate && python3 manage.py loaddata init.json && python3 manage.py runserver 0.0.0.0:8000" volumes: - .:/code ports: - "8000:8000" working_dir: "/code/orm" env_file: - .env depends_on: - db
コードはリポジトリにて公開しています。
準備
テーブルはブログを題材に作りました。モデル定義は以下になります。
from django.db import models class Post(models.Model): title = models.CharField(max_length=255) text = models.TextField() class Comment(models.Model): post = models.ForeignKey('Post', on_delete=models.CASCADE) text = models.TextField(blank=True, null=True)
ダミーデータはフィクスチャで準備し、各検証はDjango Extensions を使いました。
試すクエリ
以下の5つのクエリを試してみたいと思います。
- 1テーブルから全データを取得
- 外部キー含めたデータを取得
- カラムに条件指定して取得する
- Case式 を使った分岐
- Countによる集計
1. テーブルから全データを取得
Postテーブルから記事データを全て取得してみます。
DjangoのORMでは all
メソッドで全データを取得することができます。
>> posts = Post.objects.all() >> posts.values() <QuerySet [{'id': 1, 'title': 'PyConに行ってきた', 'text': '楽しかった'}, {'id': 2, 'title': 'Djangoチュートリアル', 'text': '投票アプリの構築までできた'}]> >> print(posts.query) SELECT "blog_post"."id", "blog_post"."title", "blog_post"."text" FROM "blog_post"
2. 外部キー含めたデータを取得
Commentと外部キーであるPostを併せて取得してみます。
>> comments = Comment.objects.select_related().all() >> comments.values() <QuerySet [{'id': 1, 'post_id': 1, 'text': '一番よかったセッションは?'}, {'id': 2, 'post_id': 1, 'text': '懇親会はどうだった?'}, {'id': 3, 'post_id': 2, 'text': '自分は難しくてできなかった'}]> >> [comment.post.title for comment in comments] ['PyConに行ってきた', 'PyConに行ってきた', 'Djangoチュートリアル'] >> print(comments.query) SELECT "blog_comment"."id", "blog_comment"."post_id", "blog_comment"."text", "blog_post"."id", "blog_post"."title", "blog_post"."text" FROM "blog_comment" INNER JOIN "blog_post" ON ("blog_comment"."post_id" = "blog_post"."id")
3. カラムに条件指定して取得する
特定の条件下のデータを取得してみます。
>> pycon_comment = Comment.objects.filter(post__id=1) >> pycon_comment <QuerySet [<Comment: Comment object (1)>, <Comment: Comment object (2)>]> >> print(pycon_comment.query) SELECT "blog_comment"."id", "blog_comment"."post_id", "blog_comment"."text" FROM "blog_comment" WHERE "blog_comment"."post_id" = 1
4. CASE式を使った分岐
SQLのCASE-WHENを使った例です。
Postにを新しくジャンルというカラムを作って分類してみます。
>> genre_post = Post.objects.annotate( ...: genre=Case( ...: When(title__contains='Django', then=Value('Django')), ...: default=Value('Python'), ...: output_field=CharField()) ...: ).values_list('title', 'genre') >> genre_post <QuerySet [('PyConに行ってきた', 'Python'), ('Djangoチュートリアル', 'Django')]> >> print(genre_post.query) SELECT "blog_post"."title", CASE WHEN "blog_post"."title"::text LIKE %Django% THEN Django ELSE Python END AS "genre" FROM "blog_post"
5. Countによる集計
記事についているコメント数を集計してみます。
aggregate
メソッドは dict
を返します。
>> Comment.objects.aggregate( ...: pycon=Count('pk', filter=Q(post__id=1)), ...: django=Count('pk', filter =Q(post__id=2)) ...: ) {'pycon': 2, 'django': 1}
まとめ
Case式やCountのオブジェクトが用意されているのは初めて知りました。
Upsertみたいな更新系もやってみたいです。
The Agile Guildでのチーム開発
これはThe Agile Guild(TAG) Advent Calendar 2018 6日目の記事です。
The Agile Guild(TAG)に参加して取り組んだプロジェクトについて振り返りたいと思います。
自己紹介
私は元々Pythonメインのサーバーサイドエンジニアで DjangoによるAPIを実装したりしてました。
半年前くらいにでVue.jsに携わる機会があり、TAGではフロントエンドに多く関わらせていただいてます。
フロントエンド開発楽しいです。
TAGで関わってるプロジェクトについて
プロジェクトではIonicを使ったモバイルアプリのフロントエンドを担当しています。
メンバーはエンジニアとしてTAGから3人、POやマネージャーが一人づつのチームです。
TAGメンバーは複業のため、リモートかつ活動する時間帯も異なっています。
なので、情報の同期に遅れがあり、スピーディーにやりとりできない環境でとても難しいです。
開発サイクルは1スプリント1週間で、毎週月曜日にふりかえりをやってます。
ジョインしてからの課題
プロジェクトの参加当初はスムーズに開発に入れずに悩んでいました。
言語化すると以下のような課題があったように思えます。 (今も全て解決しているわけではないですが)
- 開発プロセスがわからず、進められない
- 他のチームメンバーの状況が見えにくい
- 一つのフューチャーに対してどこまで開発すればいいかわからない
- 知識の偏りがある(マネージャーがIonicに一番詳しかった)
モヤモヤしながら動けないまま数スプリントを過ごしてしまいました。
カイゼン活動
そんな中で他のTAGメンバーやマネージャー主体で、以下のカイゼン活動がなされました。
リモート朝会ではその日に稼働できるかや作業状況を共有する仕組みです。
開発プロセスの整理ではアーキテクチャからGitの運用フローなど細かいところまで決め事を作ってもらいました。
チームビルディングではドラッガー風エクササイズを使って、お互いの思いを共有しました。
そこからチームに足りていない役割やタスクを洗い出して、誰がやるかをざっくり定めていました。
これらのカイゼンはとても参考になっていて、本業の方でも導入したりしています。
また、AWS環境構築やサーバー開発など含めてリモートモブプロ会をよく開催しています。
作業のノウハウをチームに共有することで、いろんな人が対応できるようになるのが面白いです。
できていないこと・今後やりたいこと
できていないが今後やりたいことは以下になります。
- チームに不足している役割や宙に浮いているタスクを積極的に巻き取ること
- フロントエンドだけでなくバックエンドやインフラ周りの知識を得ること
- 本業の忙しさに負けないこと
まだ、課題が多くやらなければいけないことがありますが、カイゼンしながら開発したいと思います。
頑張って年内にリリースしたいです。
VueにAmplifyを入れてみた
この記事はVue.js #3 Advent Calendar 2018の4日目の記事です。
フロントエンドのライブラリかつAWSのリソースを作ることができるツールとして話題のAmplifyをVuejsに組み込んでみたいと思います。
- Amplifyとは
- CLIのインストール
- Vueプロジェクトの初期化
- Amplifyの初期化
- Cognitoの作成
- AppSyncの作成
- Vueパッケージをインストール
- ユーザーの認証画面を作成する
- GraphQLにリクエストしてみる
- まとめ
Amplifyとは
AmplifyはAWSリソースとの接続を支援するオープンソースのJavaScriptライブラリです。
強力なCLIツールが提供されていて、AWSリソースの作成をコマンド上で実行し、ライブラリに反映することができます。
現在はCognitoによる認証や、AppSyncを用いたGraphQLの利用がサポートされています。
The foundation for your cloud-powered mobile & web apps
CLIのインストール
インストールはNPMで行います。
$ npm i -g @aws-amplify/cli
下記コマンドで詳細が出力されたらインストール成功です。
$ amplify
Vueプロジェクトの初期化
Amplifyの初期化はプロジェクトのルートディレクトで実行することを推奨されているため、先にVueのプロジェクトを作成します。 実行はVue CLIを用いています。
keinuma% vue create amplify-vue-demo Vue CLI v3.1.3 ? Please pick a preset: Manually select features ? Check the features needed for your project: Babel, Router, Vuex, Linter ? Use class-style component syntax? Yes ? Use Babel alongside TypeScript for auto-detected polyfills? Yes ? Use history mode for router? (Requires proper server setup for index fallback in production) Yes ? Pick a linter / formatter config: ESLint ? Pick additional lint features: Lint on save ? Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? In package.json ? Save this as a preset for future projects? No
Amplifyの初期化
Amplify CLIを用いてAWS環境の初期化を行います。
$ amplify init
実行すると開発環境およびプロジェクトの情報を渡してあげます。
? Choose your default editor: Visual Studio Code ? Choose the type of app that you're building javascript Please tell us about your project ? What javascript framework are you using vue ? Source Directory Path: src ? Distribution Directory Path: dist ? Build Command: yarn build ? Start Command: yarn serve
Amplifyは現状CloudFormationから各リソースを作成するため、認証情報を教えないといけません。
AWS上でユーザーを作成するか聞かれるので、専用のユーザーを用意する場合はYesを選択しましょう。
リージョンとアカウント名を入力するとIAM作成画面に遷移し、ポリシーの設定を行います。
Using default provider awscloudformation AWS access credentials can not be detected. ? Setup new user Yes Follow these steps to set up access to your AWS account: Sign in to your AWS administrator account: https://console.aws.amazon.com/ Press Enter to continue Specify the AWS Region ? region: ap-northeast-1 Specify the username of the new IAM user: ? user name: amplify-demo Complete the user creation using the AWS console https://console.aws.amazon.com/iam/home?****
ユーザーを作成したら、コンソールに戻ってクレデンシャル情報を入力します。
Access Key IDおよびSecret Access Keyを入力したら、Amplify上で管理するための名前をつけることができます。
Press Enter to continue Enter the access key of the newly created user: ? accessKeyId: A**************** ? secretAccessKey: ******************************* This would update/create the AWS Profile in your local machine ? Profile Name: demo Successfully set up the new user.
認証情報の設定が完了すると改めて、ユーザー選択を行うので先ほど作ったdemo
を選択します。
? Do you want to use an AWS profile? Yes ? Please choose the profile you want to use demo ⠇ Initializing project in the cloud... CREATE_IN_PROGRESS amplifyvuedemo-20181126103438 AWS::CloudFormation::Stack Mon Nov 26 2018 10:34:38 GMT+0900 (日本標準時) User Initiated CREATE_IN_PROGRESS UnauthRole AWS::IAM::Role Mon Nov 26 2018 10:34:42 GMT+0900 (日本標準時) CREATE_IN_PROGRESS DeploymentBucket AWS::S3::Bucket Mon Nov 26 2018 10:34:42 GMT+0900 (日本標準時) CREATE_IN_PROGRESS AuthRole AWS::IAM::Role Mon Nov 26 2018 10:34:42 GMT+0900 (日本標準時) CREATE_IN_PROGRESS AuthRole AWS::IAM::Role Mon Nov 26 2018 10:34:43 GMT+0900 (日本標準時) Resource creation Initiated CREATE_IN_PROGRESS UnauthRole AWS::IAM::Role Mon Nov 26 2018 10:34:43 GMT+0900 (日本標準時) Resource creation Initiated CREATE_IN_PROGRESS DeploymentBucket AWS::S3::Bucket Mon Nov 26 2018 10:34:44 GMT+0900 (日本標準時) Resource creation Initiated ⠹ Initializing project in the cloud... CREATE_COMPLETE UnauthRole AWS::IAM::Role Mon Nov 26 2018 10:35:02 GMT+0900 (日本標準時) CREATE_COMPLETE AuthRole AWS::IAM::Role Mon Nov 26 2018 10:35:02 GMT+0900 (日本標準時) CREATE_COMPLETE DeploymentBucket AWS::S3::Bucket Mon Nov 26 2018 10:35:05 GMT+0900 (日本標準時) ⠸ Initializing project in the cloud... CREATE_COMPLETE amplifyvuedemo::AWS::CloudFormation::Stack Mon Nov 26 2018 10:35:09 GMT+0900 (日本標準時) ✔ Successfully created initial AWS cloud resources for deployments. Your project has been successfully initialized and connected to the cloud! Some next steps: "amplify status" will show you what you've added already and if it's locally configured or deployed "amplify <category> add" will allow you to add features like user login or a backend API "amplify push" will build all your local backend resources and provision it in the cloud "amplify publish" will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud Pro tip: Try "amplify add api" to create a backend API and then "amplify publish" t
CloudFormationのStack、デプロイ用のバケットが作成されると初期化は完了になります。
実行完了後に以下のファイルが作成されています。
- .amplifyrc
- amplify/
.amplifyrcはCloudFormationで作成されたリソース情報が記載されていて、amplifyディレクトリ以下にはプロジェクトの情報やリソースの詳細が格納されています。
Cognitoの作成
AmplifyでCognitoユーザープールを作成してみたいと思います。
addコマンドでユーザープールの設定を行います。
認証方法はEmailで、パスワードの強度などをカスタマイズしています。
IdeintityプールはCognitoからAPI以外の他のAWSリソースに紐づけるときに使用します。
例えばS3へのアップロードを許可する場合などですね。
$ amplify add auth Using service: Cognito, provided by: awscloudformation The current configured provider is Amazon Cognito. Do you want to use the default authentication and security configuration? No, I will set up my own configuration. ? Select the authentication/authorization services that you want to use: User Sign-Up, Sign-In, connected with AWS IAM controls (Enables per-user Stor age features for images or other content, Analytics, and more) ? Please provide a friendly name for your resource that will be used to label this category in the project: amplifyauthdemo ? Please enter a name for your identity pool. amplifyvuedemo96530290_identitypool_96530290 ? Allow unauthenticated logins? (Provides scoped down permissions that you can control via AWS IAM) No ? Do you want to enable 3rd party authentication providers in your identity pool? No ? Please provide a name for your user pool: amplifyvuedemo96530290_userpool_96530290 ? Multifactor authentication (MFA) user login options: OFF ? Email based user registration/forgot password: Enabled (Requires per-user email entry at registration) ? Please specify an email verification subject: パスワード再設定 ? Please specify an email verification message: 認証コードはこちらになります。 {####} ? Do you want to override the default password policy for this User Pool? Yes ? Enter the minimum password length for this User Pool: 8 ? Select the password character requirements for your userpool: Requires Lowercase, Requires Uppercase, Requires Numbers ? Userpool users are created with a standard set of attributes defined by the OpenID Connect specification. Mark the required attributes below: ? Specify the app's refresh token expiration period (in days): 30 ? Do you want to specify the user attributes this app can read and write? Yes ? Specify read attributes: Email ? Specify write attributes: Successfully added resource amplifyauthdemo locally Some next steps: "amplify push" will build all your local backend resources and provision it in the cloud "amplify publish" will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud
作成したCognitoをAWSに反映してみる。 init時に作成したCloudFormationにネストしてリソースを作成していることがわかります。
$ amplify push | Category | Resource name | Operation | Provider plugin | | -------- | --------------- | --------- | ----------------- | | Auth | amplifyauthdemo | Create | awscloudformation | ? Are you sure you want to continue? true ⠙ Updating resources in the cloud. This may take a few minutes... UPDATE_IN_PROGRESS amplifyvuedemo-20181126103438 AWS::CloudFormation::Stack Mon Nov 26 2018 11:16:29 GMT+0900 (日本標準時) User Initiated CREATE_IN_PROGRESS authamplifyauthdemo AWS::CloudFormation::Stack Mon Nov 26 2018 11:16:34 GMT+0900 (日本標準時) CREATE_IN_PROGRESS authamplifyauthdemo AWS::CloudFormation::Stack Mon Nov 26 2018 11:16:36 GMT+0900 (日本標準時) Resource creation Initiated ⠼ Updating resources in the cloud. This may take a few minutes... ... ✔ All resources are updated in the cloud
完了するとCognitoが作成されていることを確認できます。
また、パスワードの設定などがカスマイズされていることも確認できます。
プロジェクトの src/aws-exports.js
にユーザープールのIDが保存されます。
AppSyncの作成
続けてAppSyncを作成してみます。
認証方法は先ほど作成したCognitoを用いたいと思います。
$ amplify add api ? Please select from one of the below mentioned services GraphQL ? Provide API name: amplifyvuedemo ? Choose an authorization type for the API Amazon Cognito User Pool Use a Cognito user pool configured as a part of this project ? Do you have an annotated GraphQL schema? No ? Do you want a guided schema creation? true ? What best describes your project: ? What best describes your project: One-to-many relationship (e.g., “Blogs” with “Posts” and “Comments”) ? Do you want to edit the schema now? No ? Press enter to continue GraphQL schema compiled successfully. Edit your schema at ***/amplify-vue-demo/amplify/backend/api/amplifyvuedemo/schema.graphql Successfully added resource amplifyvuedemo locally Some next steps: "amplify push" will build all your local backend resources and provision it in the cloud "amplify publish" will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud
今回はSchemaを定義せずにチュートリアルで用意されているブログ用のスキーマを選択しました。
チュートリアルではデータソースにDynamoDBが使われているので、AppSyncと直接同期することができます。
完了するとamplify/backend/api/{API name}/build/schema.graphql
が作成されて、スキーマが定義されます。
schema.graphql
type Blog @model { id: ID! name: String! posts: [Post] @connection(name: "BlogPosts") } type Post @model { id: ID! title: String! blog: Blog @connection(name: "BlogPosts") comments: [Comment] @connection(name: "PostComments") } type Comment @model { id: ID! content: String post: Post @connection(name: "PostComments") }
Resolverが amplify/backend/api/{API name}/build/resolver
以下に格納され、リソースの詳細情報が保存されています。
作成したAppSyncをAWSに反映しましょう。
$ amplify push | Category | Resource name | Operation | Provider plugin | | -------- | --------------- | --------- | ----------------- | | Api | amplifyvuedemo | Create | awscloudformation | | Auth | amplifyauthdemo | No Change | awscloudformation | ? Are you sure you want to continue? true GraphQL schema compiled successfully. Edit your schema at ***/amplify-vue-demo/amplify/backend/api/amplifyvuedemo/schema.graphql ? Do you want to generate code for your newly created GraphQL API (Y/n) ? Do you want to generate code for your newly created GraphQL API Yes ? Choose the code generation language target ? Choose the code generation language target javascript ? Enter the file name pattern of graphql queries, mutations and subscriptions (src/graphql/**/*.js) ? Enter the file name pattern of graphql queries, mutations and subscriptions src/graphql/**/*.js ? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions (Y/n) ? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions Yes ⠇ Updating resources in the cloud. This may take a few minutes... UPDATE_IN_PROGRESS amplifyvuedemo-20181126103438 AWS::CloudFormation::Stack Mon Nov 26 2018 11:34:52 GMT+0900 (日本標準時) User Initiated UPDATE_IN_PROGRESS authamplifyauthdemo AWS::CloudFormation::Stack Mon Nov 26 2018 11:34:57 GMT+0900 (日本標準時) ... ✔ Generated GraphQL operations successfully and saved at src/graphql ✔ All resources are updated in the cloud GraphQL endpoint: https://***.appsync-api.ap-northeast-1.amazonaws.com/graphql
言語を選択してコード生成ができます。今回はJavaScriptを選択しています。
完了するとプロジェクトに以下のファイルが生成されています。
src/graphql
.graphqlconfig.yml
.graphqlconfig.yml
はAppSyncの情報が格納されています。
src/graphql
は定義されているクエリやミューテーションが文字列として格納されている。
使用するときにクエリを選ぶことができる。
Vueパッケージをインストール
Vueのライブラリとして使用するためのパッケージをインストールする
$ npm i aws-amplify aws-amplify-vue
これでAmplifyをライブラリとして使う準備ができました。
ユーザーの認証画面を作成する
AmplifyにはいくつかUIが用意されています。
認証では Authenticator
コンポーネントを組み込むことで認証フローの実装をスキップできます。
認証情報はストア上で行うため、先にストアの設定を行います。
store.js
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: { user: null }, mutations: { setUser(state, user) { state.user = user } }, actions: { } })
認証情報はページ遷移ごとにチェックする必要があるため、ルーティングに認証チェックフローを実装します。
Vue Routerと一緒にAmplifyのライブラリのインポートを行います。
router.js
import Vue from 'vue' import Router from 'vue-router' import Home from '../views/Home.vue' import AmplifyStore from '../store' import { AmplifyEventBus, AmplifyPlugin, components } from 'aws-amplify-vue' import * as AmplifyModules from 'aws-amplify' Vue.use(Router) Vue.use(AmplifyPlugin, AmplifyModules)
認証状態がパスワードチェック前なのかサインイン前なのかなどをEventBusで確認します。
router.js
let user; getUser().then((user) => { if (user) { router.push({path: '/'}) } }) AmplifyEventBus.$on('authState', async (state) => { if (state === 'signedOut'){ user = null; AmplifyStore.commit('setUser', null); router.push({path: '/auth'}) } else if (state === 'signedIn') { user = await getUser(); router.push({path: '/'}) } }); function getUser() { return Vue.prototype.$Amplify.Auth.currentAuthenticatedUser().then((data) => { if (data && data.signInUserSession) { AmplifyStore.commit('setUser', data); return data; } }).catch(() => { AmplifyStore.commit('setUser', null); return null }); }
ルーティング前にユーザーが設定されているかで、ログイン画面にルーティングするかを決める処理を実装します。
router.beforeResolve(async (to, from, next) => { if (to.matched.some(record => record.meta.requiresAuth)) { user = await getUser(); if (!user) { return next({ path: '/auth', query: { redirect: to.fullPath, } }); } return next() } return next() })
Amplifyが用意しているAuthenticatorコンポーネントを /auth
に宣言します。
const router = new Router({ mode: 'history', base: process.env.BASE_URL, routes: [ { path: '/', name: 'home', component: Home }, { path: '/auth', name: 'auth', component: components.Authenticator } ] })
ローカル上で起動し http://localhost:8080/auth
にアクセスするとログイン画面にルーティングされます。
これで認証機能を作成することができました。
Authenticator
コンポーネントはストアのユーザー情報とCogntioにリクエストした認証状態を確認し、適切なルーティングを行ってくれます。
GraphQLにリクエストしてみる
AppSyncのリソースと同時に作成された src/graphql/
を使うことでリクエストを簡単に実装できます。
はじめに必要なライブラリをインポートしましょう。
import { API, graphqlOperation } from 'aws-amplify' import * as mutaions from '@/graphql/mutations'
メソッドにGraphQLへのリクエストを持ったコンポーネントを実装します。
export default { name: 'CreatePost', data: function() { return { postData: { author: '', content: '' }, myPost: '' } }, methods: { createPost: function () { API.graphql(graphqlOperation(mutaions.createPost, this.postData )) .then((response) => { console.log(response) this.myPost = response }) .catch((err) => { console.log(err) }) } } }
graphqlOperation
に使用するクエリとパラメータを指定したオブジェクトをAPI.graphql
関数に渡しています。
クエリを変更するだけで、リクエストを変えることができます。
実装の全体はGitHubにリポジトリを作成しているのでこちらをご確認ください。
まとめ
AmplifyをVueに組み込んでみました。
CLIでAWSリソースを作るのは便利ですが、CI/CDとの兼ね合いが難しそうです。
VueのAmplify UIはReactに比べてまだデザインの拡張性は難しいですが、簡単にAWSとの連携を実装できます。
AmplifyはOSSなので欲しい機能などはPRを出してみたいです。
参考:
Django備忘録: 管理画面のパスワードリセットにメールを使う
Djangoの管理画面のパスワードリセットにメールアドレス認証を実装する機会があったので、記事にまとめてみました。 公式ドキュメントを参考に実装しています。
Django の admin サイト | Django documentation | Django
GitHubに実装を公開しています。
目次
Djangoの初期設定
Djangoのマネージドコマンドを使ってプロジェクトの初期化を行います。
$ django-admin startproject django_admin_email
URLの追加
Djangoは管理画面のパスワードリセットのURL設定を追記するだけで設定できます。
URLの name
はテンプレートで決められているため、変えられません。
urls.pyに以下を追記する。
from django.contrib import admin from django.contrib.auth import views as auth_views from django.urls import path urlpatterns = [ path('admin/', admin.site.urls), path( 'admin/password_reset/', auth_views.PasswordResetView.as_view(), name='admin_password_reset', ), path( 'admin/password_reset/done/', auth_views.PasswordResetDoneView.as_view(), name='password_reset_done', ), path( 'reset/<uidb64>/<token>/', auth_views.PasswordResetConfirmView.as_view(), name='password_reset_confirm', ), path( 'reset/done/', auth_views.PasswordResetCompleteView.as_view(), name='password_reset_complete', ), ]
/admin/login
にアクセスするとログイン画面にパスワードリセットのリンクが追加されます。
管理画面のログインのテンプレートHTMLを確認してみましょう。
password_reset_url
がURLに登録されているかで、リンクを出し分けていることがわかります。
django/login.html at master · django/django · GitHub
{% if password_reset_url %} <div class="password-reset-link"> <a href="{{ password_reset_url }}">{% trans 'Forgotten your password or username?' %}</a> </div> {% endif %}
Gmailで送ってみる
開発環境でのメール送信はc-bataさんの記事が参考にになります。
ここではGmailを使ったメール送信を設定してみます。
settings.pyに以下の設定を追加します。 EMAIL_HOST_USER
, EMAIL_HOST_PASSWORD
は適宜変更してください。
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' EMAIL_USE_TLS = True EMAIL_HOST = 'smtp.gmail.com' EMAIL_HOST_USER = 'example@gmail.com' EMAIL_HOST_PASSWORD = 'password' EMAIL_PORT = '587'
送信する前にGoogle側のパスワード設定を変える必要があります。
アカウント設定の「ログインとセキュリティ」から「 安全性の低いアプリの許可」をONにします。
実際に送られたメールはこちら。
このメールは 0.0.0.0:8000 で、あなたのアカウントのパスワードリセットが要求されたため、送信されました。 次のページで新しいパスワードを選んでください: http://0.0.0.0:8000/reset/MQ/xxxxxxxxxxxxxxxxx/ あなたのユーザー名 (念のため): keinuma ご利用ありがとうございました! 0.0.0.0:8000 チーム
ローカル感丸出しですが無事に送れました。
SendGridやAWS SESを組み込んだメール送信もやってみたいです。
Vue Fes Japan参加レポート
日本初のVueカンファレンスVue Fes Japanに参加してきました。 実業務でのお話やVueのこれからのようなコアな話まで聞けたので、レポートにまとめたいと思います。
キーノート
Evan氏によるVue3.0の展望についてでした。 主に以下の変更が加えられます。
- メモリ使用量低下による高速化
- ビルドファイルの軽量化
- メンテナンスしやすさの向上
- Hooks API
何点か気になった話を書いていきます。
ビルドファイルの軽量化ではTree Shakingが出てきました。
使われていない v-if
やv-model
などをバンドルせずにビルドできるそうです。
メンテナンスのしやすさではデバッグ機能の追加がありました。
Devtoolの確認に加えて、イベントなどのブレークポイントを見れるようになるのはでかいですね。
Hooks APIはReactで出てきた機能らしいです。アフターパーティーで詳しく聞けました。
ストア管理するほどでもない状態を楽にできるそうなので、Reactを触って先に体験したいですね。
勉強不足で多くを理解できなかったですが、復習して3.0を待ちたいと思います。
ランチセッション
LINEさん
ランチセッション一発目はLINEさんでした アプリだけではなく、クーポンやスタンプのページはVueで開発されているようです。 新規開発もNuxtやVueを用いて開発されてるみたいですねー。
ブースではVueのクイズに答えると抽選でVue.js入門をゲットできる催しをやってました。 抽選は逃してしまいましたが、面白い企画でした。
Scouterさん
NuxtMeetUpでおなじみのScouterさんの発表でした。 みなさんだいたい2014年のVueがメジャーバージョンになる前から使い始めてますね。 Scouterさんはエンジニア10名で継続的にプロダクトをローンチしていてすごいです。
Reproさん
ラストは普段もくもく会でお世話になっているReproさんのセッションでした。 最初はjQueryで開発されていたものをVueで書き換えたということでした。 jQueryからの移行しやすさはVueの魅力の一つなのかなと思いました。
セッション
Vue.js と Web Components のこれから
WebComponents(以下WC)とVueとの共存についてのセッションでした。 WCのメリットは以下のようです。
デメリットは以下
- 属性値にstringしか渡せない
- 外部からのイベントハンドリングが難しい
- CSS設計方法が大きく変わる
Vue CLI3はWCへのbuildをサポートしているためビルド時にtargetオプションをつけることで動かせます。(参考)
$ vue-cli-service build --target wc --name element ./src/App.vue
そのため、Vueで開発してきたものをWCを用いて描画することが簡単にできます。
WCはWeb標準機能のため、相対的に寿命が長く、激しい変更が少ないことが見込まれます。 いつVueが終わってしまうかという心配事が減ることが期待できますね。
WCはあくまでDOMの可視化にまつわる仕組みなのでアプリケーションフレームワークとしての機能は有していません。
データ管理やイベントなどフレームワークとしてはVueが、UI部分はWCに置き換わるのが今後の展望とのことでした。
WCのデフォルトでマテリアルデザインをサポートし始めたら強いですね。
記法がVueに似ている(VueがWCに着想を得ている)とのことだったので自作アプリにも組み込みたいです。
Vue Designer: デザインと実装の統合
デザインと実装に距離ができてしまう問題を解決するツールについてのセッションでした。
デザインとフロントエンドの実装では以下の課題がみられます。
- デザインファイルと実装で使うツールが異なる
- デザインと実装の管理が重複する
- 動的なデザインを考慮しにくい
現在開発されていて、上記課題を解決しようとしているツールには以下があるそうです。
そこでkatashinさんはSFCでデザインできるようにするツールを開発中でした。
GitHub - ktsn/vue-designer: Vue component design tool
SFCをプレビューし、ドラッグ&ドロップやインスペクターでデザインを変更します。
変更はSFCのファイルにも反映されて、デザインとSFCファイルの双方向バインディングが実現されていました。
デザイナーにも使ってもらえることを目標にしているので、デザインツールで使えるような機能も考えているようです。
実装はクライアント・サーバ型でできていて、SFCをASTに変換し、WebSocketを通してレンダリングされていました。
クライアント・サーバ型のメリットは以下になるそうです。
- エディタロックインしない
- スタンドアロンもできる
- 共同編集できる
パースはvue-eslint-parser, postcss, babelを使っていて、TypeScript対応もあるみたいですね。
一人でパーサーを書いて、WebSocketを使い、デザインツールを追従していることが驚きでした。
今後はコンポーネントカタログの自動生成も考えているそうなので、とても期待しています。
Atomic Design のデザインと実装の狭間
デザイナーとエンジニア間の協業という観点のエモいお話でした。
現在Atomic Designが主流になる中でデザイナーからコンポーネントベースの手法が適応されている背景があります。 コンポーネントベースでデザインする上での難点は以下になっているそうです。
- 学習コストが高い
- デザインの自由度が下がる
- 認識が異なる
コンポーネントはエンジニアの概念のため、デザイナーに適応することは難しいそうです。
例として挙げられていたのは構造化プログラミング vs オブジェクト指向プログラミングでした。
Javaが出てきた当初はスピードなどを槍玉に挙げられて非難されていたが、オブジェクト指向に慣れると、
効率が上がるため現在では主流になっています。
デザインの世界では構造化プログラミング的手法のため、いきなり実践するということは無理とのことです。
そもそもデザイナーとエンジニアは責任が異なります。
デザイナーはインターフェースの価値を高めて、ユーザーに届け、エンジニアは効率を重視し、保守性を高めています。
そんな中での現実的な解決策は以下が挙げられていました。
弊社ではエンジニアがコンポーネント化する作業を行なっているので、より良いプロセスを考えていきたいです。
noteをNuxt.jsで再構築した話
SPAからNuxtへ移行したためになるお話でした。
noteは当初Angularで開発されていたようです。
この時の技術選定はAngularのみメジャーバージョンに上がっていることが大きかったようです。
しかし、SPAのみでは以下の課題がありました。
- 初期表示速度の遅延
- コーディング規約などの負債
フレームワークを変えてのフルスクラッチは難しいと思います。
noteさんはCEOが技術よりということで理解が得られていました。
Vueを選んだ理由は以下になるそうです。
- 学習コストの低さ
- ドキュメントの充実度(日本語が充実)
- コミュニティの活発度が高い
移行手順はページごとに社内リリースから初めて徐々に本番に移行していくやり方ですね。
- 既存UIを確実に移行
- ページごとにURLベースで振り分ける
- 社内リリース、Dog Fooding、一般公開の順番
設計方針ではVuex + Atomic Designを取り入れていました。
Vueを使って堅牢なシステムを作るときのベストなのかなと思います。
個人的にAtomic Designは分子と有機体の違いがわからなくなりますね…
Vuexの参照は有機体以上にしているそうです。
ルールを明文化して、コードレビューの観点にすることが大事なんだと思いました。
Atomic Designを用いることでコンポーネント数が肥大化し、チームが把握できない問題を解決するためにStory Bookを導入していました。
私自身設定周りで挫折した経験があるのですが、Nuxt2.0になったことで楽になっているそうです。
ストーリーをメンテナンスするのが大変なのと、通信周りのスタブ化が辛いため、対象を原子・分子に絞っていました。
インフラはNuxt on Lambdaの構成でした。
ハマりポイントは mya-akeさんのブログで解消できるそうです。
インフラ管理のコストやスケーリングを考えると良さそうですね。
以下の欠点を超えられれば使いたいです。
- NodeのバージョンがLambdaに依存する
- デプロイできる容量が決まっている
これまでの移行プロジェクトはnoteのなかでまとまっています。 note.mu
Nuxtの導入を考えているのでとても参考になりました。
1年間単体テストを書き続けた現場から送る Vue Component のテスト
ラストはコンポーネントテストのお話でした。
コンポーネントの何をどこまでテストするかはいつも悩むのでノウハウを聞けてよかったです。
単体テストを自動化するのは外部からみた振る舞いをテストしていました。
「外部からみた振る舞い」は何かしらのインプットに対してアウトプットが期待とおりかチェックすることです。
コンポーネントの場合のインプットは
- Lifecycle
- Props
- Store
- User Interface
これに対してアウトプットは
- HTML, CSS
- Event
- Storeのアクション
にまとめられていました。
Lifecycleでは必要なデータを取得するぐらいなので優先度は低いそうです。
他のテストに影響が出るので、場合によってはモックしてもよさそうですね。
PropsやStoreはスナップショットテストを用いていました。
コード修正の前後の差分を確認し、期待通りであればUpdateする機能です。
今まで使いこなせていない機能なので活用したいと思います。
文字列だけではなくCSSのテストをVisual Testにて行なっていました。
スナップショットテストの応用で画像を取得して差分を出力する仕組みです。
CIに組み込むことで、レビュー観点の一つになるため、レビュワーの負担が軽減するのが良さげです。
レビュワーがチェックアウトしてビルド・確認をする手間が省けるのは大きいですね。
ツールはStorybookとreg-suitoを用いていました。
User Interfaceのテストも同様にVisual Testを使っていました。
テスト範囲はクリックとinputイベントに絞っていて、難しいテストは実施しない選択を取っているそうです。
変更に強くするためのテストであることを忘れずに自分のプロジェクトに適応していきたいです。
まとめ
フロントエンドのカンファレンス自体初めてでしたがとても内容の濃い1日でした。
実践的なお話をたくさん聞けたので、どんどん投入していきたいと思います。
スタッフの方々本当にありがとうございました。
GraphQLを試してみた
こんにちは。ぬまたです。
約半年ぶりの投稿です。空けすぎました。
最近AppSyncを触る機会があったので、基礎となるGraphQLを学びたいと思います。
目次
GraphQLとは
GraphQLはフロントエンドがサーバのデータをいい感じに取得するためのクエリ言語です(怒られそう)。 ざっと調べる限りは以下のような特徴がありました。
- 読み取りと書き込みが分離している
- エンドポイントが一つ
- データ群の処理結果などをパラメータに追加できる
- Schemaや型を定義する
- 変数やコメントアウトを使うことができる
- キャッシュが比較的容易
実際に使ってみないとわからないので、GitHub APIで試してみます。 GitHubはAPIのバージョン4からGraphQLを導入しています。
参考: - GitHub API v4
検証環境
GitHubのデベロッパーツールを使用します。
GraphQLはエンドポイントをリソースごとに分けずにパラメータで指定します。
GitHubではリクエストを https://api.github.com/graphql
に向け、パラメータにクエリを入れています。
GitHub API を試してみる
ログインしているユーザーのメールアドレス取得
単一のリソースfields
から指定したキーの値を取得できます。
現在ログインしているユーザのメールアドレスを取得してみます。
GitHubは viewer
という 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やコミット数を指定しています。
commits
に totalCount
パラメータが入っているので、データを全て取得せずに総数を把握することができますね。
自分でSchemaを定義する場合はクライアントが欲しい値を自由に追加することができます。
特定条件のリポジトリ数の取得
GraphQLのPaginationを使ってクエリ結果の総数を取得できます。 Pythonで書かれているスター数1000以上のリポジトリが何個あるか調べてみます。
クエリ
query { search(query: "language: Python stars:>1000", type: REPOSITORY) { repositoryCount }
結果
{ "data": { "search": { "repositoryCount": 28 } } }
search
はGitHubで定義されているコネクションです。クエリに加えて読み取り開始位置 after
などを定義できるようになっています。
今回は query
と type
に検索したい条件をして、値に repositoryCount
を取ることでリポジトリ数を取得しています。
search
の引数を変えて、色々な検索をかけられるのはよいですね。
まとめ
今回は読み取りの Query
を中心にGraphQLを試してみました。
GraphQLのメリットはクライアントの負担が減ることなのかなという所感です。
Schemaの定義はクライアントの要求を取り入れつつ、データベース内のモデルの知識が求められそうですね。 その辺りも今後調べたいと思います。