Quantcast
Channel: Node.jsタグが付けられた新着記事 - Qiita
Viewing all 8825 articles
Browse latest View live

JavaScriptのトップレベルスコープは常にグローバルスコープではなかった

$
0
0

JSのトップレベルスコープは常にグローバルオブジェクトだと思っていた

タイトル通りですが、これに関しては疑っていなかったので個人的に割と衝撃でした。何か(?)に裏切られた気持ちです。。
もう少し詳しく書くと、JSを実行した時のグローバルスコープ(=グローバルオブジェクト)は、ブラウザ上ではwindowで、Node.js上ではglobalである。が、トップレベルスコープがそれぞれwindowglobalではなかった。
この記事では、トップレベルスコープについて検証していきます。

※トップレベルスコープ・・・関数とかの外側、rubyだとmain。

windowglobalについて検証

グローバル変数について確認

まず、ブラウザ上で実行した下記のコードを見ると、windowグローバルオブジェクト内のプロパティ(グローバル変数)になっていることがわかる。

// ブラウザ上で実行varfoo='global';bar='global';(function(){foobar='global';})()console.log(window.foo);// globalconsole.log(window.bar);// globalconsole.log(window.foobar);// global

次に、同様のコードをNode.js上で確認する。

// Node.js上で実行varfoo='global';bar='global';(function(){foobar='global';})()console.log(global.foo);// undefinedconsole.log(global.bar);// globalconsole.log(global.foobar);// global

Node.jsだとglobal.fooundefinedになっている。
varで変数を宣言するとそのスコープの変数になり、使用しないとグローバル変数になるのは基本だが念のため確認。

(function(){foo='global';varbar='global';})();console.log(foo);// globalconsole.log(bar);// ReferenceError: foobar is not defined

varでの宣言はローカルスコープになる。→ Node.jsのトップレベルドメインvar付き宣言した時はエラーになる。ということは、globalはもしかしたら単純なグローバルオブジェクトじゃなくて、何か間に挟まってる??

thisを確認する

thisは呼び出しもとで値が変わるが、トップレベルスコープで実行した場合自身の値が入る。

// ブラウザ上で実行console.log(this);// window
// Node.js上で実行console.log(this);//Objet{}

ブラウザ上でthisを確認した場合、window=グローバルオブジェクト=トップレベルスコープになっている。
一方、nodejs上で実行した場合Object{}になっている。
ということは、global=グローバルオブジェクト≠トップレベルスコープ!!

結局、Node.jsのトップレベルスコープって何なんだ?

Node.jsドキュメントに以下の記載があった。

global#
Added in: v0.1.27
The global namespace object.
In browsers, the top-level scope is the global scope. This means that within the browser var something will define a new global variable. In Node.js this is different. The top-level scope is not the global scope; var something inside a Node.js module will be local to that module.

ブラウザではトップレベルスコープグローバルスコープだが、Node.js上ではトップレベルスコープではないvarで宣言したものはmoduleローカル変数になると書かれている。

なので、Node.jsのトップレベルドメインでグローバル変数を定義する場合は注意が必要だ!!
と思ったが、そもそもグローバル変数使うのってあまりよくないですよね。JavaScriptについて一つ詳しくなったということで許してください。


node splice関数で文字列操作

$
0
0

まえがき

splice関数で配列操作してみた。

参考文献

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/join
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/split
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice

環境

$bash--version
GNU bash, バージョン 5.0.0(1)-release(x86_64-pc-linux-gnu)
Copyright (C) 2019 Free Software Foundation, Inc.
ライセンス GPLv3+: GNU GPL バージョン 3 またはそれ以降 <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
$node--version
v12.13.0
$node
Welcome to Node.js v12.13.0.
Type ".help"for more information.
> console.log("hello");
hello
undefined

スクリプト

3つ。

#1.left_stair.sh

先頭要素取得して末尾要素に追加していくパターン。

#2.right_stair.sh

末尾要素取得して先頭要素に追加していくパターン。

#3.square.sh

上記スクリプトをそれぞれ左と右、右と左に並びて、与える引数を正順と逆順の2つにしたパターン。

$cat left_stair.sh
#!/bin/bash

usage(){cat<<EOF
Usage:
\$$0\$(echo -e '\U1f4a'{0..9})
EOF
}

chk_args(){declare-aargv=()local argv=($@)local argc="${#argv[@]}"["${argc}"-le 1 ]&& usage &&exit 1
}

circle(){echo"$@" | xargs -I@ node -e'
  function mock4(init_args){
    let ary=init_args.split(" ");
    for(let i=1;i<=ary.length;i++){
      let ele=ary.splice(0,1);
      ary.splice(init_args.split(" ").length,0,ele);
      console.log(ary.join(""));
    }
  }
  mock4("@");
  '}

main(){local init_args="$(cat -)";
  chk_args "${init_args}"while read ln;do circle "${ln}";done<<<"${init_args}"}[-p /dev/stdin ]&&cat - | main
[-p /dev/stdin ]||echo-ne"$@" | main

先頭要素が次の対象行の末尾要素に追加されていく様子がわかる。

$echo-e'\U1f4a'{0..9} | ./left_stair.sh 
💡💢💣💤💥💦💧💨💩💠
💢💣💤💥💦💧💨💩💠💡
💣💤💥💦💧💨💩💠💡💢
💤💥💦💧💨💩💠💡💢💣
💥💦💧💨💩💠💡💢💣💤
💦💧💨💩💠💡💢💣💤💥
💧💨💩💠💡💢💣💤💥💦
💨💩💠💡💢💣💤💥💦💧
💩💠💡💢💣💤💥💦💧💨
💠💡💢💣💤💥💦💧💨💩
$cat right_stair.sh
#!/bin/bash

usage(){cat<<EOF
Usage:
\$$0\$(echo -e '\U1f4a'{0..9})
EOF
}

chk_args(){declare-aargv=()local argv=($@)local argc="${#argv[@]}"["${argc}"-le 1 ]&& usage &&exit 1
}

circle(){echo"$@" | xargs -I@ node -e'
  function mock5(init_args){
    let ary=init_args.split(" ");
    for(let i=1;i<=ary.length;i++){
      let ele=ary.splice(-1,1);
      ary.splice(0,0,ele);
      console.log(ary.join(""));
    }
  }
  mock5("@");
  '}

main(){local init_args="$(cat -)";
  chk_args "${init_args}"while read ln;do circle "${ln}";done<<<"${init_args}"}[-p /dev/stdin ]&&cat - | main
[-p /dev/stdin ]||echo-ne"$@" | main

末尾要素が先頭要素に追加されていく様子がわかる。

$echo-e'\U1f4a'{0..9} | ./right_stair.sh 
💩💠💡💢💣💤💥💦💧💨
💨💩💠💡💢💣💤💥💦💧
💧💨💩💠💡💢💣💤💥💦
💦💧💨💩💠💡💢💣💤💥
💥💦💧💨💩💠💡💢💣💤
💤💥💦💧💨💩💠💡💢💣
💣💤💥💦💧💨💩💠💡💢
💢💣💤💥💦💧💨💩💠💡
💡💢💣💤💥💦💧💨💩💠
💠💡💢💣💤💥💦💧💨💩
$cat ./square.sh
#!/bin/bash

usage(){cat<<EOF
Usage:
\$$0\$(echo -e '\U1f4a'{0..9})
EOF
}

chk_args(){declare-aargv=()local argv=($@)local argc="${#argv[@]}"["${argc}"-le 1 ]&& usage &&exit 1
}

circle(){paste-d' '<(./left_stair.sh $(echo-e"$@"))<(./right_stair.sh $(echo-e"$@"|rev))paste-d' '<(./right_stair.sh $(echo-e"$@"))<(./left_stair.sh $(echo-e"$@"|rev))}

main(){local init_args="$(cat -)";
  chk_args "${init_args}"while read ln;do circle "${ln}";done<<<"${init_args}"}[-p /dev/stdin ]&&cat - | main
[-p /dev/stdin ]||echo-ne"$@" | main

きれいだ。

$echo-e'\U1f4a'{0..9} | ./square.sh 
💡💢💣💤💥💦💧💨💩💠 💠💩💨💧💦💥💤💣💢💡
💢💣💤💥💦💧💨💩💠💡 💡💠💩💨💧💦💥💤💣💢
💣💤💥💦💧💨💩💠💡💢 💢💡💠💩💨💧💦💥💤💣
💤💥💦💧💨💩💠💡💢💣 💣💢💡💠💩💨💧💦💥💤
💥💦💧💨💩💠💡💢💣💤 💤💣💢💡💠💩💨💧💦💥
💦💧💨💩💠💡💢💣💤💥 💥💤💣💢💡💠💩💨💧💦
💧💨💩💠💡💢💣💤💥💦 💦💥💤💣💢💡💠💩💨💧
💨💩💠💡💢💣💤💥💦💧 💧💦💥💤💣💢💡💠💩💨
💩💠💡💢💣💤💥💦💧💨 💨💧💦💥💤💣💢💡💠💩
💠💡💢💣💤💥💦💧💨💩 💩💨💧💦💥💤💣💢💡💠
💩💠💡💢💣💤💥💦💧💨 💨💧💦💥💤💣💢💡💠💩
💨💩💠💡💢💣💤💥💦💧 💧💦💥💤💣💢💡💠💩💨
💧💨💩💠💡💢💣💤💥💦 💦💥💤💣💢💡💠💩💨💧
💦💧💨💩💠💡💢💣💤💥 💥💤💣💢💡💠💩💨💧💦
💥💦💧💨💩💠💡💢💣💤 💤💣💢💡💠💩💨💧💦💥
💤💥💦💧💨💩💠💡💢💣 💣💢💡💠💩💨💧💦💥💤
💣💤💥💦💧💨💩💠💡💢 💢💡💠💩💨💧💦💥💤💣
💢💣💤💥💦💧💨💩💠💡 💡💠💩💨💧💦💥💤💣💢
💡💢💣💤💥💦💧💨💩💠 💠💩💨💧💦💥💤💣💢💡
💠💡💢💣💤💥💦💧💨💩 💩💨💧💦💥💤💣💢💡💠

あとがき

こういうの作ろうとおもうといろいろ調べて好奇心や探究心が湧いてくるから、楽しい。

splice関数便利。

以上、ありがとうございました。

Node.js 簡単な応答バッチ処理

$
0
0

はじめに

Node.jsでyまたはnの応答に従い、バッチ処理を実行するJavaSciptのサンプルを作成しました。
この辺、Promiseとasync/awaitなどを利用して対話的に同期処理を実行すると分けがわからなくところです。
初学者向けと自らの備忘録で掲載します。

前提

バッチ処理リスト

  • batchlist.json
[{"msg":"★ ★ リストを確認します。(ls) 実行 = y, 中止 = n ★ ★","con":"y","can":"n","cmd":"ls"},{"msg":"★ ★ パスを確認します。(pwd) 実行 = y, 中止 = n ★ ★","con":"y","can":"n","cmd":"pwd"}]

JSON項目説明

項目説明
msg応答文、実行と中止を定義
con実行(continu)の文字列を定義
can中止(cancel)の文字列を定義
cmd実行するコマンド(command)を定義

バッチ処理スクリプト

  • Dobatch.js
constexec=require('child_process').execFileSync;constfs=require('fs');constjson=JSON.parse(fs.readFileSync('./batchlist.json','utf8'));// コンソール標準入出力constrl=require("readline").createInterface(process.stdin,process.stdout);constgets=()=>newPromise(res=>rl.once("line",res));/**
 * コマンドラッパー(同期処理)
 * @param {String } cmd - コマンド
 */functionsh(cmd){try{console.log(exec(cmd,{shell:true}).toString());}catch(error){console.log(error.stderr.toString());}}// バッ処理実行(asyncfunction(){for(vari=0;json.length>i;i++){console.log(json[i].msg);str=awaitgets();if(str==json[i].con){console.log(json[i].cmd);sh(json[i].cmd);}elseif(str==json[i].can){process.exit();}else{console.log(str+"は入力不正です。")i--;}}process.exit();})();

説明

  • コマンド実行はchild_processのexecFileSyncを利用して同期処理
  • JSONの読み出しもfsのreadFileSyncを利用して同期処理
  • 残るは、コンソールからの入出力応答をPromoiseとasync/awaitで同期処理
    gets関数で定義

実行

Windows PowerShell
Copyright (C) 2009 Microsoft Corporation. All rights reserved.

PS C:\Users\~>bash --version ...①
GNU bash, version 4.4.12(2)-release(x86_64-pc-msys)
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
PS C:\Users\~>bash ...②
USER MINGW64 ~/
$ node Dobatch.js ...③
★ ★ リストを確認します。(ls)実行 = y, 中止 = n ★ ★
y
ls
batchlist.json
Dobatch.js

★ ★ パスを確認します。(pwd)実行 = y, 中止 = n ★ ★
y
pwd
/c/Users/~/


USER MINGW64 ~/
$

説明

① bashのバージョン確認
② PowerShellからbashへ切替え
③ Dobatch.jsの実行

※ 実行で表示されるパスは出力を編集して表示しています。

まとめ

Node.jsのコンソールからの標準入力がめんどくさい、VBのMsgBox的なコーディングをしようとして悪戦苦闘した記憶がありました。ファイルの読み込みとコマンド実行を同期処理にすれば、以外と簡単にできると思います。たぶレスポンスはいまいちだと思います。
色々なシステム導入ツールがあると思いますが、JSONファイルのバッチ定義で簡易システム導入ツールとしてご利用してみてください。ただし、参考までに、例によって一切のご利用はあくまでも自己責任でお願いします。

[nodemon] app crashed - waiting for file changes before starting... というエラーに遭遇した時の対処法

$
0
0

[nodemon] app crashed - waiting for file changes before starting... というエラーに遭遇した時の対処法

Githubのissuesをのぞいてみる

見たことないくらいのコメント数が付いていました。
https://github.com/linnovate/mean/issues/1066

いろいろな意見があるが問題としては

  • nodemonがない(インストールしていない)
  • インストールしたnode_modulesに問題がある

上記を踏まえ行った対応

  1. npm install -g nodemonでnodemonをインストール
  2. node_modulesを一旦削除してnpm iでインストールしなおす

こちらを実行し再度ビルドしたところ正常に動きました。

npmコマンドで、permission deniedが発生する

$
0
0

状況

babelをインストールしようとすると、permission deniedが発生し、インストールに失敗しました。

$ npm i @babel/core @babel/node @babel/preset-env --save-dev
npm ERR! code EACCES
npm ERR! syscall rename
npm ERR! path /home/user/repos/play_express/node_modules/@babel/core
npm ERR! dest /home/user/repos/play_express/node_modules/@babel/.core.DELETE
npm ERR! errno -13
npm ERR! Error: EACCES: permission denied, rename '/home/user/repos/play_express/node_modules/@babel/core' -> '/home/user/repos/play_express/node_modules/@babel/.core.DELETE'
npm ERR!  {[Error: EACCES: permission denied, rename '/home/user/repos/play_express/node_modules/@babel/core' -> '/home/user/repos/play_express/node_modules/@babel/.core.DELETE']
npm ERR!   cause:
npm ERR!    { Error: EACCES: permission denied, rename '/home/user/repos/play_express/node_modules/@babel/core' -> '/home/user/repos/play_express/node_modules/@babel/.core.DELETE'
npm ERR!      errno: -13,
npm ERR!      code: 'EACCES',
npm ERR!      syscall: 'rename',
npm ERR!      path: '/home/user/repos/play_express/node_modules/@babel/core',
npm ERR!      dest:
npm ERR!       '/home/user/repos/play_express/node_modules/@babel/.core.DELETE'},
npm ERR!   stack:
npm ERR!    'Error: EACCES: permission denied, rename \'/home/user/repos/play_express/node_modules/@babel/core\' -> \'/home/user/repos/play_express/node_modules/@babel/.core.DELETE\'',
npm ERR!   errno: -13,
npm ERR!   code: 'EACCES',
npm ERR!   syscall: 'rename',
npm ERR!   path: '/home/user/repos/play_express/node_modules/@babel/core',
npm ERR!   dest:
npm ERR!    '/home/user/repos/play_express/node_modules/@babel/.core.DELETE',
npm ERR!   parent: 'play_express' }
npm ERR!
npm ERR! The operation was rejected by your operating system.
npm ERR! It is likely you do not have the permissions to access this file as the current user
npm ERR!
npm ERR! If you believe this might be a permissions issue, please double-check the
npm ERR! permissions of the file and its containing directories, or try running
npm ERR! the command again as root/Administrator.

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/user/.npm/_logs/2019-11-12T02_06_04_443Z-debug.log

対応

npmをグルーバルで再インストールしました。

$ rm-rf ~/.npm-global
$ npm config set prefix '~/.npm-global'$ npm i -g npm

ちなみに、npm config get prefixで、現在のprefixを確認できます。

結果

babelをインストールできました。

超初心者が子供の服装について目安を教えてくれるLine Botを作ってみた

$
0
0

はじめに

プログラミングを勉強し始めてから2か月経過しました。
休み休みなのでちょっとずつの進歩ですが、
自分の成長のために新たなLINE BOTにトライしてみたくなりました。
季節の変わりめ、いつも困るのが子供の服装です。
子供は大人より体温が高いらしく、同じ感覚で服を用意すると
すぐ「あつ~い」と脱がれてしまいます・・・。
挙句の果てに上着を忘れたり、やたら荷物が増えたり。
ということで気温から服装の目安を教えてくれるLine Botを作ってみました。

作った目的

前回までのLine Botは基本的にオウム返しのみの設定でした。
今回は少しステップアップするためWEBのAPIにつなげることに
挑戦してみました。

仕様

1⃣明日の気温を教えてくれる
2⃣気温を入れると、子供の服の目安を教えてくれる。

環境

基本的には「LINE BOT作ってNowで動かした時のメモ」を参考にさせていただきました。
・Node.js v10.16.3
・Windows 10 pro
・Visual Studio Code v1.39.1
 服装の基準は下記を参照しています。
https://woman.excite.co.jp/article/child/rid_ItMama_37721/

コード

'use strict';constexpress=require('express');constline=require('@line/bot-sdk');constaxios=require('axios');constPORT=process.env.PORT||3000;constconfig={channelAccessToken:'LINEのアクセストークン',channelSecret:'LINEのシークレットトークン'};constapp=express();app.get('/',(req,res)=>res.send('Hello LINE BOT!'));app.post('/webhook',line.middleware(config),(req,res)=>{console.log(req.body.events);Promise.all(req.body.events.map(handleEvent)).then((result)=>res.json(result));});constclient=newline.Client(config);functionhandleEvent(event){constmessage=event.message.text;letreplyMessage=event.message.text;//event、message以外はnull(空)で返すif(event.type!=='message'||event.message.type!=='text'){returnPromise.resolve(null);}if(message.indexOf('子供の服')>-1){replyMessage='明日の気温入れて';}if(message.indexOf('10')>-1){replyMessage='本格的冬服';}if(message.indexOf('11')>-1){replyMessage='薄手の冬服 長袖下着+ロンTシャツ+セーター';}if(message.indexOf('12')>-1){replyMessage='薄手の冬服 長袖下着+ロンTシャツ+セーター';}if(message.indexOf('13')>-1){replyMessage='本格的秋服 半袖下着+ロンTシャツ+トレーナー';}if(message.indexOf('14')>-1){replyMessage='本格的秋服 半袖下着+ロンTシャツ+トレーナー';}if(message.indexOf('15')>-1){replyMessages='薄手の秋服 半袖下着+ロンTシャツ+カーディガン';}if(message.indexOf('16')>-1){replyMessage='薄手の秋服 半袖下着+ロンTシャツ+カーディガン';}if(message.indexOf('17')>-1){replyMessage='薄手の秋服 半袖下着+ロンTシャツ+カーディガン';}if(message.indexOf('18')>-1){replyMessage='薄手の秋服 半袖下着+ロンTシャツ+カーディガン';}if(message.indexOf('19')>-1){replyMessage='薄手の秋服 半袖下着+ロンTシャツ+カーディガン';}if(message.indexOf('20')>-1){replyMessage='夏秋服 下着タンクトップ+ロンTシャツ';}if(message.indexOf('21')>-1){replyMessage='夏秋服 下着タンクトップ+ロンTシャツ';}if(message.indexOf('22')>-1){replyMessage='夏秋服 下着タンクトップ+ロンTシャツ';}if(message.indexOf('23')>-1){replyMessage='夏秋服 下着タンクトップ+ロンTシャツ';}if(message.indexOf('明日の気温')>-1){getTommorowTemperature(event.source.userId);//スクレイピング処理が終わったらプッシュメッセージ}returnclient.replyMessage(event.replyToken,{type:'text',text:replyMessage});}constgetTommorowTemperature=async(userId)=>{constres=awaitaxios.get('http://weather.livedoor.com/forecast/webservice/json/v1?city=130010');constitem=res.data;if(item.forecasts[1].temperature.max==null){client.pushMessage(userId,{type:'text',text:"最高気温のデータが無いみたいです",});}else{client.pushMessage(userId,{type:'text',text:"最高"+item.forecasts[1].temperature.max.celsius+"",});}//データが取れなかった時の返答if(item.forecasts[1].temperature.min==null){client.pushMessage(userId,{type:'text',text:"最低気温のデータが無いみたいです",});}else{client.pushMessage(userId,{type:'text',text:"最低"+item.forecasts[1].temperature.min.celsius+"",});}}}//一番上段のexpressのコードとのセット。ポートとつなぎます。app.listen(PORT);//つながれたらTerminalに記載します。console.log(`Server running at ${PORT}`);

動作確認

Screenshot_20191112.png

課題

正直言うと、本当は2段階ではなく1段階で明日の子供の服についてコメントしてくれるボットが良かったのです。しかし、APIから取ってきた値でLINE BOTが応答できるような複雑でかっこいいコードをまだまだ書けません。
またAPIbankなるものがあり、素敵なAPIが多く掲載されていましたが、パラメーターの設定をどうするかというのが難題で、さっぱりどうして良いか迷子でした。先にQiitaに記事を掲載されていた方にめちゃ感謝。
今後、この課題をクリアしたいと痛感しました。

感想

1、2日使ったみた感想はそこそこ当たっているかなという感じです。
寒い日には私から言うよりもLINE BOTからサジェストされた方が子供も素直に聞きそうでした。
UX的にはまだまだなので、あきらめずに改良していきたいと思います。

QualityForwardのAPIを使って更新履歴を取る

$
0
0

QualityForwardはテスト管理をクラウドで提供しています。そして、多くのクラウドサービスと同様にAPIを提供しています。現在、下記のデータを取得または新規作成、更新、削除できます。

  • プロジェクト
  • テストケース
  • テストサイクル
  • テストフェーズ
  • テスト結果
  • テストスイート
  • テストスイートバージョン
  • ユーザ

現在、非公式ながらNode.js用ライブラリ qualityforward-nodeを作っていますが、その使い方の一例として、テストスイートの更新履歴を管理してみます。

コード例

コードは下記のようになります。テストスイートを取得し、それをCSV化しています。

import*asdotenvfrom'dotenv';const{parse}=require('json2csv');dotenv.config();import{QualityForward}from'qualityforward-node';(async()=>{constclient=newQualityForward(process.env.API_KEY);consttestSuites=awaitclient.getTestSuites();constcsv=parse(testSuites.map(s=>s.toJSON()),{});console.log(csv);})();

後はこれを定期的に実行するだけです。出力先は適当なGitリポジトリとしています。

$ npx ts-node test.ts > /home/user/repo/res.csv

cronなどで定期実行していれば、自動的に更新されるでしょう。データ取得時には自動でコミットも追加しておく方が良いでしょう。

差分を見る

CSVの差分を見る場合は、次のように行うと分かりやすいです。

$ git diff --word-diff-regex="[^,]+" res.csv 

そうすると、異なるセルだけが色分けされて表示されます(via csvを見やすくgit diff (word-diff-regexオプション) - Qiita)。もちろんCSV用の差分ビューワーを使うのも良いでしょう。

Screenshot_ 2019-09-13 16.24.59.png

まとめ

データが誰かによって変更された場合にも、その差分を取っておけば安心です。差分によって設定を戻すこともできますし、いつから変わったのか捕捉も簡単です。APIを使って、適切な運用が行えるようになるでしょう。

QualityForward

GraphQLについてのハンズオン

$
0
0

概要

GraphQLについて、NodeJSのウエブフレームワークmajidaiを使ってハンズオンを実施したいと思います。GraphQLとは何かを理解する前に、クライアントとサーバーの間でデータのやり取りするにあたってどの様な方法あるか考えてみましょう。僕が認識している中では、以下の3つの方法が主流ではないかと思います。

① 従来のやり方

クライアントからリクエスト受け付けるたびにDBサーバーから情報を取得し、色々と整形しレスポンス
traditional

② REST API

用途毎にエンドポイントを用意する
rest

③ GraphQL

エンドポイントが一つのみ
GraphQL

GraphQLの使い所

個人的にはRESTで十分だと思っておりますが、以下の場面ではネックになります。

例えば、作家一覧を取得するエンドポイント「/authors」と本の一覧を取得するエンドポイント「/books/author_idXX」があります。
以下のサンプルのように、RESTを使って作家さん毎の本の一覧を取得したい

作家:{Aさん:本:{本1本2本3}}Bさん:本:{本1本2本3}}}

上記のようなケースでは、恐らくJavascriptで以下のような処理を記述するでしょう。

// 作家一覧を取得するauthors=getData("/authors");// 作家毎の処理for(varauthorinauthors){// 作家毎サーバーと通信を行うbooks=getData("/books/"+author["id"]);// TODO....}

「getData」とはサーバと通信し、データを取得する関数別当あると想定してください。

問題点

こちらのJavascriptのコードを見てお気付きだと思いますが、作家の数通りサーバーと通信を行う必要があります。作家の数があればあるほど遅くなります!

解決策

ここで登場するのがGraphQLです。GraphQLとはFacebook社が開発したOSSです。
公式サイトでは以下のように定義されています。

GraphQLとはAPIのためのクエリ言語とサーバーサイドでクエリを実行するためのランタイムです

色々難しく書いてありますが、僕はRESTの新しいバージョンであると勝手に認識しております。
言葉だけでは理解しにくいのハンズオンをしてみましょう。

環境構築

NodeJSを使います。もしNodeJSが入っていないのであれば、こちらよりダウンロードしてください。

ソースコードの準備

Githubにソースコードを置いておりますのでダウンロードするか、下記のコマンドでレポジトリをクーロンしてください。

git clone https://github.com/dakc/graphql.git
必要なパッケージのインストール

graphqlフォルダー内から以下のコマンドを実行し、必要なパッケージをインストールします。

npm install

以下の3つのパッケージがインストールされます。
1. graphql
2. majidai
3. express-graphql

実行

以下のコマンドを実行し、サーバーを起動します。

npm run start

ブラウザを開いて http://127.0.0.1:8000/graphqlにアクセスします。
以下のようなページが表示されたらOKです。
graphql

作家の一覧取得

左側のテキストボックスに以下のクエリを入力し、左上の実行ボタンを押下してください。右側に何らかの結果が返ってきます。このクエリは、作家のIDと名前を取得するクエリです。

{authors{idname}}
本の一覧取得

そして、上と同様に左側のテキストボックスに本のID、名前、作者のIDを取得するクエリを実行してください。

{books{idnameauthorId}}
作家と本をまとめて取得

上と同様に以下のクエリを実行してください。

{authors{idnamebooks{idname}}}

何か気付きましたか。
最後のクエリを見てください。上のRESTの場合はループでサーバーとやり取りしていたのですが、GraphQLを使えば1回の通信で習得できます。また、クライアント側で必要なフィールドを指定する事ができます。

最後に

GraphQLを触ってみて如何でしたか。何かの参考になれば幸いです。
今回はGraphQLの中のクエリのみを紹介しましたが、それ以外にMutationSubscriptionがあります。


IoTの力を使って、スポーツ後の靴を早く乾かす

$
0
0

スポーツの秋

最近涼しくなってきて、ランニングをしている人を良く見かけるようになりました。
皆さんは運動した後のシューズケアしていますか?
IoTの力を借りて、シューズケアをしましょう。

できたもの

用意したもの

締めて¥8,570でした。

システム構成

Obnizと各種センサーを使って以下のような構成で作りました。
グループ 9.png

ピン接続

ピンは以下の通り接続します。

  • スピーカー
    • signal: 2, gnd: 0

10911238722523.jpg

  • フルカラーLED
    • gnd: 6, vcc: 5, din: 4

10911238731664.jpg

  • DCファン
    • vcc: 8, gnd: 7

10911238781825.jpg

10911238764264.jpg

  • 湿度センサー
    • vcc: 11, gnd: 10, din: vout: 9

10911238896730.jpg

10911238890552.jpg

全部つなぐとこんな感じになりました。
10911238668239.jpg

ソースコード

Node.jsで書きました。
ソースコード全量はGitHubからご覧いただけます。(https://github.com/mono0423/pos-shoes-care)

constObniz=require('obniz');constobniz=newObniz('OBNIZ_ID_HERE');obniz.onconnect=asyncfunction(){constleds=obniz.wired('WS2811',{gnd:6,vcc:5,din:4});// フルカラーLEDconstspeaker=obniz.wired('Speaker',{signal:2,gnd:0});// スピーカーobniz.io11.output(true);// 湿度センサーのvccobniz.io10.output(false);// 湿度センサーのgndobniz.io7.output(false);// DCファンのgndwhile(true){letave=0;// 湿度を1秒間におよそ100回測定して、平均を取るfor(leti=0;i<100;i++){constvoltage=awaitobniz.ad9.getWait();// 湿度センサーのvoutの値を読み取るconsthumid=voltage*100;// 電圧->湿度の変換(%)ave=ave+humid;awaitobniz.wait(10);}ave=ave/100;// 100回の平均を取るconsthumid=ave>100?100:ave;// 湿度100%以上になる場合、100%にするconsole.log('湿度: '+humid+' %');letrgb=[0,0,255];if(humid>90){rgb=[255,0,0];// LEDを赤色に変更obniz.io8.output(true);// DCファンを回すspeaker.play(1000);// スピーカーを鳴らす}elseif(humid>60){rgb=[255,255,0];// LEDを黄色に変更obniz.io8.output(true);// DCファンを回す speaker.play(800);// スピーカーを鳴らす}else{rgb=[0,0,255];// LEDを青色に変更obniz.io8.output(false);// DCファンを止めるspeaker.play(0);// スピーカーを止める}leds.rgbs([rgb]);// LEDの色を変える}};

いざ実践

上記で作成したものをシューキーパーに貼り、実際にランニングシューズの中に入れてみる。
10911509568750.jpg

わかりにくいですが、ランニング後の僕の靴は湿度90%超のようでした。
これで臭い靴とはおさらば!

振り返り

  • シューキーパーに全部の装置を収める・貼り付けるのがとても難しかった。安定感を求めるなら、ちょうど良い容器を見つけるか3Dプリントする必要があると感じた。
  • 環境を測定→制御→測定→…のループを実装出来てよかった。
  • たまに電圧をかけてもDCファンが回らないことがある。ハードのトラブルシューティングの知識を身に着けたい。
  • 小型化とか電池駆動できるようにしたい。

Node.jsでFirestoreにDate型を渡すとエラーになる

$
0
0

FirebaseのクラウドデータベースであるCloud Firestoreは、Timestamp形式での日付・時刻の保存に対応しており、その日付順にソートすることができます。

constdb=firebase.firestore()constdata=awaitdb.collection('messages').orderBy('date','asc').limit(limit).get().then(querySnapshot=>{returnquerySnapshot.docs.map(doc=>doc.data())})

また、startAtendBeforeといった特定の期間で区切ることもできます。

constdb=firebase.firestore()conststartDate=newDate(`2019/01/01 00:00:00`)constendDate=newDate(`2020/01/01 00:00:00`)constdata=awaitdb.collection('messages').orderBy('date','asc').startAt(startDate).endBefore(endDate).limit(limit).get().then(querySnapshot=>{returnquerySnapshot.docs.map(doc=>doc.data())})

ところが、上記のコードをNode.jsで動かすとエラーになります。クライアントサイドで動かす分には問題ありません。

私はNuxt.jsを使っていて、mountedで呼び出す時は問題ないのにasyncDatafetchにすると突然エラーになったのでかなり困りました。

image.png

このcustom Date objectが何なのかわからなかったのですが、とりあえずstartDateを出力してみると、

chrome
{
  startDate: Mon Apr 01 2019 00:00:00 GMT+0900 (日本標準時)
}
node.js
{
  startDate: 2019-03-31T15:00:00.000Z
}

表記が違う……?

Firestoreは前者の形なら正しく受け取ってくれますが、後者だとエラーになるようです。

唯一の救いは、両方に対応したTimestamp変換ツールがFirebaseのパッケージに含まれていること。

conststartDate=firebase.firestore.Timestamp.fromDate(newDate(`2019/01/01 00:00:00`))constendDate=firebase.firestore.Timestamp.fromDate(newDate(`2020/01/01 00:00:00`))

これなら動きます。

上記のコンソール表示では3/31 15:00になっていましたが、これはタイムゾーンの変換時の違いなので、Timestampに変換するとどちらも全く同じ時刻として扱われ、Firestoreから取ってくるだけなら何の問題もありません。

Node.jsの時刻の形式について直接問題視するような記事をあまり見かけなかったので、ひょっとすると片方でのみエラーを返すFirestoreの実装がおかしいのかもしれませが、解決したので深く考えるのは止めました。

AWS Lambdaで毎日料金を取得する

$
0
0

AWS Lambdaで毎日料金を取得する

TL;DR

AWSのダッシュボード見たほうが早い

はじめに

監視しようがしてなかろうが、どのみちAWSのコンソールを常に開いている人にとっては、AWSのダッシュボード見たほうが正確で早いです。

しかし、そこまでAWS漬けでもない人にとってはいちいちAWSのコンソールを開き、料金やインスタンスの稼働状況を確認するのは面倒です。そこでSlackに稼働状況を監視して通知するAWS Lambdaの関数を定義して、楽して監視するようにします。

やりたいLambda関数の疑似コード

  • EC2の全インスタンスを取得する
    • タグ情報を取得し、稼働状態を列挙する
  • CostExplorerから料金情報を取得する
    • 現時点の料金を出力
    • 現時点の料金を日数で割り、月の日数倍して予想価格を出す

料金は直近の1日分料金を取得し、その金額を月の残りの日数分足したほうが正確ですが、これはこれで1日だけ異様に活発だった時等に瞬間風速に引きずられてそこまで正確じゃないので、今回は今までの平均が一番平均に近いという事にします。

AWS Lambdaでの関数の作成

Lambdaの関数作成(Hello worldする物)は他にいくらでも情報があるので、そちらを参照ください。
権限周りの問題がでなければ、何か見るよりAWSの画面みたほうがわかりやすいです。

関数の作成のため、ユーザーにはiam:PassRoleの権限が必要でした。
関数自体のロールの権限は、以下を付与しました。

実際にコードに書いたメソッドと同じ名前の定義があるので、拡張する場合は適宜追加が必要です。

"ec2:DescribeInstances",
"ce:GetCostAndUsage"

nodejsでのAWS sdkの使い方

node.jsからAWS SDKを使う際は、以下にAPIの一覧があります。

ほとんどの場合aws cliの記法が正しく、ドキュメントが豊富です。
node.jsであっても同じように書けるので、aws cliの操作を参考にnodeに移行したほうが作りやすいと思います。

APIドキュメントは何がどう返ってくるのか説明があまりなく、これだけみて作るはかなり難しいと思います。

例えば、ローカル環境等で開発する場合~/.aws/credentialsを作成したり、AWS.credentialsでトークンを設定します。
AWS Lambdaで実行する場合は、AWS.credentialsで権限上書きをしようとしても、関数に割り当てたロールの権限で自動的に初期化され書き換えが行えず、初期化処理も勝手に終わるので、適当に書いてもなんか動きます。

実コード

実物

https://github.com/ksuzukiTVDn1/awsPriceChecker

Lambdaへの展開

展開にあたり、aws-sdkはLambda上に既に存在しているため、それ以外のnode_modulesをZIP化してアップロードする必要があります。
アップロードは「コードエントリタイプ」の「.zipファイルをアップロード」から行うことが出来ます。
nodejsにとって必要なlockファイル等は不要です。また、package.jsonをLambda上でいい感じに「npm install」的な事をするという事は出来ません。

エントリポイント

node.jsの場合、LambdaのエントリーポイントはLambda設定パネルの「ハンドラ」の定義に従って動作するため、デフォルトの「index.handler」の場合、以下が動作します。

  • index.js
  • export.handler の関数

そのため、今回のコードの場合以下が動作します。

# index.js
exports.handler = (event, context, callback) => {
...
  result = ec2_status();
...
  result = costExplorer_status();
...
}

個々の関数の処理

hanlder関数から呼び出した関数は以下のaws cliの操作を受け持ちます。

  • ec2_status
    • aws ec2 describeInstances
  • costExplorer_status
    • aws costexplorer getCostAndUsage

処理そのものは返ってくるJSONを掘ってるだけなのでそんなに見どころはないです。
内容を元にslackのpayloadを作成し、slackのhookにPOSTしています。

出力例

結果的に以下のような出力が自動的に行われます。
001.png

Lambdaの駆動トリガー

関数のトリガーは、Lambdaの「トリガーを追加」から行います。
スケジュール実行の場合は「CloudWatch Events」から定義することができ、cron記法で記載することで定時実行されます。

まとめ

30日動かしても1円行かないので助かります。

【初心者向け】kubenetesのSpring bootとNodejsのアプリケーションをOpenShiftへデプロイしてみた

$
0
0

さまり

 OpenShiftにSpring-bootとNodejsのアプリケーションのデプロイを試してみましたので、そのメモといくつか気づいたことを残して見ました。結論からいうとOpenShiftの特有の手法は今回特に利用していないため、大きい違いは発見していません。KubernetesよりOpenShiftに移行することによるデプロイ手順の参考になればと思います。

環境

 本手順で利用したOpenShiftのバージョンは4.1です(minishiftではありません)。ローカルPCはMacbookを利用していました。
本手順にて利用したコードは以下のGithubから確認してください。
https://github.com/Zhuangbili/openshift_spring_nodejs

Spring boot アプリケーションのデプロイ

準備

 OpenShiftでマイクロサービスの開発を行うためのベースコードを作成することがメインの目的でしたので、まず簡単なコードを書いて見ました。参考:https://github.com/Zhuangbili/openshift_spring_nodejs/tree/master/UserInfo
 アプリケーションを開発する側としてコンテナーであるから、Kubernetesであるからを意識する必要は特にありません。

ビルド及びデプロイ

 ビルドからデプロイの方式はいくつがありますが、以下のようになります。

1.Springプロジェクトのビルド→Dockerビルド→Dockerイメージをリポジトリへの登録→OpenShiftにデプロイする。

 下記のコマンドを利用する。(GitOpsを利用する場合OCコマンドの部分は省略する。)
 (利用するYamlファイルは https://github.com/Zhuangbili/openshift_spring_nodejs/tree/master/yamlを参照)

$mvn clean package
//Dockerfileはgitリポジトリを参照
$docker build -t<repository>/<imagename>:<tag> .$docker push <repository>/<imagename>:<tag>
$oc login
...
$oc project <Project Name> // OpenShiftのプロジェクトはKubernetesのNamespaceとほぼ同じ
$oc create -f<Application Name>-deployment.yaml
$oc create -f<Application Name>-service.yaml

 アプリケーションをそのまま公開する場合Routeを作成する(RouteはKubernetesにはないもの)。
スクリーンショット 2019-10-23 14.51.05.png

$oc expose <Application Service Name>
OR
$oc create -f<Application Name>-route.yaml

 Istioを利用する場合にGateWay、VirtualServiceを作成し、サービスと連携する。
スクリーンショット 2019-10-23 14.54.15.png

$oc create -f gateway.yaml
$oc create -f<Virtual Service Name>-virtualservice.yaml
2.Mavenを利用する場合にfabric8を利用し、そのままOpenShiftへデプロイする。

 fabric8を利用する場合、pom.xmlファイルの編集、application-openshift.propertiesファイルの作成、マニフェストファイルの指定位置への配置が前提になります。

<?xml version="1.0" encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion>
...
    <build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin><plugin><groupId>io.fabric8</groupId><artifactId>fabric8-maven-plugin</artifactId><version>4.3.0</version></plugin></plugins></build>
...

<!-- この例ではSpring bootアプリとMySQLのやり取りのサンプルであるので、以下の部分も必要となる。--><profile><id>openshift</id><dependencies><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.6</version><scope>runtime</scope></dependency></dependencies><activation/><build><plugins><plugin><groupId>io.fabric8</groupId><artifactId>fabric8-maven-plugin</artifactId><version>${fabric8-maven-plugin.version}</version><executions><execution><id>fmp</id><phase>package</phase><goals><goal>resource</goal><goal>build</goal></goals></execution></executions></plugin></plugins></build></profile>
...

 application-openshift.propertiesファイルのDatasouceに以下のように記入する

#urlの前提はmysqlサービスが事前に同じプロジェクトにデプロイされていること
spring.datasource.url=jdbc:mysql://${MYSQL_SERVICE_HOST}:${MYSQL_SERVICE_PORT}/${DATABASE_NAME}#usernameとpasswordはdeployment.yamlファイルを参照
spring.datasource.username=${MYSQL_DB_USERNAME}spring.datasource.password=${MYSQL_DB_PASSWORD}spring.datasource.driver-class-name=com.mysql.jdbc.Driver

 デプロイするアプリケーション関連のマニフェストファイルは「src/resource/fabric8」配下に配置する。
※注意:1つのマニフェストファイルに複数のItemを記入しないこと(例:deployment.yamlファイルにDeploymentとサービス両方記入するとエラーが発生する。)
deployment.yamlの例(詳細はgitリポジトリを参照)

apiVersion:apps/v1kind:Deploymentmetadata:name:userinfonamespace:application-deploymentlabels:app:userinfospec:replicas:1template:spec:containers:-env:-name:MYSQL_DB_USERNAMEvalueFrom:# OpenShiftコンソールから作成したサービスは自動的にsecretが生成されるので、そのまま利用すれば良いsecretKeyRef:key:database-username:mysql-name:MYSQL_DB_PASSWORDvalueFrom:secretKeyRef:key:database-passwordname:mysql-name:DATABASE_NAMEvalueFrom:secretKeyRef:key:database-namename:mysql

 上記の手順が終わったらターミナルにて以下のコマンドを実行する。

$mvn clean fabric8:deploy -Popenshift
3.oc new-app <git repsitory url>コマンドを利用する。

 OpenShiftはコマンドでGit Repositoryのソースコードをそのままビルドし、デプロイすることができる。ただ以下の条件に満たす必要がある。

The 'oc new-app'command will match arguments to the following types:

  1. Images tagged into image streams in the current project or the 'openshift' project
     - if you don't specify a tag, we'll add ':latest'
  2. Images in the Docker Hub, on remote registries, or on the local Docker engine
  3. Templates in the current project or the 'openshift' project
  4. Git repository URLs or local paths that point to Git repositories

 本文章ではこのやり方について紹介しない。

NodeJSアプリケーションのデプロイ

準備

 アプリケーションのデプロイのためにサンプルコードを書いてみた。
参考:https://github.com/Zhuangbili/openshift_spring_nodejs/tree/master/UserInfo

ビルド及びデプロイ

 下記のコマンドを利用する。(GitOpsを利用する場合OCコマンドの部分は省略する。)
 (利用するYamlファイルは https://github.com/Zhuangbili/openshift_spring_nodejs/tree/master/yamlを参照)

$docker build -t<repository>/<Application Name>:tag .$docker push <repository>/<Application Name>:tag
$oc login
...
$oc project <Project Name>
$oc create -f<Application Name>-deployment.yaml
$oc create -f<Application Name>-service.yaml

 アプリケーションをそのまま公開する場合Routeを作成する(RouteはKubernetesにはないもの)。

$oc expose <Application Service Name>
OR
$oc create -f<Application Name>-route.yaml

 istioを利用する場合場合GatewayとVirtualServiceを作成し、GateWayと連携する。

$oc create -f gateway.yaml
$oc create -f<Virtual Service Name>-virtualservice.yaml

仕組みはSpring bootと同じである。

まとめ

 今回紹介したのは手動のデプロイ方法であるだけで、実際にパイプラインを利用すると、異なる部分もあるとおもうので、参考になればと思います。
 OpenShift特有のDeployment Config、image stream、s2i等はまだためしていないため、紹介ができませんが、今後順次紹介します。

非プログラマのWindowsマシンにGit、Node.js、Nuxt.js環境を構築する手順

$
0
0

デザイナー(非プログラマー)のWindowsPCにNuxt開発環境を構築してもらう必要がありましたので手順書を作りました。

目標

非プログラマーが非プログラマ―の操作によって開発環境を構築します。
最終的にWindowsPCでNuxt.jsのプロジェクトを起動することを目標とします。

導入内容

  • Git for Windows
  • Tortrise Git
  • Node.js
  • Yarn
  • Nuxt.js

事前に準備するもの(プログラマーが伝えておくこと)

  • Nuxt.jsプロジェクトを管理しているGitリポジトリのURL
  • 導入するNode.jsのバージョン(たいていLTSでOKかと思います)
  • Nuxt.jsプロジェクトの起動・接続方法(たいていyarn devlocalhost:3000でOKかと思います)

手順

Gitのインストール

以下の記事が完璧な内容でした。
TortoiseGitのセットアップ

こちらを参照してGit for Windows、Tortrise Gitを導入してください。
GitのホスティングサービスはGitLabで解説されていますが、GitHubでもほぼ同じ手順で実施できます。

上記の記事を参照し、最後のgit cloneまで全て進めてください。

ツールの簡易説明

  • Git for Windows: "Git"というツールをWindows環境で使用するために導入します。
  • Tortrise Git: GitをGUI(マウス操作)で使用できるようにするために導入します。

Nodeのインストーラのダウンロード

以下からNodeのインストーラをダウンロードし、実行してください。
Node.js ダウンロード

たくさんの選択肢があり、どれを選択したらよいか迷うかもしれません。
ポイントは以下です。

  • 32bit or 64bit
    • Windows 64bit 確認といったキーワードでGoogle検索し、自身のPCがどちらなのかを確認してください。
  • LTS(推奨版) or 最新版
    • Node.jsのバージョンをプログラマに確認してください
  • .msi or .zip
    • わからない場合は.msiでいいと思います。

参考までに2019年11月現在の場合、以下を選ぶ方が一番多いと思います。

  • LTS(推奨版) (2019年11月現在はv12)
  • Windows Installer (.msi)
  • 64bit

Node.jsの簡易説明

Nuxt.jsを実行するために導入します。
「ノードジェイエス」または「ノード」と呼びます。

Node.jsのインストール

ダウンロードしたファイルを実行し、Node.jsをインストールしてください。
全てYesやOKで進められるので、詳細な説明は割愛します。
こちらはGitより簡単です。

コマンドプロントの起動

Node.jsのコマンドを実行するためにコマンドプロンプトを開きます。
開き方はコマンドプロント 開き方といったキーワードでGoogle検索して確認してください。

コマンドプロントを開くことができたら以下のコマンドを実行してください。

コマンドプロンプト(実行するコマンド)
node --version

Node.jsのバージョンが表示されたら成功です。

コマンドプロンプト(実行結果)
C:\Users\ユーザー名>node --version
v12.13.0

Yarnのインストール

Yarnを導入します。
コマンドプロンプトで以下を実行してください。

コマンドプロンプト(実行するコマンド)
npm install -g yarn

ダウンロードできたことを確認するために、以下を実行してください。

コマンドプロンプト(実行するコマンド)
yarn --version

Yarnのバージョンが表示されたら成功です。

コマンドプロンプト(実行結果)
C:\Users\ユーザー名>yarn --version
1.19.1

Yarnの簡易説明

パッケージマネージャ(必要なものを簡単にダウンロードしてくれるツール)です。
よく「同じようなツールのnpmよりも優れている」と紹介されます。

yarn install

上記でYarnをインストールしました。
続いてYarnを使用して必要なパッケージをインストールします。

まず、作業フォルダを先ほどgit cloneしたフォルダに変更します。
パスは「git cloneを実行したフォルダ」ではなく「git cloneで生成されたフォルダ」という点に注意してください。

コマンドプロンプト(実行するコマンド)
cd "git cloneで生成されたフォルダのパス"

# もしCドライブ以外(Dドライブなど)でgit cloneした場合、以下のように`/d`も付けてください。
cd /d D:\

そしてyarn installを実行し、必要なパッケージを取得します。

コマンドプロンプト(実行するコマンド)
yarn install

最後にsuccessと表示されたら成功です。

Nuxt.jsの起動とアクセス

最後にNuxt.jsのプロジェクトを起動し、Web画面を表示します。
コマンドプロントにyarn devと入力してください。

コマンドプロンプト(実行するコマンド)
yarn dev

そしてブラウザのURL欄にlocalhost:3000と入力してください。
画面が表示されたら成功です。

以上で全手順が完了です。

備考

非プログラマ向けの手順なので、用語を雑に説明している点はご了承ください。
ただ、誤った説明は書いていないつもりです。
もし誤りがありましたらご指摘頂けますと嬉しいです。

また、環境によって差異が発生しそうな部分には"たいてい"といった言葉を付けるようにしました。
手順に詰まった場合はそのあたりをまず確認してみてください。

Nightmare.js でEC-CUBEの注文データを程よく自動生成する

$
0
0

環境

・EC-CUBE4.0.3
・Node.js

はじめに

自動で注文データを作ろうと思うと、EC-CUBE3系の頃からnanasessさん作成の Fakerを使ったGenerateDummyDataCommandコマンドに大変お世話になっています。
4系でも、少し仕込みをしてから bin/console e:f:g (←アルファベット順なので覚えやすいw)で各種データを作ることができます。
また、slack から EC サイトの自動注文テストを走らせる (puppeteer)も良記事です。

今回は、古参のNightmare.jsを使って実現しています。
こんな感じで、自動実行できるわけです。

image

背景

なぜ必要だったかというと、作成された受注データをEC-CUBEから基幹系に渡すのに、24Hでテスト稼働して処理のキャパを見たり(bin/console e:f:gをcronで回してもいいのですが)、各種データを拡張していたりして小回りを利かせたいのでjsで書きました。
こちらとは別に、JMeterを使ってぶん回すことも実際にはやっています。

実行

ソースはこちらになります。

$ npm install colors
$ npm install nightmare

でインストールした後に

node order_by_non_member.js 

で実行します。

処理

co(function*(){// 商品詳細画面を表示yieldnightmare.goto(host+'products/detail/'+productId).wait('.ec-headingTitle');// 規格の選択yieldselectCombo(nightmare,'#classcategory_id1');yieldselectCombo(nightmare,'#classcategory_id2');

レスポンスを待って、次の処理に行く必要があるため
ジェネレータ関数内の要所要所にyieldを書いて、coを使うというスタイルです。

これでいざテスト環境で試そうと思ったら、ダイジェスト認証を突破できなかったりオレオレ証明書に引っかかったりしてしまいましたが、ignore-certificate-errorsがそのまま指定できるおかげで回避できました。
この辺はソースに反映しています。

また、上のソースには書いていませんが、カード決済を選んだ場合に、カード情報を入力せずに何割かは離脱させたりといったこともテスト時にはやっています。
ECでは大丈夫ですが、連携先システム側の挙動を見るために、"><script>alert(document.cookie);</script>などのデータを自動入力させるとか、わりとやりたいことをすぐに反映して試せます。

最後に

Puppeteerでawaitで書くのが今風ではありますが、やりたいことは実現できました。
今度は、Puppeteerを触ってみたいと思います。

API Gateway + Lambda + DynamoDBでサーバーレス環境を構築する

$
0
0

API Gateway + Lambda + DynamoDB = Serverless

AWSでサーバーレスを構築するアーキテクチャを考えた時に、もっとも基本的でわかりやすい構成は、API Gateway + Lambda + DynamoDB(ないしはS3)の組み合わせだと思います。

このエントリーで触れること

  • Lambda関数の作り方
  • API Gatewayでのリソースの作り方
  • API GatewayとLambda関数の紐づけ方
  • Lambda関数でのDynamoDBのいじり方

AWS SAMには触れないです。

Lambdaで関数を作る

Lambdaで関数を作ってデプロイするのは非常に簡単な作業です。

ランタイムはnode.jsを使います。

関数を作成する方法としては、
1. AWSコンソール上で関数を作成する
2. ローカルで関数を作成してzip化し、Lambdaにアップロードする
3. ローカルで関数を作成してzip化し、S3にアップロードした後に、Lambdaからそれを呼ぶ
4. ローカルで関数を作成してzip化し、cliでデプロイする
の4種類の方法があり、もっとも簡便に作るのであれば1で事足ります。

しかし、もっと込み入った内容のロジックが必要なのであれば、2,3,4の方法を選びます。

たとえば、node_modulesを入れてyarnでインストールしてきたライブラリを使いたいとか、lambda上ではできないけどローカルでは用意できるようなものを設定したい時には、2,3,4の方法でデプロイした方が良さそうです。

ここでは1と2,3,4の関数作成方法に分けて簡単に関数を作ってみます。

AWSコンソール上で関数を作成

AWS Lambdaのページからサイドペインの「関数」を選択すると、関数が一覧で表示されます。
この一覧のヘッダー部分に以下のように関数の作成ボタンがあります。

Screenshot 0031-11-05 at 10.42.09 PM.png

ボタンクリック後の設定では、

  • 一から作成(選択)
  • 関数名(入力)
  • ランタイム(ここではnode.js10.x) (選択)
  • 基本的なLambdaアクセス宣言で新しいロールを作成(選択)

「関数の作成」からLambda関数を作成します。

すると、作成した関数の詳細ページへ飛び、関数のコードを編集することができます。

Screenshot 0031-11-12 at 9.37.29 PM.png

デフォルトのコードでは、'Hello from Lambda!'の文字列がstatusCode200で返ってくるようになっています。
ここでrequestを受け取って処理するなどを行うためには、この後説明するAPI Gatewayの設定を行う必要があります。

ローカルで関数を作成してLambdaにアップロード

$ mkdir myFunction | cd myFunction
$ yarn
$ yarn add moment
$ vim index.js

index.jsの中身は適当にこんな感じで:point_down_tone2:

Screenshot 0031-11-12 at 9.34.11 PM.png

コードが書けたらnode_modulesと同じレベルのディレクトリ内でzipコマンドを実行します。

$ tree
.
├── index.js
├── node_modules // 以下省略
├── package.json
└── yarn.lock
$ zip myFunction . -r
$ ls
index.js       myFunction.zip node_modules   package.json   yarn.lock

解凍した時に上記の内容を含んだmyFunctionディレクトリがトップレベルになる必要があるのでzipコマンドには-rオプション(recurse into directory)をつけています。

zipができたらLambdaのコンソールに移動して、以下のコードエントリタイプのドロップダウンから「.zipファイルをアップロード」を選択します。

Screenshot 0031-11-05 at 11.14.08 PM.png

アップロードボタンが出てくるのでそこから上で作成したzipをアップロードしてみましょう。
保存すると、関数コード欄のソースコードがローカルで作成したコードに書き換わります。

インストールしておいたmomentも使えるようになり、エディターのサイドペインにnode_modulesが追加されていると思います。
コンソール上だけだとnode_modulesを利用することができませんが、このようにローカルで作成してからアップロードするとコンソール上でもnode_modulesを扱えるようになります。

Screenshot 0031-11-05 at 11.23.16 PM.png

ローカルで関数を作成してS3にアップロード

Lambdaに直接zipをアップロードすることもできますが、S3にアップロードしたzipを参照することもできます。
Lambda上に表示される注意書きにもありますが、大きなサイズのzipをアップロードする際にはS3経由でLambdaにデプロイするのが望ましいようです(具体的には10MB以上はS3の利用を検討した方が良いらしいです)。

上記で作成したmyFunction.zipを使いまわします。

$ zip myFunction . -r

でmyFunction.zipを作成したのち、S3コンソールを開きます。

適当な名前のバケットを作成して、その中にmyFunction.zipをアップロードします。

Screenshot 0031-11-12 at 9.43.06 PM.png

次にLambdaコンソールを開きます。
関数コードの「AmazonS3からのファイルのアップロード」を選択して、S3にアップロードしたzipのオブジェクトURLを入力します。

Screenshot 0031-11-12 at 9.46.42 PM.png

あとは関数を保存するだけです。
テストを実行すると、レスポンスが返ってきます。

Screenshot 0031-11-12 at 9.48.54 PM.png

ローカルで関数を作成してCLIでデプロイ

CLIで関数を作成する場合は、create-functionを利用します。
https://docs.aws.amazon.com/cli/latest/reference/lambda/create-function.html

  create-function
--function-name <value>
--runtime <value>
--role <value>
--handler <value>
[--code <value>]
[--description <value>]
[--timeout <value>]
[--memory-size <value>]
[--publish | --no-publish]
[--vpc-config <value>]
[--dead-letter-config <value>]
[--environment <value>]
[--kms-key-arn <value>]
[--tracing-config <value>]
[--tags <value>]
[--layers <value>]
[--zip-file <value>]
[--cli-input-json <value>]
[--generate-cli-skeleton <value>]

また、関数を更新する場合には、update-function-codeかupdate-function-configurationを利用します。
https://docs.aws.amazon.com/cli/latest/reference/lambda/update-function-code.html
https://docs.aws.amazon.com/cli/latest/reference/lambda/update-function-configuration.html

update-function-codeはlambda関数のコードを更新するコマンドで、
update-function-configurationはlambda関数の設定(ランタイムや環境変数)を更新するコマンドです。

他のコマンドについてはCLI Command Referenceを参照してください。

さて、デプロイした後はそれだけでは関数は使えませんので、API Gatewayを介して呼び出せるように設定します。

API GatewayからLambda関数を利用する

API GatewayとLambdaの関係性は、

  • API Gateway
    クライアントからの通信を受け付けてLambda関数にリクエストを渡す。
    また、そこから受け取ったレスポンスをクライアントに返却する。
    (API GatewayはLambda以外にも既存のHTTPエンドポイントやSQSやSNSなどのAWSリソースと連携させることもできます)

  • Lambda
    API Gatewayから受け取ったリクエストを基に処理を実行し、レスポンスを返却する。

Screenshot 0031-10-27 at 7.58.53 PM.png

① メソッドリクエスト

クエリーのパラメータ等を設定する。

② 統合リクエスト

API Gatewayのリソースが呼び出す対象を設定する(ここではlambda関数となる)。
lambdaへ送るbodyパラメーターのテンプレートを設定する。

③ 統合レスポンス

lambdaから返ってきたレスポンスをAPI Gatewayのレスポンス設定にマッピングする。

④ メソッドレスポンス

API Gatewayがクライアントへ返すステータスコードごとのレスポンスヘッダーやボディを設定する。

API Gatewayでエンドポイントを作る

API Gatewayの「APIの作成」からAPIを作っていきます。
今回はプロトコルをRESTとし、名前はmyAPIとします。

Screenshot 0031-11-12 at 10.05.19 PM.png

作成されたmyAPIのリソース画面で、「アクション」のドロップダウンをクリックするとアクション一覧が表示されます。
ここでリソースを作成します。

Screenshot 0031-11-12 at 10.05.53 PM.png

リソース名を入れて「リソースの作成」を行います。

Screenshot 0031-11-12 at 10.18.59 PM.png

そして、作成したリソースにメソッドを追加していきます。
「アクション」ドロップダウンからメソッドの作成を選択し、メソッドの種類を選択します。
ここではGETを選択してチェックします。

Screenshot 0031-11-12 at 10.19.48 PM.png

GETメソッドの統合タイプにLambda関数を選択し、
Lambda関数に上記で作成したmyFunction関数を選択します。

Screenshot 0031-11-12 at 10.21.50 PM.png

保存すると以下のような画面が表示されます。
ここで前述したメソッドリクエスト、統合リクエスト、統合レスポンス、メソッドレスポンスを設定します。

Screenshot 0031-11-12 at 10.23.11 PM.png

今回冗長となるのでこれらの細かい説明は省きます。

あとはAPIをデプロイして、作成されるエンドポイント/myresourceにアクセスしてみましょう。
デプロイ時のステージなどは好きなように設定してみてください。

Screenshot 0031-11-12 at 10.28.12 PM.png

すると、先ほど作成したLambda関数のレスポンスがブラウザに表示されるはずです。

Screenshot 0031-11-12 at 10.29.14 PM.png

LambdaからDynamoDBに接続する

次はLambdaからDynamoDBの値を取得して、その値をクライアントへ返せるようにします。

DynamoDBにデータを作る

DynamoDBのダッシュボードから「テーブルの作成」をクリックします。

Screenshot 0031-11-12 at 10.35.39 PM.png

パーティションキー等の設計についてはDynamoDBのベストプラクティスを参考にしてみてください。

作成したテーブルに項目を追加します。
「項目の作成」からパーティションキーの値の他に任意の項目を追加します。

Screenshot 0031-11-12 at 10.40.10 PM.png

これでデータが用意できたので、このテーブルにLambdaから接続して値を取得できるようにします。

以下のようなjsファイルを用意し、zip化します。

constAWS=require('aws-sdk')constmoment=require('moment')constdynamo=newAWS.DynamoDB.DocumentClient({region:'ap-northeast-1'})exports.handler=async(event,context)=>{constparams={TableName:"testTable",Key:{uuid:"7dd1b5f4-bb05-4a87-b464-5ddf88254837"}}awaitdynamo.get(params,(err,data)=>{if(err)context.fail(err)context.succeed({statusCode:200,body:data,time:moment().format('YYYY/MM/DD')})}).promise()}
$ tree
.
├── index.js
├── node_modules // 以下省略
├── package.json
└── yarn.lock
$ zip myFunction . -r
$ ls
index.js       myFunction.zip node_modules   package.json   yarn.lock

直接LambdaにアップロードでもS3にアップロードしてそれを参照するのでも良いです。
アップロード後に保存してテストを実行してみてください。

Screenshot 0031-11-13 at 12.27.32 AM.png

こんな感じでレスポンスが返ってきたら成功です。
この関数を紐づけたAPI Gatewayのエンドポイントに接続してみてください。

Screenshot 0031-11-13 at 12.28.57 AM.png

DynamoDBから取得したデータがブラウザに表示されると思います。

もっと応用的に、リクエストを受け取って任意のデータを取得したいような場合には、統合リクエストのマッピングテンプレート等を設定して、Lambdaに渡るeventの中身を定義する必要がありますが、ここでは省きます。

ここまでで以下の項目について触れてきました。

  • Lambda関数の作り方
  • API Gatewayでのリソースの作り方
  • API GatewayとLambda関数の紐づけ方
  • Lambda関数でのDynamoDBのいじり方

上でやったようにコンソールで色々と操作しましたが、AWS SAMを使えばコマンドラインでサーバーレスを構築する構築することもできます。
ローカルでテストができるのは気が楽ですね:blush:


AWS DynamoDBをjsでいじるときに非同期処理で詰まった話

$
0
0

AWS DynamoDBをjsでいじるときに非同期処理で詰まった話

初めに

このお話は、単に私がJavaScriptの非同期処理を理解していなかっただけなので、プロの皆さんでしたら余裕で回避できる問題だと思います。

私みたいな初学者で、同じようなことに詰まっている方がこの記事を見て解決していただけたら幸いです。

私がやりたかったこと

DynamoDBのCRUD操作を関数内で行い、実行結果を戻り値として受け取る といったことをしたかったのですが、戻り値に値が入っていない状態で返されてしまいました。

// 問題のあるコードfunctiongetHoge(){lethoge;letparams={TableName:'table',Key:{"id":"01"}};documentClient.put(params,function(err,data){if(err){console.log(err);}else{console.log(data);// {}hoge=data;}});returnhoge;}exports.handler=function(event){lethoge=getHoge();console.log(hoge);// undifind}

PHPのようなノリで書いてしまったため、undifindの原因がわからずに悩みまくってましたが、ようやっと非同期処理のせいだ!って気づいたため、以下のように書き直しました

書き直した

// ちゃんと動くやつasyncfunctiongetUser(){lethoge;letparams={TableName:'table',Key:{"id":"01"}};try{// hogeにデータが入るまで待つhoge=awaitdocumentClient.get(params).promise();}catch(e){console.log(e);}returnhoge;}exports.handler=async(event)=>{lethoge=awaitgetUser();console.log(hoge);// {jsonデータが見れる!}};

はい、解決。

公式ドキュメントを見てみると、、、

DynamoDB DocumentClient get-propaty

varparams={TableName:'Table',Key:{HashKey:'hashkey'}};vardocumentClient=newAWS.DynamoDB.DocumentClient();documentClient.get(params,function(err,data){if(err)console.log(err);elseconsole.log(data);});

コールバックで書かれてるので、JS初心者には自力での解決キツイっす。。。

最後に言いたいこと

非同期むづかしい

npm pack でnpmパッケージを圧縮する

$
0
0

node_module内のファイル(主にandroidのbuild.gradleなど)を編集した際にnpm packのコマンドを使用して変更を加えたパッケージを.tgzファイルにして、package.jsonの読み込み先を.tgzファイルに必要がある。

手順

例:react-native-mapsbuild.gradleを編集した時

  • 編集したパッケージのフォルダに移動(node_modules/react-native-maps/)し、.tgzファイルを生成
npm pack
  • custom_modulesフォルダを作っておき、生成された.tgzファイルをcustom_modulesフォルダに移動する
  • 読込先を.tgzファイルに変更する
npm i react-native-maps-0.24.2.tgz
  • package.jsonの読み込み先が変更されているか確認する
package.json
"react-native-maps":"file:custom_modules/react-native-maps-0.24.2+.tgz"

参考にした記事

https://qiita.com/sasaplus1/items/f206dccd50709a50bbd0

node.jsでHTTPリクエストを受け取るAPIを作る

$
0
0

はじめに

今回はNode.jsのフレームワークであるExpressでHTTPリクエストを受け取るアプリケーション?を作りたいと思います。
利用用途としては

  • しっかりリクエスト送れてるの?
  • Expressとかnode.jsでAPIを作ってみたい

といったことが想定されます。私の場合は主に、というか9割前者ですね。JMETERでリクエストが正しく送れているか確認したかっただけです。

実装

下準備

まず以下のコマンドを実行します。この時、プロジェクトのディレクトリ内で行いましょう。

> npm init

あとはダイアログに従い、入力するだけです。
するとpackage.jsonが作成されると思います。

次にExpressをインストールします。

> npm install express -save

このコマンドをたたけばいろいろダウンロードされ、環境が整います。

実行するファイルを作る

index.js
constexpress=require('express')constapp=express()// body-parserのおまじない// 参照:https://expressjs.com/ja/4x/api.html#req.bodyconstbodyParser=require('body-parser')app.use(bodyParser.urlencoded({extended:true}))app.listen(3000)app.get('/get',function(req,res){res.send('Receive Get request')console.log('GETリクエストを受け取りました')})app.post('/post',function(req,res){res.setHeader('Content-Type','text/plain');res.send('Receive POST request as '+req.body.text)console.log('POSTリクエストを受け取りました')console.log(req.body)})

body-parserのおまじないこれかなり重要です。これがないとreq.bodyでは表示されません。

実行してみよう

ここではVScodeのREST Clientを使います。

GETリクエスト

GET http://localhost:3000/get

返ってくるレスポンス

HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/html; charset=utf-8
Content-Length: 19
ETag: W/"13-XJXW/hqJ3os48dLZM0f0yOQl3GA"
Date: Fri, 15 Nov 2019 16:02:55 GMT
Connection: close

Receive Get request

POSTリクエスト

POST http://localhost:3000/post
Content-Type:application/x-www-form-urlencoded

text=テスト送信

返ってくるレスポンス

HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/plain; charset=utf-8
Content-Length: 38
ETag: W/"26-i+FQlfv64fSmWg1IEMiZfpdw9eY"
Date: Fri, 15 Nov 2019 16:03:59 GMT
Connection: close

Receive POST request asテスト送信

注釈

res.send('Receive POST request as '+req.body.text)

のtextの部分を変えることでPOSTリクエストの

text=テスト送信

のtextの部分を変えることができます。

まとめ

ほかにもjson形式のPOSTリクエストを受け取るとかできますが、今回は省略。

参考

https://qiita.com/kefian1go/items/198d9da2240846e28a48
https://expressjs.com/ja/4x/api.html#req.body

これからチーム開発に参加するUIデザイナーがGitHubでの開発フローをまとめた

$
0
0

これからチーム開発に参加するUIデザイナーがGitHubでの開発フローをまとめた

注意

  • (自分用備忘録です)
  • (開発初学者です)

前提

  • 環境構築が終わっていてローカルホストが立ち上がっている状態
  • リモートリポジトリのcloneがローカルに完了している

いつもはじめにやること

0-1.開発が決まったら、作業リボジトリにXDなどのデザインデータのリンクを貼った開発issue を立てる
- issue番号はメモっておき、あとでbranch名に使う

0-2.githubに接続する
$ ssh github

0-3.ターミナルでcloneした作業レポジトリ(ローカル)まで移動する

ターミナルでの操作
$ cd git
$ cd 作業リポジトリ名

準備完了!

フロー

  1. レビュワーがGitHubのPull requestsにrelease noteを切ってるのを確認する

    例) master from release/0.0.1

  2. Pull requestsの項目の一番を見る 上のvが最新のrelease note

    例) release/0.0.1

  3. リモートのrelease/0.0.1から最新の情報をfetchする

    $ git fetch origin release/0.0.1

  4. 自分が改善するissue/#番号 のbranchを切る
    $ git checkout -b issue/#番号

  5. 今のbranchを確認する
    $ git branch

  6. 今切ったbranchに切り替える
    $ git checkout issue/#番号

  7. コードを頑張って書き換える

  8. 修正したらCommitする(Commit方法は2つある)

commit方法1

Sourcetreeのファイルステージで変更箇所を確認しコメント欄に修正箇所を書き、にコミットボタンを押す
 [コミットを直ちにorigin/testプッシュする]を選択すると、プッシュまで行われる。

commit方法2

Sourcetreeのファイルステージで変更箇所を確認し、

$ git commit -m "branch名" -m "コメント"でcommit

9, GitHubにPush
$ git push origin branch名でPushする

10, GithubでPull request
自分が作ったbranch を New Pull Requestする

11, PullのURLをSlackに貼ってレビューお願いする!

Pullrequestで気をつけること

  • 中規模issueであればプルリクエストは小分けにする
    • 一つのissueを自分でタスクを小分けにする
    • 小さなタスクissue#1-1をブランチを切り編集しコミット、OKをもらったら、次はまた小さなタスクissue#1-2でプルリクエストするとレビュワーに負担がかからない
    • OKをもらったらmergeする

その他

  • issueのmilestone項目でフィルタリングすることで同じrelease目標のissueがわかる(リリース権限のある人が設定を行っていたら。)
  • ブラウザチェックは ChromeとfirefoxとSafariとか現場で決まったもの

一緒の場所を修正作業してる人が先に変更点をmergeした場合

  • ローカルを新しい情報に更新する
    $ git pull origin release/0.0.1
    $ merge release/0.0.1

  • マージしようとしてコンフリクトした時にmergetoolで差分を確認する
    $ git mergetool
    今のmasterブランチの状態と、マージ前の状態とマージしようとしているファイルの状態がわかりやすく表示されるので、差分を編集しマージする。

Vue Web App Tutorial

$
0
0

11月16日 Web Application 製作入門

富山IT勉強会#4のハンズオン資料をまとめたものです。
非エンジニア、およびWebアプリ未経験者を対象に簡単なWebアプリ(SPA)を作成するハンズオンを行います。

このハンズオンのゴール(目的)

  • 必要最低限の知識で Webアプリを作れるようになる(概念、環境構築 ~ Local環境での動作確認)
  • 今後、更にレベルアップしていく際の切り口を共有する

おことわり

一日(4h ほど)で行う予定であるため、最低限知っていれば取りあえずアプリが作れる程度に厳選しております。
そのため、かなり内容は端折っておりますのでご了承ください。
また、私自身プログラミング経験はWindowsアプリケーションがほとんどで、
Web関係は直近一年弱程度ですので情報にはかなり偏りがあります。
また、誤情報等、お気付きの場合にはご指摘頂けると幸いです。

やること

  • JavaScript入門(環境構築〜変数、制御構文、クラス、関数を最小限、簡単に。npmコマンドにもふれる)
  • Node.js、Vue を使用したWebアプリケーションを作成(Vue基本構文, Vue/Cliコマンド)
  • expressを使用したWebAPIの作成とWebアプリとの通信(express 最小構成でのWebAPIサーバー構築)
  • 時間があった時のオプション
    • PythonでもWebAPIを作成してみる(Flask 最小構成)
      • -> AI 機能を組み込んだWebAPIへ応用可?
    • Electronを使用したデスクトップアプリ化(electron-vueを使用)
      • -> Web技術を使用したデスクトップアプリ開発へ繋げたい場合
    • CSSフレームワークを使用してみる(vuetify)
      • -> もっと見た目をきれいに、機能的にしたい場合

やらないこと

  • formタグを使ったGet、Post等の通信
  • yarn
  • 本番環境へのデプロイ(ローカル環境でのみの作業とします)
  • セキュリティ関連
  • DataBase操作

1. Web(Server - Client 型アプリケーション) の仕組み

本当にザックリですが図を作ってみました。

image.png

Webサイト、Webアプリなどは、このClient-Server型のアプリケーションになります。
Client Side はChrome等のブラウザを通してサーバーへリクエストを行います。
サーバーPCの中では、この時Apache等のサーバーアプリやデータベースが起動していて、
リクエストに応じた処理を行い、結果をHtmlファイルやJson等で返信します(レスポンス)。

今回は、この図でいうバックエンドのサーバーアプリ部分をexpressで作ります。
また、レスポンスとして返すHtmlファイルを、Vueを使って作成します。

2. 環境構築

Node.jsのインストール

nodejs.org :https://nodejs.org/ja/からLTS版をダウンロード。
ダウンロードされたインストーラーを実行してインストールを行ってください。

Node_js.png

インストール完了後、以下のコマンドをshellへ入力して、バージョンが返ってきたらOK。

node -v
npm -v

visual studio code (vscode) のインストール

vscodeを用いてハンズオンを行いますが、お気に入りのエディタがある場合はそちらを使っていただいてもかまいません。

https://code.visualstudio.com/downloadから自分のOSに合わせたインストーラーをダウンロードし、インストールを行ってください。

3. JavaScript(というかNode.js)入門

まずはhello world

hello.js
constmsg='hello world';console.log(msg);
node ./hello.js

=> hello world

変数の型(es6)

今回使用するもののみピックアップ。

Number

数値型。C#やPython等では整数型(Int)、浮動小数点型(Float)等あったがjsはこれだけ

constpi=3.14;varindex=1;

String

文字列型

constmessage='hello world';// '' で囲むvarname="huga";// "" もOK

Boolean

true or false

constflg=true;console.log(flg);// => trueconsole.log(!flg);// => falseif(flg){console.log('flg is true');// => flg is true}

Array(配列)

配列。どちらかというとリストに近いような...

// 宣言方法constarr=[];// arr => [](空配列)letvalues=newArray();// values => [](空配列)constarr2=[1,2,3];// arr2 => [1,2,3]constvalues2=newArray(5);// values => [ <5 empty items> ] (5要素の配列)console.log(values2[1]);// => undefined// 要素の追加arr.push(1);arr.push(2);console.log(arr);// => [1,2]// 要素の参照console.log(arr[0]);// => 1console.log(arr[1]);// => 2// 要素の取り出しconsole.log(arr.pop());// => 2console.log(arr);// => [1]

Function

関数

// 通常?の宣言functiontwice(num){returnnum*2;}// ラムダ式constjoinStr=(str1,str2)=>{returnstr1+str2;};console.log(twice(2));// 4console.log(joinStr("hello ","world"));// hello world

Object

オブジェクト、連想配列。classとかもes5ではオブジェクトになる?? keyに対して任意のデータを割り当てる

// 宣言constobj={};constobj2=newObject();// 要素の追加obj["id"]=1;obj.name="huga";obj.getName=function(){returnthis.name;};console.log(obj);// => { id: 1, name: 'huga', getName: [Function] }// 要素の参照console.log(obj["id"]);// => 1console.log(obj.name);// => hugaconsole.log(obj.getName());// => huga

null, undefined

null は他の言語と大体同じ?参照先が未割当の状態。
undefined はそもそも項目すらない状態?(よくわかってません)
今回はここを理解していなくてもできるようにやっていきます。

const, let, var

var
変数を宣言し、ある値に初期化することもできる。グローバル変数として定義される

let
ブロックスコープのローカル変数を宣言し、ある値に初期化することもできる。
ローカル変数(スコープ内で有効な変数)で、再代入が可能。

const
読み取り専用の名前付き定数を宣言する。
ローカル変数(スコープ内で有効な変数)で、再代入が不可能。

制御構文

if

分岐処理

letflg=false;if(flg){// 実行されない}else{// 実行される}if(flg!=true){// 実行される}

for

繰り返し処理

constnumbers=[1,2,3,4,5];for(leti=0;i<numbers.length;i++){constelement=numbers[i];console.log(element);// => 1// => 2// => 3// => 4// => 5}

他にもswich, while等あります。

参考リンク
- 文法とデータ型(MDN)
- ES2015(ES6) 入門

4. HTML

適当なフォルダをvscodeで開いて、適当な名前.htmlというファイルを作成します。
ファイル内で htmlと入力すると以下のように補完が表示されると思いますので、html5を選択します。

以下のようにテンプレートが挿入されます。

<!DOCTYPE html><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><metahttp-equiv="X-UA-Compatible"content="ie=edge"><title>Document</title></head><body></body></html>

bodyタグの中にHTMLを記述していきます。

Header

見出しです。h1 ~ h6まであり、数字が小さい程基本的には表示が大きくなります。
bodyタグの中に記述して確認してみます。

<body><h1>h1 ヘッダーです</h1><h2>h2 ヘッダーです</h2><h3>h3 ヘッダーです</h3><h4>h4 ヘッダーです</h4><h5>h5 ヘッダーです</h5><h6>h6 ヘッダーです</h6></body>

〇結果

h1 ヘッダーです

h2 ヘッダーです

h3 ヘッダーです

h4 ヘッダーです

h5 ヘッダーです
h6 ヘッダーです

リスト

リストを表示します。番号付き(<ol></ol>)と番号無し(<ul></ul>)があります。

<body><!-- 番号付きリスト --><ol><li>リスト1</li><li>リスト2</li><li>リスト3</li><li>リスト4</li></ol><!-- 番号無しリスト --><ul><li>リスト1</li><li>リスト2</li><li>リスト3</li><li>リスト4</li></ul></body>

〇 結果

  1. リスト1
  2. リスト2
  3. リスト3
  4. リスト4
  • リスト1
  • リスト2
  • リスト3
  • リスト4

表を表示します。

<body><tableborder="1"><tr><th>項目1</th><td>A</td></tr><tr><th>項目2</th><td>B</td></tr><tr><th>項目3</th><td>C</td></tr></table><hr><tableborder="1"><thead><tr><th>キー</th><th></th></tr></thead><tbody><tr><td>項目1</td><td>A</td></tr><tr><td>項目2</td><td>B</td></tr><tr><td>項目3</td><td>C</td></tr></tbody></table></body>

〇 結果

項目1A
項目2B
項目3C

キー
項目1A
項目2B
項目3C

入力要素

テキストボックスやボタン、スライダー等。
通常は<form></form>タグの中で使用してGETやPOSTメソッドで送信に使いますが、
Vue等のフレームワークでは変数の操作に用いたりします。

<body><div><h3>テキストボックス </h3><inputtype="text"></div><div><h3>テキストボックス(パスワード) </h3><inputtype="password"></div><div><h3>ボタン </h3><inputtype="button"value="ボタンです"></div><div><h3>チェックボックス </h3>チェックしてください<inputtype="checkbox"></div><div><h3>ラジオボックス </h3><inputtype="radio"name="my-radio"value="radio-item1">ラジオ1
        <inputtype="radio"name="my-radio"value="radio-item2">ラジオ2
        <inputtype="radio"name="my-radio"value="radio-item3">ラジオ3
    </div><div><h3>コンボボックス </h3><select><optionselected="0">選択アイテム1</option><option>選択アイテム2</option><option>選択アイテム3</option><option>選択アイテム4</option></select></div><div><h3>スライダー</h3><inputtype="range"></div><div><h3>カラーピッカー </h3><inputtype="color"></div><div><h3> FILE API </h3><inputtype="file"id="file-handler"></div></body>

〇結果 (Chrome)

image.png

4. Vue入門

準備

Webアプリケーション フレームワークのVueを使用して作成していきます。

〇 参考リンク:
- Vue.js(本家)
- 基礎から学ぶ Vue.js(猫本サイト)

まずはHTMLファイルとスタンドアロン版のVue.js(CDN)を使用してVueの基本構文を確認します。
フォルダ内にvue-starter.htmlを作成し、以下のように入力します。

vue-starter.html
<!DOCTYPE html><htmllang="ja"><head><metacharset="utf-8"><title>Vue Starter</title></head><body><divid="app"><!-- このエリアにVue 形式の HTMLを記述していく(描画DOMエリア) --></div><!-- vue.js を読み込んでからVueオブジェクトを記載していく --><script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script><script>// Vue オブジェクトvarapp=newVue({el:'#app',data(){return{// object 形式でプロパティを記述していく};},// 算出プロパティ(キャッシュされる)computed:{},// 監視プロパティwatch:{},// 関数(キャッシュされない、都度評価される)methods:{},// ライフサイクルフック ---------------------------------------------------// https://jp.vuejs.org/v2/api/#beforeUpdatebeforeCreate(){},created(){},mounted(){},beforeUpdate(){},updated(){},activated(){},deactivated(){},beforeDestroy(){},destroyed(){}// ライフサイクルフック ---------------------------------------------------});</script></body></html>
プロパティ名説明
elVueでレンダリングするHTML DOMのidを記述する。
dataレンダリングで参照されるプロパティ。画面描画のもとになる
computeddataを元に算出されるプロパティ。いわゆるgetter
watch登録されているdataの変化を監視し、変更があった場合の処理を記述する
methods関数。ボタンのクリックイベント等、特定のイベントにフックして実行する処理を記述する

ライフサイクルフック

Vueが描画を行う際の特定のタイミングで呼ばれるイベント。
詳しくは以下参照。

宣言的レンダリング(プロパティの参照)

描画DOMエリアとVueオブジェクトを以下のように修正します。

<divid="app"><!-- このエリアにVue 形式の HTMLを記述していく(描画DOMエリア) --><p>{{message}}</p></div>
// Vue オブジェクトvarapp=newVue({el:'#app',data(){return{// object 形式でプロパティを記述していくmessage:'Hello Vue!'};}});

〇結果

image.png

繰り返しレンダリング(v-for)

配列やオブジェクトの要素を繰り返しレンダリングします。
描画DOMエリアとVueオブジェクトを以下のように修正します。

<divid="app"><!-- このエリアにVue 形式の HTMLを記述していく(描画DOMエリア) --><p>Array のレンダリング</p><ul><liv-for="(item, index) in itemList">{{index}}: {{item}}</li></ul><p>Object のレンダリング</p><tableborder="1"><thead><th>key</th><th>value</th></thead><tbody><trv-for="(item, key) in obj"><td>{{key}}</td><td>{{item}}</td></tr></tbody></table></div>
data(){return{// object 形式でプロパティを記述していくitemList:['huga','hoge','poyo'],obj:{name:'Vue',id:1,lang:'JavaScript'}};},

〇結果
image.png

イベント(v-on)

ボタンクリック等のイベントをフックして処理を実行します。
描画DOMエリアとVueオブジェクトを以下のように修正します。

<divid="app"><!-- このエリアにVue 形式の HTMLを記述していく(描画DOMエリア) --><buttonv-on:click="call()">click me!!</button></div>
methods:{call(){alert('button clicked');}},

〇結果
ボタンをクリックするとアラートが表示されます。
image.png

条件付きレンダリング(v-if, v-show)

v-ifv-show共に条件を満たす場合にのみレンダリングされます。
違いは、レンダリングされない場合、以下の点が異なる。
- v-if: レンダリングされない場合、DOMが生成されない。
- v-show: レンダリングされない場合もDOMが生成される。style属性がdisplay: none;となることで表示されなくなる。

描画DOMエリアとVueオブジェクトを以下のように修正します。

<divid="app"><!-- このエリアにVue 形式の HTMLを記述していく(描画DOMエリア) --><button@click="toggleFlg()">フラグ切り替え</button><pv-if="isShow == true">フラグがtrue なら描画されます</p></div>
data(){return{// object 形式でプロパティを記述していくisShow:true};},// 関数(キャッシュされない、都度評価される)methods:{toggleFlg(){this.isShow=!this.isShow;}}

○結果
ボタンを押すたびに下部のテキストの表示、非表示が切り替わります。

算出プロパティと監視プロパティ(computed,watch)

描画DOMエリアとVueオブジェクトを以下のように修正します。

<divid="app"><!-- このエリアにVue 形式の HTMLを記述していく(描画DOMエリア) --><h2>computed</h2><p>num is: {{num}}</p><p>twice is: {{twice}}</p><button@click="increment()">numへ1加算</button><h2>watch</h2><p>{{watchStr}}</p></div>
data(){return{// object 形式でプロパティを記述していくnum:1,watchStr:""};},// 算出プロパティ(キャッシュされる)computed:{twice(){// 二乗の数を返すreturnMath.pow(this.num,2);}},// 関数(キャッシュされない、都度評価される)methods:{increment(){this.num++;}},// 監視プロパティwatch:{// 関数名は監視するプロパティ名num(newValue,oldValue){this.watchStr=`${oldValue} -> ${newValue}`;}},

○結果
Vue_Starter.png

form要素との連携(v-bind、v-model)

inputタグのform要素と同期してdataを操作できます。

html部

<divid="app"><!-- このエリアにVue 形式の HTMLを記述していく(描画DOMエリア) --><h2>v-model(双方向同期)</h2><inputtype="text"v-model="message"><p>{{message}}</p><selectv-on:input="selectedItem = $event.target.value;"><optionselected="0"v-for="item in listItems">{{item}}</option></select><p>入力値:{{selectedItem}}</p><!-- v-bind:r="radius" は :r="radius" と省略してもOK --><h2>v-bind(片方向同期)</h2><inputtype="range"v-model="radius"max="100"min="0"><p>radius = {{radius}}</p><svgviewbox="0 0 300 300"width="300"height="300"><circlev-bind:r="radius"cx=150cy=150></circle></svg></div>

JavaScript部

data(){return{// object 形式でプロパティを記述していくmessage:'hello vue',selectedItem:null,listItems:["item1","item2","item3",],radius:10,};},

○結果

Vue_Starter.png

Vue CLI を使ったWebアプリ(SPA)の作成

ここからfront-end、back-end に分けてWebアプリ(のようなもの)を作っていきます。
まずは、front-end側から作り始めます。

プロジェクトフォルダ作成

お好きなところにvue-spa-sampleという名前でフォルダを作ります。

vue/cliのインストール

vue-spa-sampleフォルダ内でコマンドラインを立ち上げます。
以下のコマンドでVue CLI をグローバルへインストールします。

npm i -g @vue/cli

なお、npm のコマンドは以下が纏まっていてみやすかったです。
npm 入門

インストールが終了したら以下のコマンドでバージョンが返ってくることを確認します。

vue -V

front-end プロジェクトの作成

続けてコマンドラインから
bat
vue create front

でプロジェクトを作成します。
frontの部分はプロジェクト名になるので、
本来はお好きな名前をつけていただいてOKです。

? Please pick a preset:

default (babel, eslint) を選択します。
パッケージのダウンロードが始まります。
全て終了したら、表示されているように以下のコマンドを入力してdev serverを立ち上げます。

cd front
npm run serve

dev server が立ち上がったら、ブラウザからhttp://localhost:8080へ接続します。
以下のような画面が表示されます。

front_と_front_—_node_◂_npm_TERM_PROGRAM_Apple_Terminal_NVM_CD_FLAGS__TERM_xterm-256color_—_80×24.png

確認したら、vscodeでfrontフォルダを開きます。
これからVueのSFCファイルを見て行きますが、ハイライトや補完等の機能が使えるように、
エクステンションでveturを検索し、インストールしておきます。

単一ファイルコンポーネント(SFC)とコンポーネント

Vue CLI で作られたプロジェクトはSFCに対応しています。
SFCとは、コンポーネント(Vueの描画領域を部分的に記述し、部品化したもの)をtemplatescriptcssのそれぞれのタグに分けて記述したものです。

現在のプロジェクトで言うと、src/App.vueが画面全体を描画しているコンポーネントで、
src/components/HelloWorld.vueは部分的リンクの部分を描画している部品になります。

コードで見るとこんな感じです。

全画面_2019_11_16_15_23.png

コンポーネントを作ってみる

src/components/内にMainPage.vueという名前でSFCを作ります。
以下のようにコードを記述します。

src/components/MainPage.vue
<template><div><h1>自作コンポーネントのページです</h1><h2>hello {{name}}</h2></div></template><scriptlang="ts">importVuefrom'vue';exportdefaultVue.extend({name:'main-page',data(){return{name:'vue',// 好きな名前を入れてください}}});</script><style></style>

ファイルを保存したら、src/App.vueを以下のように修正します。

src/App.vue
<template><divid="app"><imgalt="Vue logo"src="./assets/logo.png"><!-- コメント化 --><!-- <HelloWorldmsg="Welcome to Your Vue.js App"/> -->
    <!-- 追加 --><main-page/></div></template><scriptlang="ts">// import HelloWorld from './components/HelloWorld.vue' // <-コメント化importMainPagefrom"./components/MainPage.vue";// <-追加、.vueを忘れないでexportdefault{name:'app',components:{// HelloWorld // コメント化MainPage// 追加}}</script><style>#app{font-family:'Avenir',Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-align:center;color:#2c3e50;margin-top:60px;}</style>

ファイルを保存します。
再度先ほどブラウザで開いたhttp://localhost:8080を見るとオートリロードがかかって
以下のようになっていると思います。(なっていなかったら手動でリロードしてください)

front_と_App_vue_—_front.png

MyButton コンポーネントの追加

src/components/内にMyButton.vueという名前でオリジナルのボタンを作って見ます。
私はデザインはできないので、以下のサイト様からパク参考にさせて頂きました。
CSSで作る!押したくなるボタンデザイン100(Web用)

src/components/MyButton.vue
<template><ahref="#"class="btn-square"@click="change()">{{label}}</a></template><scriptlang="ts">importVuefrom'vue';exportdefaultVue.extend({name:'my-button',props:['label'],methods:{change(){this.$emit('label-update','changed');}}})</script><stylescoped>.btn-square{display:inline-block;padding:0.5em1em;text-decoration:none;background:#668ad8;/*ボタン色*/color:#FFF;border-bottom:solid4px#627295;border-radius:3px;}.btn-square:active{/*ボタンを押したとき*/-webkit-transform:translateY(4px);transform:translateY(4px);/*下に動く*/border-bottom:none;/*線を消す*/}</style>

styleタグ内に付いているscopedは、SFCファイル独自のもので、
記述すると定義されているCSSをこのコンポーネントのみに適用することができます。

また、Vueオブジェクト内のpropsは、コンポーネントの呼び出し側からデータを受け取ることのできるプロパティです。また、methods内のthis.$emitは、このコンポーネント呼び出すイベントを定義しています。
具体的には、ボタンが押下されると、label-updateイベントが起き、呼び出し元の親コンポーネントへ変更された値を通知します。
(Vueでは子コンポーネントがporpを勝手に書き換えることはNGとされており、このようにイベントとして親コンポーネントに通知を送り、親オブジェクトに変更をかけてもらうようにしています。)

MainPage.vueを以下のように変更し、結果を確認して見ます。

src/components/MainPage.vue
<template><div><h1>自作コンポーネントのページです</h1><h2>hello {{name}}</h2><!-- 追加   --><my-button:label="btnName"@label-update="updateName"/></div></template><scriptlang="ts">importVuefrom'vue';importMyButtonfrom"./MyButton.vue";// 追加exportdefaultVue.extend({name:'main-page',components:{MyButton},data(){return{name:'vue',// 好きな名前を入れてくださいbtnName:'Myボタン'// 追加}},// 追加methods:{updateName(val:string){this.btnName=val;}}});</script><style></style>

front_と_MainPage_vue_—_front.png

5. WebAPIの作成

express を使ってWebAPIを作成します。
vue-spa-sampleフォルダ内にbackフォルダを作り、その中でコマンドラインを立ち上げます。
以下のコマンドを順に打ち込んで行きます。
bat
npm init -y
npm i -S express cors body-parser

インストールが終わったら、vscodeでbackフォルダを開きます。
back/srcフォルダ内にrouterフォルダを作成し、その中にroot.jsファイルを作成します。

src/router/root.js
constexpress=require("express");constrouter=express.Router();router.get('/',(req/* リクエスト */,res/* レスポンス */)=>{// json でレスポンスres.send({message:'res from express'});});module.exports=router;

back/srcフォルダ内にapp.jsファイルを作成し、以下のコードを記述します。

src/app.js
constexpress=require("express");constbodyparser=require("body-parser");constcors=require("cors");constindexRouter=require("./router/root");constapp=express();// CORS 制限の解除app.use(cors());// 通信にJsonを使用するapp.use(bodyparser.json())// /にindexRouterをルーティングapp.use('/',indexRouter);module.exports=app;// appを公開

back/srcフォルダ内にindex.jsファイルを作成し、以下のコードを記述します。

src/index.js
constapp=require("./app");constport=3000;app.listen(port,()=>{// port listen を始めた時に実行される処理console.log(`express listen port ${port}`);});

全て保存したら、コマンドラインで以下のコマンドを実行

node ./src/index.js

サーバーが起動したら、ブラウザからhttp://localhost:3000にアクセスする。
以下のようになったらOK。

localhost_3000.png

6 WebアプリとWebAPI で通信

これまで作ってきたfront-end と back-end を連携して行きます。

front側のdev-serverをctrl + cで終了し、以下のコマンドを入力する

npm i -S axios

axiosがインストールされたら、再度

npm run server

でdev-server を起動して起きます。

axiosで通信する

src/components/MainPage.vueを以下のように修正します。

src/components/MainPage.vue
<template><div><h1>自作コンポーネントのページです</h1><h2>hello {{name}}</h2><my-button:label="btnName"@label-update="updateName"/><!-- 追加   --><div><p>{{message}}</p><button@click="getExpress()">get express root</button></div></div></template><scriptlang="ts">importVuefrom'vue';importMyButtonfrom"./MyButton.vue";import*asaxiosfrom"axios";exportdefaultVue.extend({name:'main-page',components:{MyButton},data(){return{name:'vue',// 好きな名前を入れてくださいbtnName:'Myボタン',message:'no message'// 追加}},methods:{updateName(val:string){this.btnName=val;},// 追加getExpress(){axios.default.get('http://localhost:3000').then((responce)=>{this.message=responce.data.message;});}}});</script><style></style>

http://localhost:8080にアクセスしてget express rootのボタンを推してみる。
no messageが res from expressに変わったら成功。localのexpressサーバーと通信できてます。

hangouts_google_com_が画面を共有しています。_と_front2.png

今後やって行くこと


Viewing all 8825 articles
Browse latest View live