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

シンプルなサーバーをDocker/Node.jsで構築してみた

$
0
0

はじめに

業務でログ監視システムの構築を行い、その際にログを出力するだけのシンプルなサーバーをDocker/Node.jsで作りました。
スタブ用途等ですぐに建てれる、サーバーの雛形としてお使い頂ければと思い、記事投稿に至りました。

前提

Docker / docker-composeを用いているので、インストールをお願いします。
インストール手順は、他の方がいっぱい書いておりますのでそちらをご参考に。

リポジトリ

下記のリポジトリになります。
simple_server

使い方

docker-compose builddocker-compose up等々は、
当リポジトリでは シェルスクリプトでラップしています。

ビルド手順

下記のシェルを実行し、イメージのビルドを行います。

/bin/sh build.sh

上記のシェルを実行すると、イメージの構築が出来ます。

% docker images
REPOSITORY                    TAG                 IMAGE ID            CREATED             SIZE
simple_server_simple-server   latest              45163213fabc        4 hours ago         110MB

実行手順

下記のシェルを実行し、コンテナの起動を行います。

/bin/sh run.sh

下記でコンテナが起動できたことを確認できます。
ポートはhostの 30000ポートを、 コンテナの 3000ポートにバインドしています。

% docker ps
CONTAINER ID        IMAGE                         COMMAND                  CREATED             STATUS              PORTS                     NAMES
fb6bc7a1a37e        simple_server_simple-server   "docker-entrypoint.s…"   7 seconds ago       Up 5 seconds        0.0.0.0:30000->3000/tcp   simple_server_simple-server_1

アクセス確認

ここまででサーバーを立てることができたので、アクセスの確認を行います。
現状はログファイルにログを出力するだけの簡単な仕組みですが、お好きなように変更頂ければと。

ルートURL

% curl http://localhost:30000/
Hello World

infoログ

% curl http://localhost:30000/info
info logging
% tail -n 1 ~/workspace/simple_server/simple-server/log/app.log
{"message":"info logging","level":"info"}

warnログ

% curl http://localhost:30000/warn
warn logging
% tail -n 1 ~/workspace/simple_server/simple-server/log/app.log
{"message":"warn logging","level":"warn"}

errorログ

% curl http://localhost:30000/error
error logging
% tail -n 1 ~/workspace/simple_server/simple-server/log/app.log
{"message":"error logging","level":"error"}

備考

リポジトリ内の docker-compose.ymlをご覧頂いたら分かるかと思いますが、
下記のように appディレクトリ配下、 logディレクトリ配下に関してはマウントしており、
起動したままホスト環境側で変更可能です。

docker-compose.yml
volumes:-./simple-server/app:/app-./simple-server/log:/var/log-/app/node_modules

最後に

dockerやっぱりめちゃくちゃ便利ですね。
作って捨てることが出来るのは嬉しいです。
ご自身のgithub等々にストックとして追加しておくことで、実案件時に助かることが多いのではないでしょうか。
雛形としてお使い頂けたら幸いです!


Create React Appで作成したReactアプリをGoogle App Engineにデプロイする際の設定

$
0
0

Create React Appで作成したReactアプリをGAEにデプロイする際に、少し設定ファイルの工夫が必要だったので共有します。

ざっくりした手順

  1. yarn run buildnpm run buildでリリースビルドを作成
  2. app.yamlでデプロイ時の設定を指定
  3. gcloud app deployでGAEにデプロイ

通常これらはCI上で行われるべきものですが、手元の開発環境でも実行できます。
(今回、手順3.の詳細は説明しません)

yarn buildでリリースビルドを作成

Create React Appで作成したReactアプリでは、buildコマンドを実行することで、簡単にリリースビルドを作成することができます。
リリースビルドの成果物はだいたい以下のような構造になっています。

build
├ static
  └ js
    ├ ~~~~.js
    ├ ...
    └ ~~~~.js
├ asset-manifest.json
├ favicon.ico
├ manifest.json
├ ...
└ service-worker.js

cssがある場合はstaticの下にcssディレクトリができるかもしれません(未確認)

app.yamlでデプロイ時の設定を指定

URLとリリースビルドの成果物のマッピングを行うため、handlersの設定が必要です。
設定ファイルを以下のようになります。

app.yaml
runtime:nodejs10instance_class:F1automatic_scaling:max_instances:1min_instances:1default_expiration:'30d'handlers:-url:/staticstatic_dir:build/static-url:/(.*\.(json|ico|js))$static_files:build/\1upload:build/.*\.(json|ico|js)$-url:.*static_files:build/index.htmlupload:build/index.html

handlersの設定では、

  • /staticにはリリースビルド成果物のbuild/staticマッピング
  • /(.*\.(json|ico|js))$にはリリースビルド成果物のbuild/.*\.(json|ico|js)$をマッピング
    • 正規表現を使っています(ここでは拡張子がjson, ico, jsのいずれかのファイルのみアップロードしてマッピングしています)
  • 上記以外のURLには、build/index.htmlをマッピングします
    • この設定がないと、アドレスバーから直接特定の子ページに移動する際、404エラーが発生する場合があります

なお、お試し用なので、マシンタイプをF1に、インスタンスをオートスケールしないように設定しています。
それがこの部分です。

instance_class: F1

automatic_scaling:
  max_instances: 1
  min_instances: 1

また、default_expirationではキャッシュを指定します(ここではキャッシュ期間を30日に指定しています)

gcloud app deployでGAEにデプロイ

後は、以下のコマンドを叩けば手元のbuildディレクトリ以下のファイルがGAEにデプロイされます。

gcloud app deploy

IBM Cloud FunctionsでAPIを作成する

$
0
0

はじめに

IBM Cloud Functions上に作成したActionを、APIとして呼び出す方法にはいくつかあります。
WEBアクションを有効にするのが一番お手軽ですが、指定出来ることが少なく、
APIゲートウェイを使用するの若干手間がかかりますが、より複雑な指定が可能になります。

以下では、それぞれの方法での作り方について、覚書としてまとめます。

サンプルのアクション

APIとして呼び出して実行するサンプルのアクションは次の通りです。
パラーメータとして渡されたnameに対して、Hello name!を返します。
nameの指定がなかった場合は、Hello Someone!を返します。

functionmain(params){letname=(params.name)?params.name:'Someone';return{message:'hello '+name+'!'};}

方法1.Webアクション

一番簡単な方法は、Webアクションを有効にすることです。
アクション名横の「Web Action」をクリックします。
web-action-enable.png

「Enable as Web Action」にチェックを入れて、Saveします。
web-action-enable2.png

下のURLがAPIの呼び出し先となります。
Webアクションが対応しているHTTPメソッドは、GET,POST,PUT,PATCH,DELETEになります。
注意:上記URLを呼び出しても上手くいかない場合は、末尾に.jsonを付けて呼び出して下さい。

呼び出し結果は次のようになります。

$ curl -X GET https://jp-tok.functions.cloud.ibm.com/api/v1/web/XXXXX/default/testAction.json
{"message": "hello Someone!"}
curl -X GET https://jp-tok.functions.cloud.ibm.com/ap/v1/web/XXXXX/default/testAction.json?name=Smith
{"message": "hello Smith!"}

この方法の欠点は、トークン認証の仕組みがないので、URLを知っていれば誰でも実行できてしまう点です。

方法2.IAMトークンを使ったREST API

IAMトークンを利用すれば、APIの実行に認証の仕組みを導入できます。
IBM CloudのIAMトークンの取得方法はいくつか存在しますが、ここでは、サービスIDを使います。
他の方法は、こちらの記事を参考にして下さい。
https://qiita.com/testnin2/items/3a4ca8a1a2b79b713c48

サービスIDのAPIキーの取得

「管理」>「アクセス(IAM)」>「サービスID」から、API呼び出ししたいアクションがデプロイされている、Cloud Functionsの名前空間を選択します。
「作成」ボタンが有効でない場合は、右上のメニューからロックを解除します。
unlock.png
unlock2.png

ここで、対象の名前空間のAPIキーを作成します。

IAMトークンの取得

サービスIDのAPIキーを取得したら、次のページを参考に、curlをコピペしてきて実行します。
https://cloud.ibm.com/docs/iam?topic=iam-iamtoken_from_apikey&locale=ja

curl -k-X POST \--header"Content-Type: application/x-www-form-urlencoded"\--header"Accept: application/json"\--data-urlencode"grant_type=urn:ibm:params:oauth:grant-type:apikey"\--data-urlencode"apikey=<apikey>"\"https://iam.cloud.ibm.com/identity/token"

次のようなjsonが帰ってくるので、access_tokenの値を使用します。

{"access_token":"XXXXX","refresh_token":"XXXXX","token_type":"Bearer"}

IAMトークンを使ってAPI実行

WebアクションのURLを確認したページに、同じくIAMを利用する場合のURLが記載されています。
基本的に記載のcurlをそのまま実行すれば、アクションが実行されます。
REST-Action.png
返ってきたjsonのresponseの中に実行結果が含まれています。

"response":{"result":{"message":"hello Someone!"},"size":28,"status":"success","success":true}

この方法の欠点は認証の仕組みがあるものの、IAMのトークンの有効期限が60分なので、再取得する必要がある点です。
参考:https://cloud.ibm.com/docs/iam?topic=iam-iamtoken_from_apikey&locale=ja
また、2019年11月現在、-dなどで、パラメータを渡しても上手く動作しないようです。
参考:https://stackoverflow.com/questions/56616014/how-to-fix-error-parameter-name-when-using-ibm-cloud-functions-rest-api

方法3.APIゲートウェイ

APIゲートウェイを使う方法は、これまでの方法よりも複雑な指定が可能になります。

  • APIのパスの指定
  • HTTPメソッドの指定
  • 複数のアクションを1つのAPIとしてラップ
  • レート制限の導入
  • APIキーと秘密鍵による認証
  • OAuthによる認証

など、他にもありますが、ここでは単純にAPIキーを発行して、外部ユーザーがIBM Cloud Functionのアクションを、APIとして実行出来る状態にします。

APIの作成

Cloud Functionsのトップから、「API」を選択して、「Creat Cloud Functions API」を選択します。
createAPI.png

API情報の設定

APIゲートウェイでは、WebアクションやIAMを使う方法よりも、多くのことが設定可能です。
ここでは、外部から実行するために、基本的な設定のみ行います。

API basics

APIの名前とAPIのベースのパスを指定します。
basics.png

Operations

呼び出すアクションと、呼び出し先のパス、呼び出す際のHTTPメソッドなどを指定します。
operation.png

複数のパス・メソッド・アクションを指定して、1つのAPIとしてラップすることもできます。
operation2.png

Application authentication

APIの認証の仕組みを指定します。認証なし・APIキーのみ・APIと秘密鍵の3種類が指定できます。
AppAuth.png

Rate limiting

レート制限の設定をします。ここでは、1秒間に1000回までとしています。
Rating.png

APIキーの取得

設定が済み、APIを作成したら、APIキーを取得します。(最大5つまで作成可能です。)
「共有」から「Sharing Outside of Cloud Foundry organization」から外部ユーザー用のAPIキーを取得します。
outside-key.png
API Portal Linkから、このAPIの簡単なドキュメントが参照できます。
ただし、2019年11月現在IAMの名前空間では利用できません。
補足のCF-basedの名前空間の作成方法を参考にして下さい。

(参考)
APIの認証設定で秘密鍵を指定した場合は、内部ユーザー向けの「Sharing within Cloud Foundry organization」から秘密鍵が取得できます。
inside-key.png

APIの実行

APIキーを取得したら、Application authenticationの欄にしたがって、ヘッダーにAPIキーを入れて、Operationsの通り、HTTPメソッドを実行します。
ルートのURLは、「サマリー」から確認できます。
API-Route.png

上記で設定した情報は次の通りでした。

セクション設定項目設定値
Operationsパス/test
OperationsHTTPメソッドGET
Application authenticationAPIキーパラメータ名X-IBM-Client-Id

したがって、次のようなcurlで実行し、次のような実行結果となります。

$ curl -X GET https://XXXXX.jp-tok.apigw.appdomain.cloud/API-TEST/test \-H'X-IBM-Client-Id:XXXXX'{"message": "hello Someone!"}$ curl -X GET https://XXXXX.jp-tok.apigw.appdomain.coud/API-TEST/test?name=Smith \-H'X-IBM-Client-Id:XXXXX'{"message": "hello Smith!"}

注意:上手く実行されない場合はWEBアクションを有効にしてみて下さい。

[補足]CF-basedの名前空間の作成

「管理」>「アカウント」から「Cloud Foundryの組織」を選びます。
既に存在するCloud Foundryの組織を選ぶか、「作成」から新規作成を行います。
CF-org.png
「スペースの追加」から新しく名前空間を作ります。
add-space.png
注意:シドニーはIBM Cloud Functionsでは使用できないので、米国南部(ダラス)などを選んで下さい。

Puppeteerで作ったスクリプトを実行可能ファイル化する

$
0
0

TL; DR

  • Node.jsを実行可能ファイルに変換するのにPkgを使用した。
  • puppeteer.launch()のオプションにexecutablePath: /Applications/Google Chrome.app/Contents/MacOS/Google Chromeを指定する。要は普段使っているであろうGoogle Chromeのパスに変更してあげる必要がある。
  • 共有先のPCによっては実行権限が無かったりするので、chmodで付与してあげる。

課題

  • ブラウザ上で行っていた管理者限定の単純作業を自動化したい。

やったこと

  • ブラウザ操作はPuppetterでやる。ログイン情報とかはscriptの実行時に尋ねるようにする。今回はenquirerを使った。
  • 自分の権限的に見れない画面だったので、保存したHTMLファイルを貰って、それをローカルサーバーで開き、Puppetterの自動入力の実装をした。
  • Puppeteerで作ったはいいが環境設定の手間をかんがえると、repositryを共有して yarn installyarn startみたいに実行させるわけにもいかなそう。
  • とりあえず動作確認したいだけなので、そのためだけにGUIアプリまで作るのだるい。
  • PkgというNode.jsを実行可能ファイル化してくれる君を見つけたのでそれを使うことにした。
  • いざ、作って共有!実行してもらったところ、command not foundと怒られてよくよく見ると、ファイルに実行権限がなかったので、chmod 755 ./コマンド名で実行可能にしてあげる。

感想

headless: falseにした時の自動で動いてる感、パソコンハックされてる感あって面白い。
XPath使ったことなかったけど、柔軟にDOMにアクセスできるの便利だった。XPath HelperというXPathを入力すると該当の要素をハイライトしてくれるChrome拡張があるのでおススメ。

OpenShiftによるJava EEアプリケーションのモダナイゼーションをやってみた(2)

$
0
0

この投稿の前提

この投稿はQiitaの別投稿「OpenShiftによるJava EEアプリケーションのモダナイゼーションをやってみた。」で作成した、ビジネスロジック用のOpen Libertyで実行されているJava EEアプリケーションに加えて、今回で患者向けUIのNode.jsアプリケーションをMiniShiftに作成して、結合するのが今回の範囲になります。
minishift-2.png

今回作成する患者向けUIのNode.jsアプリケーションはモックモードで動作するので、この記事だけで試せますが、ユースケースは別記事に書いてあるので、OpenShiftによるJava EEアプリケーションのモダナイゼーションをやってみた も軽くみてもらえると励みになります。

はじめに

IBM CloudのOpenShiftのマネージドサービスである「 Red Hat OpenShift on IBM Cloud 」を使った「OpenShiftによるJava EEアプリケーションのモダナイゼーション」の記事を寄稿しました。
https://www.atmarkit.co.jp/ait/articles/1911/15/news002.html

その記事で使用している「 Red Hat OpenShift on IBM Cloud 」は有料サービスのため、たくさんの人に記事の手順を試してもらうのは難しいと思います。 そこで、無料で利用できるMiniShiftを使って、気軽にOpenShiftでJava EEアプリケーションのモダナイゼーションを体感してもらえるように、リライトしました。

患者用UIのモダナイゼーション

患者用UI(ユーザーインターフェイス)のモダナイゼーションは、「 Node.js、Source-to-Image ツールキット、OpenShift によるアプリケーションのモダナイズ ーKubernetes ベースのマイクロサービスで従来型の患者記録アプリを変換する」というコードパターンを使います。UIは、オープン・スタンダードなJavaScriptと最新のユニバーサルCSS、およびレイアウト用のHTML5 Canvasでプログラムされています。

早速、患者用UIを作り始めましょう。コードパターンの手順はGUI操作です。ただ、今回私達はソリューションアーキテクトですので今回はCLI操作でGUIと同じ手順を再現してみましょう。

1.まず、コードパターンのGitHubのリポジトリ「 IBM/node-s2i-openshift」で「 Fork 」ボタンをクリックして自分のGitHubのリポジトリにフォークします。 GitHubのアカウントをお持ちでない方は事前に作成してください。
git-clone-1.png

2.Red Hat UBI(RHEL7)のNode.js 10コンテナイメージをopenshiftプロジェクトにインポートします。コンテナイメージをプロジェクト間で共有するユースケースではopenshiftプロジェクトにコンテナイメージをインポートします。

openshiftプロジェクトにインポートするためには管理者権限が必要なのでsystem:adminでログインしてからインポートします。インポート終了後、developerユーザーにスイッチします。

# oc login -u system:admin
# oc import-image ubi7/nodejs-10 --from=registry.access.redhat.com/ubi7/nodejs- 10 \
-n openshift --confirm
# oc login -u developer

注: デフォルトで用意されてるカタログのベースイメージがCentOSのため、本番環境での使用を意識してRed Hat UBIを使用します。この記事の掲載後、何かしらの理由でインポートできない場合はこの手順はスキップしてください。この後の手順でCentOSを使用する場合の手順を用意しています。

3.コードパターンでは新しいプロジェクトを作成していますが、ここでは前の手順で作成したExample Health用のプロジェクトを使います。healthプロジェクトに移動します。

# oc project health

4.インポートの結果を確認します。OpenShiftプロジェクトのImageStream(is)の一覧を出力します。

# oc get is -n openshift
NAME         DOCKER REPO                            TAGS                           UPDATED
dotnet       172.30.1.1:5000/openshift/dotnet       2.0,latest                     31 hours ago
httpd        172.30.1.1:5000/openshift/httpd        2.4,latest                     31 hours ago
jenkins      172.30.1.1:5000/openshift/jenkins      1,2,latest                     31 hours ago
mariadb      172.30.1.1:5000/openshift/mariadb      10.1,10.2,latest               31 hours ago
mongodb      172.30.1.1:5000/openshift/mongodb      latest,2.4,2.6 + 3 more...     31 hours ago
mysql        172.30.1.1:5000/openshift/mysql        5.5,5.6,5.7 + 1 more...        31 hours ago
nginx        172.30.1.1:5000/openshift/nginx        1.10,1.12,1.8 + 1 more...      31 hours ago
nodejs       172.30.1.1:5000/openshift/nodejs       0.10,10,4 + 4 more...          31 hours ago
nodejs-10    172.30.1.1:5000/openshift/nodejs-10    latest                         5 hours ago
perl         172.30.1.1:5000/openshift/perl         5.24,5.26,latest + 2 more...   31 hours ago
php          172.30.1.1:5000/openshift/php          5.5,5.6,7.0 + 2 more...        31 hours ago
postgresql   172.30.1.1:5000/openshift/postgresql   9.6,latest,10 + 3 more...      31 hours ago
python       172.30.1.1:5000/openshift/python       3.6,latest,2.7 + 3 more...     31 hours ago
redis        172.30.1.1:5000/openshift/redis        latest,3.2                     31 hours ago
ruby         172.30.1.1:5000/openshift/ruby         2.5,latest,2.0 + 3 more...     31 hours ago
wildfly      172.30.1.1:5000/openshift/wildfly      10.0,10.1,11.0 + 5 more...     31 hours ago

nodejs-10が今回インポートしたコンテナイメージです。一覧で表示されるnodejsやdotnetは、下記画像のWebコンソールのカタログと同じです。OpenShiftはCLIでも簡単にアプリケーションをデプロイする仕組みを用意しています。

s2i-1.png

5.OpenShiftのS2I(Source to Image)を使って患者UI用のアプリケーションをビルド・デプロイします。

  • 実行するコマンド
# oc new-app openshift/nodejs-10~https://github.com/daihiraoka/node-s2i-openshift.git \
--name='patientui' --context-dir='/site'

openshift/nodejs-10はベースとなるコンテナイメージのImageStream名(openshiftプロジェクトのnodejs-10)、~(チルダ)で挟んで右側が、ソースコードリポジトリ、--name=生成するアプリケーションの名前、 --context-dirはソースコードリポジトリのサブディレクトリを指定しています。S2I(Source to Image)はベースとなるコンテナイメージとソースコード使って新しいコンテナイメージを生成するRed Hat独自のビルド方式です。

注: Red Hat UBIがインポートできなかった場合は、openshift/nodejs-10をopenshift/nodejs:10に変更してCentOS7のnodejsを使用してください。

実行結果は以下の様に出力されます。

--> Found image fca7ff7 (3 weeks old) in image stream "openshift/nodejs-10" under tag "latest" for "openshift/nodejs-10"

    * A source build using source code from https://github.com/daihiraoka/node-s2i-openshift.git will be created
      * The resulting image will be pushed to image stream tag "patientui:latest"
      * Use 'start-build' to trigger a new build
    * This image will be deployed in deployment config "patientui"
    * Port 8080/tcp will be load balanced by service "patientui"
      * Other containers can access this service through the hostname "patientui"

--> Creating resources ...
    imagestream.image.openshift.io "patientui" created
    buildconfig.build.openshift.io "patientui" created
    deploymentconfig.apps.openshift.io "patientui" created
    service "patientui" created
--> Success
    Build scheduled, use 'oc logs -f bc/patientui' to track its progress.
    Application is not exposed. You can expose services to the outside world by executing one or more of the commands below:
     'oc expose svc/patientui' 
    Run 'oc status' to view your app.

oc new-appコマンドを実行すると、patientuiという名称でこれから作成されるコンテナイメージを管理するためのImageStream、ビルドするためのBuildConfig、デプロイするためのDeploymentConfig、IPで通信するためのServiceが作成されます。そして、BuildConfigはBuildConfigの作成をトリガーにコンテナのビルドが自動で始まり、ビルドが完了(DockerレジストリにPush)するとpatientuiのImageStreamにバージョン履歴が追加されます。するとDeploymentConfigはImageStreamのバージョン履歴の変更を検出して、自動的にPod(コンテナ)のデプロイを開始し、アプリケーションが利用できる様になります。

このようにOpenShiftはS2I(Source-to-Image)、ImangeStreamを使って自動ビルド・デプロイを標準で利用できます。

しばらくするとpatientui-1-<英数字>のSTATUSがRunningに変わります。

# oc get pod
NAME                READY     STATUS      RESTARTS   AGE
patientui-1-build   0/1       Completed   0          2m
patientui-1-lpql7   1/1       Running     0          5m

6.患者用UIアプリケーションをインターネットに公開するためのRouteを作成します。

# oc expose svc/patientui
route.route.openshift.io/patientui exposed

# oc get route
NAME       HOST/PORT                              PATH  SERVICES  PORT TERMINATION WILDCARD
patientui  patientui-health.192.168.42.218.nip.io       patientui 8080-tcp         None

7.ブラウザでホスト名をアクセスします。

patient-ui-1.png

デフォルトではモックモードで動作しています。nameとpassword共にtestを入力するとログインすることができます。
ここでは、患者UIとビジネスロジックを結合するために、「 settings 」をクリックして画面遷移します。

8.赤枠に、ビジネスロジック用のJava EEアプリケーションのAPIのURLを入力して、「 java 」をクリックします。

URL: http://example-health-api-health.192.168.42.218.nip.io/resources/v1/

patient-ui-0.png

9.ログイン情報を確認するために、healthデーターベースのPatientsテーブルからuser_idとpasswodを取得します。

  • まず mysqlのPod名を取得します。
# oc get pod
NAME            READY     STATUS      RESTARTS   AGE
mysql-1-build   0/1       Completed   0          3m
mysql-1-hktv2   1/1       Running     0          2m
  • Pod名を使ってmysqlのPodにrshして、mysqlコマンドを実行します。
# oc rsh mysql-1-hktv2
sh-4.2$ mysql -u admin -ppassword health -e"select first_name,last_name,birthdate,user_id,password from Patients;"+------------+-----------+------------+------------+------------+
| first_name | last_name | birthdate  | user_id    | password   |
+------------+-----------+------------+------------+------------+
| Clyde      | Ferry     | 1949-03-19 | clydef     | clydef     |
| Lawerence  | Pollich   | 2003-10-18 | lawerencep | lawerencep |
| Opal       | Larkin    | 1999-04-21 | opall      | opall      |
| Tresa      | Green     | 1980-08-30 | tresag     | tresag     |
| Stanley    | Kris      | 1987-11-06 | stanleyk   | stanleyk   |
| Karri      | McDermott | 2011-05-01 | karrim     | karrim     |
| Janise     | Metz      | 1963-09-16 | janisem    | janisem    |
| Alphonse   | Hand      | 2009-04-15 | alphonseh  | alphonseh  |
| Pedro      | Perea     | 1993-09-13 | pedrop     | pedrop     |
| Octavio    | Mante     | 1951-02-28 | octaviom   | octaviom   |
+------------+-----------+------------+------------+------------+

10.Clydeさんの患者情報にログインするためにnameとpasswordに「 clydef 」を入力して「 Sign In 」ボタンをクリックします。

patient-ui-2.png

11.ログインするとClydeさんの個人情報、予約履歴、処方記録が表示されます。

patient-ui-3.png

これで患者用UIのNode.jsアプリケーションとビジネスロジック用のJava EEアプリケーションの結合ができました。

Java EEアプリケーションのモダナイゼーションの旅の終わり

そろそろJava EEアプリケーションのモダナイゼーションの旅も終わりです。

この旅では

  • モノリシックなJava EEアプリケーションをアプリケーション分割し、OpenShiftでマイクロサービスとして動作する方法
  • OpenShift の Source to Image (S2I) を使ってデプロイする方法

について、2つのコード・パターンを使ってモダナイゼーションを体感しました。次は、架空の会社ではなく皆さんの旅を始める時です。

今後の展開

OpenShiftに移行した結果、Example Healthは容易に機能拡張できるようになりました。 その後、Example Healthはシステムを拡大して新しいマイクロサービスを追加していきます。その1つが健康記録管理者用のPHPアプリケーションのCode Patternsです。

健康記録管理者用のPHPアプリケーションの話は「OpenShiftによるJava EEアプリケーションのモダナイゼーションをやってみた(3)」に続きます。

Node.jsで簡易的なHTTPサーバーを起動 / sleepと組み合わせ

$
0
0

Node.jsのバージョンはv12.13.1です。

server.js
consthttp=require('http')server=http.createServer((req,res)=>{res.writeHead(200,{'Content-Type':'text/plain'})res.end('Hello World')})server.listen(8080)

起動。

node server.js

sleepと組み合わせる。(デバッグ用)

consthttp=require('http')SLEEP_MSEC=3000server=http.createServer((req,res)=>{setTimeout(()=>{res.writeHead(200,{'Content-Type':'text/plain'})res.end('Hello World')},SLEEP_MSEC)})server.listen(8080)

参考:https://gist.github.com/leozc/4667579

WSLのUbuntu環境でyoutube音楽をWeb操作する(おまけでradikoとサイマルラジオ)

$
0
0

はじめに

●youtube音楽をWEBで操作し、PCスピーカで再生できるようにする。
(Googlehomeで声で操作するのWeb操作版です)
【操作例】WEBから「クリスマスイブ」と入力する
WEB.png

環境

●Windows10 HOMEのPCにWSLのubuntuをインストールする。
●ubuntuでapache2,npm,node.js,youtube-dl,mplayer,mpvをインストールする
●WSLのubuntuでpulseaudioでPCのスピーカを使うようにする
●Windows側でpulseaudioサーバのインストールが必要です
 https://www.cendio.com/thinlinc/downloadの 「Client Bundle」のリンクからダウンロードできます
●radikoを再生するには、その環境構築(radiko.sh)が必要です。

youtube音楽を再生するプログラム(ytplay.js)

●単体で起動する方法
$node ytplay.js 検索キー(例 クリスマスイブ)
●youtubeのKeyは自分のKeyに置き換えてください

const exec   = require('child_process').exec;
const Youtube = require('youtube-node');
const youtube = new Youtube();

var limit = 1;
var items;
var item;
var title;
var id;

// 取得したAPIキーを入力する。
youtube.setKey('XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX');
youtube.addParam('order', 'viewCount');
youtube.addParam('type', 'video');
youtube.addParam('regionCode', 'JP');

//youtube検索文字取得
const value = process.argv[2];
if (!value) return
console.log(value)

// キーワードから検索し、動画IDやタイトルを取得する。
play_youtube(value);
// 音楽ファイルのURLを取得し、再生する。
function play_youtube(keyword) {
  youtube.search(keyword, 1, {'type':'video','videoCategoryId':10} , function(error, result) {
    if (error) {
      console.log(error);
      return ;
    }
    for (const item of result.items) {
      if (item.id.videoId) {
        var exec = require('child_process').exec;
        command = "youtube-dl 'https://www.youtube.com/watch?v="+item.id.videoId+"' -o - | mplayer - -novideo"
        console.log(command);
        exec(command,  {maxBuffer: 40000*1024},function(error, stdout, stderr) {
           if (error !== null) {
            console.log('exec error: '+error);
          }
        });
      }
    }
  });
}

index.php

<?php
if(isset($_GET['comment'])){
$comment = $_GET['comment'];
exec("killall mplayer");
exec("killall mpv");
exec("node /home/ユーザ名/ytplay.js " .$comment);
}
if(isset($_GET['id'])){
$id = $_GET['id'];
if($id=="stop"){
exec("killall mplayer");
exec("killall mpv");
}elseif($id=="TBS"){
exec("killall mplayer");
exec("killall mpv");
exec("/home/ユーザ名/radiko.sh -p " .$id. " > /dev/null &");
}elseif($id=="QRR"){
exec("killall mplayer");
exec("killall mpv");
exec("/home/ユーザ名/radiko.sh -p " .$id. " > /dev/null &");
}elseif($id=="LFR"){
exec("killall mplayer");
exec("killall mpv");
exec("/home/ユーザ名/radiko.sh -p " .$id. " > /dev/null &");
}elseif($id=="INT"){
exec("killall mplayer");
exec("killall mpv");
exec("/home/ユーザ名/radiko.sh -p " .$id. " > /dev/null &");
}elseif($id=="FMT"){
exec("killall mplayer");
exec("killall mpv");
exec("/home/ユーザ名/radiko.sh -p " .$id. " > /dev/null &");
}elseif($id=="FMJ"){
exec("killall mplayer");
exec("killall mpv");
exec("/home/ユーザ名/radiko.sh -p " .$id. " > /dev/null &");
}elseif($id=="JORF"){
exec("killall mplayer");
exec("killall mpv");
exec("/home/ユーザ名/radiko.sh -p " .$id. " > /dev/null &");
}elseif($id=="BAYFM78"){
exec("killall mplayer");
exec("killall mpv");
exec("/home/ユーザ名/radiko.sh -p " .$id. " > /dev/null &");
}elseif($id=="RN1"){
exec("killall mplayer");
exec("killall mpv");
exec("/home/ユーザ名/radiko.sh -p " .$id. " > /dev/null &");
}elseif($id=="RN2"){
exec("killall mplayer");
exec("killall mpv");
exec("/home/ユーザ名/radiko.sh -p " .$id. " > /dev/null &");
}elseif($id=="HOUSOU-DAIGAKU"){
exec("killall mplayer");
exec("killall mpv");
exec("/home/ユーザ名/radiko.sh -p " .$id. " > /dev/null &");
}elseif($id=="https://nhkradioakr1-i.akamaihd.net/hls/live/511633/1-r1/1-r1-01.m3u8"){
exec("killall mplayer");
exec("killall mpv");
exec("mpv " .$id. " > /dev/null &");
}elseif($id=="https://nhkradioakr2-i.akamaihd.net/hls/live/511929/1-r2/1-r2-01.m3u8"){
exec("killall mplayer");
exec("killall mpv");
exec("mpv " .$id. " > /dev/null &");
}elseif($id=="https://nhkradioakfm-i.akamaihd.net/hls/live/512290/1-fm/1-fm-01.m3u8"){
exec("killall mplayer");
exec("killall mpv");
exec("mpv " .$id. " > /dev/null &");
}elseif($id=="https://stream.backstagecafe.jp/live/_definst_/rVFqYTFQ053A/chunklist.m3u8"){
exec("killall mplayer");
exec("killall mpv");
exec("mpv -vo null " .$id. " > /dev/null &");
}elseif($id=="http://sky1.torontocast.com:9041/stream"){
exec("killall mplayer");
exec("killall mpv");
exec("mpv -vo null " .$id. " > /dev/null &");
}elseif($id=="https://ottava-live.webcdn.stream.ne.jp/hls-live/mvqj18ky8u87ske8/playlist.m3u8"){
exec("killall mplayer");
exec("killall mpv");
exec("mpv -vo null " .$id. " > /dev/null &");
}else{
exec("killall mplayer");
exec("killall mpv");
exec("mplayer -playlist " .$id. " > /dev/null &");
}
}
?>
<html>
<head>
<meta name="viewport" content="width=device-width">
</head>
<body>
<p><a href="index.php?id=stop">stop</a></p>
<form action="index.php" method="get">
<input type="text" name=comment>
<input type="submit" value="youtube">
</form>
<ul>
<li><a href="index.php?id=TBS">TBSラジオ</a></li>
<li><a href="index.php?id=QRR">文化放送</a></li>
<li><a href="index.php?id=LFR">ニッポン放送</a></li>
<li><a href="index.php?id=INT">InterFM897</a></li>
<li><a href="index.php?id=FMT">TOKYO FM</a></li>
<li><a href="index.php?id=FMJ">J-WAVE</a></li>
<li><a href="index.php?id=JORF">ラジオ日本</a></li>
<li><a href="index.php?id=BAYFM78">bayfm78</a></li>
<li><a href="index.php?id=RN1">ラジオNIKKEI第1</a></li>
<li><a href="index.php?id=RN2">ラジオNIKKEI第2</a></li>
<li><a href="index.php?id=HOUSOU-DAIGAKU">放送大学</a></li>
<li><a href="index.php?id=https://nhkradioakr1-i.akamaihd.net/hls/live/511633/1-r1/1-r1-01.m3u8">NHK 第一</a></li>
<li><a href="index.php?id=https://nhkradioakr2-i.akamaihd.net/hls/live/511929/1-r2/1-r2-01.m3u8">NHK 第二</a></li>
<li><a href="index.php?id=https://nhkradioakfm-i.akamaihd.net/hls/live/512290/1-fm/1-fm-01.m3u8">NHK FM</a></li>
<li><a href="index.php?id=https://stream.backstagecafe.jp/live/_definst_/rVFqYTFQ053A/chunklist.m3u8">Backstage Cafe</a></li>
<li><a href="index.php?id=http://sky1.torontocast.com:9041/stream">J1 Radio(J-POP聴き放題チャンネル)</a></li>
<li><a href="index.php?id=https://ottava-live.webcdn.stream.ne.jp/hls-live/mvqj18ky8u87ske8/playlist.m3u8">OTTAVA(クラ シック)</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30044/livestream/playlist.m3u8">FM WING</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30060/livestream/playlist.m3u8">ラジオニセコ</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30005/livestream/playlist.m3u8">三角山放送局</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30016/livestream/playlist.m3u8">FM JAGA</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30047/livestream/playlist.m3u8">FMいるか</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30074/livestream/playlist.m3u8">FMりべーる</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30079/livestream/playlist.m3u8">Be FM</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30050/livestream/playlist.m3u8">カシオペアFM</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30097/livestream/playlist.m3u8">みやこハーバーラジオ</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30007/livestream/playlist.m3u8">RADIO3</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30037/livestream/playlist.m3u8">ラジオ気仙沼</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30018/livestream/playlist.m3u8">FMいずみ</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30092/livestream/playlist.m3u8">なとらじ801</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30030/livestream/playlist.m3u8">FMゆーとぴあ</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30076/livestream/playlist.m3u8">横手かまくらFM</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30089/livestream/playlist.m3u8">鹿角きりたんぽFM</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30014/livestream/playlist.m3u8">エフエム椿台</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30019/livestream/playlist.m3u8">FM-MOTCom</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30020/livestream/playlist.m3u8">KOCOラジ</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30092/livestream/playlist.m3u8">南相馬ひばりFM</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30023/livestream/playlist.m3u8">FMひたち</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30043/livestream/playlist.m3u8">まえばしCITYエフエム</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30026/livestream/playlist.m3u8">すまいるエフエム</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30002/livestream/playlist.m3u8">フラワーラジオ</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30035/livestream/playlist.m3u8">FM Kawaguchi</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30096/livestream/playlist.m3u8">ハローハッピー・こしがやエフエム</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30008/livestream/playlist.m3u8">REDS WAVE</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30081/livestream/playlist.m3u8">八王子FM</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30036/livestream/playlist.m3u8">レインボータウンFM</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30063/livestream/playlist.m3u8">レディオ湘南</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30080/livestream/playlist.m3u8">FMHOT893</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30057/livestream/playlist.m3u8">FMカオン</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30006/livestream/playlist.m3u8">ラジオ・ミュー</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30001/livestream/playlist.m3u8">FM N1</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30012/livestream/playlist.m3u8">敦賀FM</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30086/livestream/playlist.m3u8">あづみ野エフエム放送</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30032/livestream/playlist.m3u8">FM軽井沢</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30062/livestream/playlist.m3u8">Clao!</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30004/livestream/playlist.m3u8">MD-FM</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30040/livestream/playlist.m3u8">FMおかざき</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30065/livestream/playlist.m3u8">Pitch FM</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30049/livestream/playlist.m3u8">エフエムひらかた</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30013/livestream/playlist.m3u8">FMジャングル</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30067/livestream/playlist.m3u8">FM aiai</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30084/livestream/playlist.m3u8">ビーチステーション</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30051/livestream/playlist.m3u8">FM TANABE</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30053/livestream/playlist.m3u8">DARAZ FM</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30010/livestream/playlist.m3u8">エフエムびざん</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30070/livestream/playlist.m3u8">FM SUN</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30024/livestream/playlist.m3u8">FM815</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30054/livestream/playlist.m3u8">あまみエフエム</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30069/livestream/playlist.m3u8">FMいしがきサンサンラジオ</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30055/livestream/playlist.m3u8">FMなんじょう</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30003/livestream/playlist.m3u8">FMニライ</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30093/livestream/playlist.m3u8">FMぎのわん</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30098/livestream/playlist.m3u8">ぎのわんシティFM</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30083/livestream/playlist.m3u8">FMとよみ</a></li>
<li><a href="index.php?id=http://mtist.as.smartstream.ne.jp/30011/livestream/playlist.m3u8">FMうるま</a></li>
<li><a href="index.php?id=http://www.simulradio.info/asx/kawasaki.asx">かわさきFM</a></li>
<li><a href="index.php?id=http://www.simulradio.info/asx/fmpalulun.asx">FMぱるるん</a></li>
<li><a href="index.php?id=http://www.simulradio.info/asx/banban.asx">BAN-BANラジオ 加古川市</a></li>
<li><a href="index.php?id=http://www.simulradio.info/asx/fm-tachikawa.asx">エフエムたちかわ</a></li>
<li><a href="index.php?id=http://www.simulradio.info/asx/totsuka.asx">FM戸塚</a></li>
<li><a href="index.php?id=http://www.simulradio.info/asx/chofu_fm.asx">調布FM</a></li>
<li><a href="index.php?id=http://www.simulradio.info/asx/fm-iwaki.asx">FMいわき</a></li>
<li><a href="index.php?id=http://www.simulradio.info/asx/FmYutopia.asx">FMゆーとぴあ</a></li>
<li><a href="index.php?id=http://www.simulradio.info/asx/fmmoov.asx">FM MOOV KOBE</a></li>
<li><a href="index.php?id=http://www.simulradio.info/asx/lequio.asx">FMレキオ</a></li>
<li><a href="index.php?id=http://www.simulradio.info/asx/yes-fm.asx">YES-fm 大阪府</a></li>
<li><a href="index.php?id=http://www.simulradio.info/asx/radiocafe.asx">京都三条ラジオカフェ</a></li>
<li><a href="index.php?id=http://www.simulradio.info/asx/tsukuba.asx">ラヂオつくば</a></li>
<li><a href="index.php?id=http://www.simulradio.info/asx/fmapple.asx">FMアップル</a></li>
<li><a href="index.php?id=http://www.simulradio.info/asx/fmIzumi.asx">fmいずみ 仙台</a></li>
<li><a href="index.php?id=http://www.simulradio.info/asx/FmKushiro.asx">FMくしろ</a></li>
<li><a href="index.php?id=http://www.simulradio.info/asx/takahagi.asx">たかはぎFM</a></li>
<li><a href="index.php?id=http://www.simulradio.info/asx/chuo_fm.asx">中央エフエム</a></li>
<li><a href="index.php?id=http://www.simulradio.info/asx/kawaguchi.asx">FM Kawaguchi 川口市</a></li>
<li><a href="index.php?id=http://www.simulradio.info/asx/mid-fm761.asx">MID-FM 名古屋市中区</a></li>
<li><a href="index.php?id=http://www.simulradio.info/asx/radiomyu.asx">ラジオ・ミュー 黒部市</a></li>
<li><a href="index.php?id=http://www.simulradio.info/asx/jungle.asx">FMジャングル 豊岡市</a></li>
<li><a href="index.php?id=http://csra.fm/asx/taihaku.asx">FMたいはく 仙台</a></li>
<li><a href="index.php?id=http://csra.fm/asx/katsushika.asx">かつしかFM</a></li>
<li><a href="index.php?id=http://csra.fm/asx/fmuu.asx">FM-UU 牛久市</a></li>
<li><a href="index.php?id=http://csra.fm/asx/toyota.asx">RADIO LOVEAT 豊田市</a></li>
<li><a href="index.php?id=http://csra.fm/asx/suzuka.asx">Suzuka Voice FM 鈴鹿市</a></li>
<li><a href="index.php?id=http://www.darazfm.com/streaming.asx">DARAZ FM 米子市</a></li>
<li><a href="index.php?id=http://www.npo-d.org/simul/AmamiFM.asx">あまみFM 奄美市</a></li>
<li><a href="index.php?id=https://musicbird-hls.leanstream.co/musicbird/JCB075.stream/playlist.m3u8?args=web_03">BananaFM 和歌山県</a></li>
<li><a href="index.php?id=http://yp.shoutcast.com/sbin/tunein-station.m3u?id=1864012">Jazz Sakura</a></li>
<li><a href="index.php?id=http://yp.shoutcast.com/sbin/tunein-station.m3u?id=1586446">J-Pop Sakura 17</a></li>
<li><a href="index.php?id=http://yp.shoutcast.com/sbin/tunein-station.m3u?id=1656313">J-Pop - PowerPlay</a></li>
<li><a href="index.php?id=http://yp.shoutcast.com/sbin/tunein-station.m3u?id=1659345">Asia DREAM Radio - Japan Hits</a></li>
<li><a href="index.php?id=http://yp.shoutcast.com/sbin/tunein-station.m3u?id=1634740">.113FM Korean Pop</a></li>
<li><a href="index.php?id=http://yp.shoutcast.com/sbin/tunein-station.m3u?id=1738454">Radio 2.0 - Valli di Bergamo</a></li>
<li><a href="index.php?id=http://yp.shoutcast.com/sbin/tunein-station.m3u?id=1749428">RADIO CLASIC MOZART</a></li>
</ul>
</body>
</html>

まとめ

●お気に入りのPCスピーカでyoutubue音楽が流せます
●プログラムはラズパイでも同様に動作しますが、youtube-dlの動作が遅く、再生開始に少々時間がかかります。

puppeteerを用いた要素の抽出(page.$())

$
0
0

nokogiriの場合はとりあえず取ってくるため、取ること自体に難しさは無いが、puppeteerの場合はメインはサイト上の処理を自動化するのが目的であるため、データを取り出す場合は取り出す処理を書く必要がある。

とりあえずこんな感じでやればできる

constselector='ul > li > a';constelementHandle=awaitpage.$(selector);constvalue=await(awaitelementHandle.getProperty('textContent')).jsonValue();

page.$(selector)とは

puppeteerのgithub(pageselectorの説明部分)
https://github.com/GoogleChrome/puppeteer/blob/v2.0.0/docs/api.md#pageselector

elementHandle.$(selector)
- selector<string> A selector to query element for
- returns: <Promise<?ElementHandle>>

こんな感じで、returnはPromiseのElementHandleとなっている。

補足説明

ElementHandle

ElementHandle represents an in-page DOM element. ElementHandles can be created with the page.$ method.

ページ内の要素をDOMで形成する。page.$を使って作ることができる。

DOM

JavaScript初心者でもすぐわかる!DOMとは何か?

DOMとは「Document Object Model」の略だ。直訳すると、「ドキュメントを物として扱うモデル」になる。プログラムからHTMLやXMLを自由に操作するための仕組みだ。

階層構造になっているモデルのこと。

つまりElementHandleとは、ページ内の要素をDOMとして形成するもの。

getProperty('textContent')

puppeteerのgithub(getPropertyの説明部分)
https://github.com/GoogleChrome/puppeteer/blob/v2.0.0/docs/api.md#elementhandlegetpropertypropertyname

これは、形成したElementHandleの中から、必要なものを取り出すための処理のこと。
色々オブジェクトはあるけれど、サイト内の文字列を取り出したい場合は、textContentを指定すれば良い。

結論

page.$でサイト内の要素をDOMとして取得して、getPropertyでDOMの中のプロパティ名(サイトの文字列の場合はtextContent)を指定してあげれば、サイト内の任意の文字を取り出すことができる。

参考

puppeteerのgithub
https://github.com/puppeteer/puppeteer/blob/v2.0.0/docs/api.md

JavaScript初心者でもすぐわかる!DOMとは何か?


Mocha, Chaiを使ったテストの表記パターン

$
0
0

TypeScriptでMocha, Chaiを使ったテスト駆動開発

インストール

TDDをサポートしてくれるパッケージのインストール。
$ npm install chai mocha ts-node @types/chai @types/mocha --save-dev

参考: Testing TypeScript with Mocha and Chai

しかし、私の場合なぜかTypeScriptをグローバルインストールしているにも関わらず、テスト実行時に「typescriptモジュールが見つからない」とエラーが出てしまうので、ローカルに開発インストールを行いました。よってコマンドは以下になります。

テスト実行時のエラー例
✖ ERROR: Cannot find module 'typescript'# typescriptが見つからない、とエラーが出る場合のインストール。$ npm install typescript chai mocha ts-node @types/chai @types/mocha --save-dev

package.json

(typescriptを含めた場合)最小限でこのようなpackage.jsonになるはず。
"scripts"下の"test"コマンド定義部分は"test"ディレクトリの下にあるファイル名が".ts"で終わるファイルを全て変更監視の対象にする場合の例です。対象のファイルが変更された場合には自動でtscによるコンパイルが行われ、テストが実行されます。

package.json
{"name":"testPatterns","version":"1.0.0","description":"samples for test cases.","main":"index.js","scripts":{"test":"mocha --require ts-node/register --watch-extensions ts \"test/**/*.ts\""},"author":"@olisheo","license":"ISC","dependencies":{},"devDependencies":{"@types/chai":"^4.2.5","@types/mocha":"^5.2.7","@types/node":"^12.12.12","chai":"^4.2.0","mocha":"^6.2.2","ts-node":"^8.5.2","typescript":"^3.7.2"}}

前記した通り、上記は"typescript"がローカルインストールされている状態で、なぜかこれが必要でした。

シンプルなテストで動作確認

テストの実行コマンド
$ npm test---w

シンプルなテスト

とりあえず一番シンプルなテストで動作を確認する。パスするケースと、失敗するケースを一つづつ用意。

describe('simplest test:',()=>{it('1 + 1 should be 2',()=>{expect(1+1).to.equal(2);});it('the test should fail because it expects "1 + 1 = 0"',()=>{expect(1+1).to.equal(0);});});
実行と結果
$ npm test---w> testPatterns@1.0.0 test /Users/user/project/testPatterns
> mocha --require ts-node/register --watch-extensions ts "test/**/*.ts""-w"

  simplest test:
    ✓ 1 + 1 should be 2
    1) the test should fail because it expects "1 + 1 = 0"


  1 passing (18ms)
  1 failing

  1) simplest test:
       the test should fail because it expects "1 + 1 = 0":

      AssertionError: expected 2 to equal 0
      + expected - actual

      -2
      +0

想定通り、一つはパスして一つはフェイルしてます。

よく使うパターン

同期処理で例外が投げられたらパス

describe('Typical tests:',()=>{it('immediate exception should synchronously be thrown.',()=>{expect(()=>{// 想定通りならば例外が発生するケースを記述。例えば下のように例外が投げられればパスする。// throw new Error('just expected exception.');  }).to.throw();});});

非同期処理をawaitで待つ

describe('Typical tests:',()=>{it('using await, timer should successfully expires',async()=>{constexpirationMessage=awaitsetTimer(1000);expect(expirationMessage).equals('OK!');});});// テスト対象の非同期関数。functionsetTimer(msec:number):Promise<string>{returnnewPromise((resolve,reject)=>{setTimeout(()=>{resolve('OK!');},msec);});}

Promiseを使った非同期。

Promiseをリターンで返すことで、Mochaが持っているPromiseのサポートを使える。しかし表記ミスを避けるために、できる限り前期のawaitを使った表記がいいと思う。ちなみにPromiseをリターンしないと、フェイルするテストがパスしてしまう。

describe('Typical tests:',()=>{it('using promise, timer should always be rejcted after timeout.',()=>{returnsetRejectionTimer(2000).then((expirationMessage)=>{expect.fail('test fails because the test case expects rejection.');}).catch((e)=>{expect(e).to.equal('NOT OK!');});});});// テスト対象の非同期関数。functionsetRejectionTimer(msec:number):Promise<string>{returnnewPromise((resolve,reject)=>{setTimeout(()=>{reject('NOT OK!');},msec);});}///////////////// これはだめ! /////////////////////////////describe('Typical tests:',()=>{it('using promise, timer should always be rejcted after timeout.',()=>{// 下は間違い。Primiseはリターンで返さないといけない。setRejectionTimer(2000).then((expirationMessage)=>{expect.fail('test fails because the test case expects rejection.');}).catch((e)=>{expect(e).to.equal('NOT OK!');});});});

実は上記の動くバージョンでも本質的なテストにはなっていなくて、Promiseがrejectされなかった場合は、expect.fail('test fails because the test case expects rejection.') で例外を発生させているため、expect(e).to.equal('NOT OK!')の条件と合致してテストがパスしているのであって、expect.fail()を削除して例外を発生させてなければ、フェイルするべきテストもパスしてしまう。

非同期はto.throw()が使えなさそう。

以下も機能しない。

非同期関数内でrejectが発生してもテストはパスしない。
describe('Typical tests:',()=>{it('delayed exception should asynchronously be thrown.',()=>{expect(async()=>{awaitsetRejectionTimer(3000);}).to.throw();// 機能しない。});});

タイムアウトを回避したい場合のテスト実行コマンド

実行時間がかかるテストも多いので、タイムアウトを延ばすためのオプションはよく使います。

--timeoutパラメーターで30秒のタイムアウトを指示した場合。
$ npm test---w--timeout 30000

これから

「非同期処理の中で例外が起こること」を正確にアサートするのは、現状難しそうです。テスト対象にPromiseを扱いやすくするchai-as-promiseなるものがあるらしいので、時間を見つけて今度はそちらをかじってみたいと思います。

sequelizeパッケージを初心者が使ってみる。

$
0
0

sequelizeとは

Node.jsにはデータベースを利用するためのパッケージがたくさん存在します。
しかし、データベースを扱うにはSQLというデータベースを操作する言語が必要になります。
sequelizeというパッケージを使えば、SQLを知らなくても、Node.jsでデータベースを利用することができます。

さらに詳しく

sequlizeは公式によるとORM(Object-relational mapping, オブジェクト関係マッピング)と呼ばれるものらしいです。
以下は引用です。

オブジェクト指向の概念とリレーショナルデータベースの概念を橋渡しする役割がORMです。オブジェクト指向とリレーショナルデータベースの相性はそれぞれの概念が異なるため、相性が良くありません。なぜなら、オブジェクト指向では、データをオブジェクトとして扱うのですが、リレーショナルデータベースではデータを2次元の表として扱うためギャップが生じてしまいます。ORMを利用することによって、オブジェクトとデータベース問い合わせの相互変換を行います。
引用:ORMとは?

sequlizeはPostgreSQL、MySQL、MariaDB、SQLite、MicrosoftSQLServerに対応しているそうです。

sequlizeを利用してみる。

今回はsequlizeでpostgreSQLを利用してみます。

前提として以下を完了しておいてください。

① Node.jsのイントール
② Yarnのインストール
③ PostgreSQLのインストール 参考:https://lets.postgresql.jp/map/install

sequlizeを使う前にデータベースを作っておきます。
PostgreSQLのpsqlターミナルを起動し、以下のコマンドを入力してください。

$ create database rensyu;

データベース名は何でも構いません。
ここではrensyuとしておきます。

データベースが完成したら、sequelizeのインストールを行なっていきます。
以下のコマンドを入力してください。

$ yarn add sequelize
$ yarn add pg
$ yarn add pg-hstore

参照:sequelize入門

インストールが終わったら、
sequelizeを利用するためのJSファイルを準備し、
そのファイル内に以下を書き込みます。
今回はシンプルにIDと名前と年齢だけのデータベースを作っていきます。

post.js
'use strict';constSequelize=require('sequelize');constsequelize=newSequelize('postgres://postgres:postgres@localhost/rensyu',{logging:false,define:{freezeTableName:true,timestamps:true}});constPost=sequelize.define('Post',{id:{type:Sequelize.INTEGER,autoIncrement:true,primaryKey:true},name:{type:Sequelize.STRING},age:{type:Sequelize.INTEGER}});Post.sync();module.exports=Post;

解説をしていきます。

constsequelize=newSequelize('postgres://postgres:postgres@localhost/rensyu',{logging:false,define:{freezeTableName:true,timestamps:true}});

これは、sequelizeのインスタンスを作成しています。
その際に、接続するデータベースを先ほど作成したものに設定しています。
postgresql://{ユーザー名}:{パスワード}@{ホスト名}/{データベース名}というようになっています。自分で作ったPostgreSQLのユーザー名やパスワードを利用してください。
loggingはデータベースに接続する際、コンソールログを行わない設定です。
defineのfreezeTableName: trueはテーブル名を固定する設定です。
timestampは自動的にcreatedAtという作成日時とupdatedAtという更新日時を自動的に追加してくれる設定です。

constPost=sequelize.define('Post',{id:{type:Sequelize.INTEGER,autoIncrement:true,primaryKey:true},name:{type:Sequelize.STRING},age:{type:Sequelize.INTEGER}});

こちらはデータベースのテーブルやデータを定義し、Postというオブジェクトに代入しています。
'Post'はテーブル名です。
id, name, ageはテーブルの列です。
それぞれに数字であるのか、文字であるのかなどの設定がされています。
idのprimaryKey: trueというのは、データベースを操作する上で参照の対象となる主キーを、idに設定しているという意味です。

Post.sync();module.exports=Post;

Post.sync();は、定義したPostというオブジェクトをデータベースに適用して同期を取っています。
module.exports = Post;は、オブジェクト自体をモジュールとして公開しています。

この他にもいろいろな設定オプションがあります。
https://sequelize.org/master/manual/getting-started.html

それでは、以上のデータベースを使ってみましょう。
適当なファイルを用意し、以下のような記述をします。

post-handler.js
constPost=require('./post');Post.create({name:name,age:age});

const Post = require('./post');はモジュールをインストールしています。
Post.createでデータベースにデータを保存しています。
あとは、EJSやPugなどのテンプレートエンジンを使えば、nameとageに好きな値を入れて保存ができます。

以上です。

参考:sequelize APIリファレンス
参考:N予備校 プログラミングコース

Greengrass(v1.9.4)上でNode.js(v8.10)のLambdaのデプロイができない問題の解決法

$
0
0

※追記
Greengrass Nodejs SDKのGitHubに書いてますね。。
https://github.com/aws/aws-greengrass-core-sdk-js

Rename the file to nodejs8.10
Make sure the file is not a symlink.

シンボリックリンクもだめらしいです。


GreengrassにNode.js(v8.10)のLambdaをデプロイすると、エラーになります。

Deployment xxxxx of type NewDeployment for group xxxxx failed error: worker with xxxxx failed to initialize

ログを確認すると、nodejs8.10というバイナリがないよと言っているようです。

/greengrass/ggc/var/log/system/runtime.log
[ERROR]-runtime execution error: unable to start lambda container.  {"errorString": "failed to run container sandbox: container_linux.go:344: starting container process caused \"exec: \\\"nodejs8.10\\\": executable file not found in $PATH\""}

無理やり作ってやると、うまくいくようになりました。

sudo ln -s /usr/bin/nodejs /usr/bin/nodejs8.10

Node.jsのセットアップの問題?

環境

OS: Ubuntu 18.04(VirtualBox on Mac)
Greengrass: 1.9.4
Node.js:10.17.0

Node.jsのインストールは公式サイト( https://github.com/nodesource/distributions/blob/master/README.md )に従いました。(2019/11/24時点で最新のv10.17.0がインストールされました)

curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash -
sudo apt-get install -y nodejs

Raspberry Piでは続きがあります

Raspberry Piでも同様の問題が起きますが、上記手順でデプロイは成功するようになります。ただし、Lambdaの実行時にエラーとなって実行されません。
こちらは解決法がわかりません。。。ヘルプ。。。

Raspberry Pi Zero W
OS:Raspbian Buster
Greengrass: 1.9.3
Node.js:10.16.3

Lambdaが呼び出されると、以下のエラー

/greengrass/ggc/var/log/user/[リージョン]/[アカウントID]/[Lambda名].log
[ERROR]-standard_init_linux.go:207: exec user process caused "operation not permitted"

ESLint v6.7.0

$
0
0

v6.6.0 | 次 (2019/12/21 JST)

ESLint 6.7.0がリリースされました。
小さな機能追加とバグ修正が行われています。

質問やバグ報告等ありましたら、お気軽にこちらまでお寄せください。

🏢 日本語 Issue 管理リポジトリ
👫 日本語サポート チャット
🏢 本家リポジトリ
👫 本家サポート チャット

🚀 本体への機能追加

設定ファイルのignorePatternsプロパティ

🔖 RFC022, #12274

共有設定を含む設定ファイルで、ignorePatternsプロパティが利用できるようになりました。このプロパティは.eslintignoreと同様に ESLint が無視するファイルをコントロールできます。

例(.eslintrc.yml)
ignorePatterns:-"!.eslintrc.js"-"/node_modules/"-"**/node_modules/"extends:-eslint:recommendedrules:quotes:error#...

Suggestions API

🔖 RFC030, #12384

あるエラーが複数の修正候補を持つ場合、ESLint は自動修正を行えず、修正候補を提示する手段もありませんでした。新しい Suggestions API を使うと、エディタの ESLint プラグイン等のインタラクティブな UI を通して修正候補を提示できるようになります。

今のところ、ユーザーはまだこの機能を利用できません。各検証ルールとエディタ プラグインのサポートをお待ちください。

【非推奨】個人設定ファイル

🔖 RFC032, #12426

ESLint が実行されたディレクトリに設定ファイル (.eslintrc.*) が存在しなかった場合、ESLint は OS のホーム ディレクトリにある設定ファイルを読み込みます。この機能を個人設定ファイルと呼びます。

この機能が非推奨になりました。

  • ESLint 7 以降、この機能を利用すると実行時警告が出力されるようになります。
  • ESLint 8 でこの機能は削除されます。

今後もホームディレクトリの設定ファイルを利用したい場合は、CLI オプションで明示的に指定してください。

eslint --config ~/.eslintrc.json lib

【非推奨】sourceCode#isSpaceBetweenTokens()

🔖 #12519

sourceCode#isSpaceBetweenTokens()メソッドが名前変更されました。新しい名前はsourceCode#isSpaceBetween()になります。

トークンだけでなく AST ノードを渡しても動作するので、単純に名前が間違っていたという理由です。元の名前のメソッドは非推奨メソッドとして残され、将来のメジャーリリースで削除されます (具体的な削除プランは示されていません)。

💡 新しいルール

grouped-accessor-pairs

🔖 #12331

Getter と Setter のペアを離れた場所に定義すると警告するルールです。

/* eslint grouped-accessor-pairs: error *///✘ BADconstobj1={getvalue(){},foo(){},setvalue(v){},}//✔ GOODconstobj2={getvalue(){},setvalue(v){},foo(){},}

Online Playground

no-setter-return

🔖 #12346

Setter に値を返すreturn文を書くと警告するルールです。getter-returnルールの兄弟です。

/* eslint no-setter-return: error *///✘ BADconstobj1={setvalue(v){returnthis._value},}//✔ GOODconstobj2={setvalue(v){if(v==null)returnthis._value=v},}

Online Playground

prefer-exponentiation-operator

🔖 #12360

Math.pow()関数の代わりに**演算子を使うように指示するルールです。

/* eslint prefer-exponentiation-operator: error *///✘ BADconstb1=Math.pow(2,8);constb2=Math.pow(a,b);constb3=Math.pow(a+b,c+d);constb4=Math.pow(-1,n);//✔ GOODconsta1=2**8;consta2=a**b;consta3=(a+b)**(c+d);consta4=(-1)**n;

Online Playground

no-dupe-else-if

🔖 #12504

else-if文の連鎖の中で、重複する条件式のために常にfalseになるif文を警告するルールです。

/* eslint no-dupe-else-if: error *///✘ BADif(a||b){//...}elseif(a){//...}//✔ GOODif(a||b){//...}elseif(c){//...}

Online Playground

no-constructor-return

🔖 #12529

コンストラクタの中でreturn文で値を返すことを禁止するルールです。

/* eslint no-constructor-return: error *///✘ BADclassB{constructor(){return{}}}//✔ GOODclassA{constructor(arg){if(arg==null)returnthis.value=arg}}

Online Playground

🔧 オプションが追加されたルール

no-underscore-dangleallowAfterThisConstructor

🔖 #11489

this.constructorに続くアンダーバーを許可するオプションが追加されました。

/* eslint no-underscore-dangle: [error, { allowAfterThisConstructor: true }] *///✘ BADclassB{foo(obj){obj.constructor._privateStuff}}//✔ GOODclassA{foo(obj){this.constructor._privateStuff}}

Online Playground

no-implicit-globalslexicalBindings

🔖 #11996

レキシカル スコープの宣言 class, const, letも警告するルールが追加されました。

/* eslint no-implicit-globals: [error, { lexicalBindings: true }] *///✘ BADclassB{}constb1=0letb2=0//✔ GOOD{classA{}consta1=0leta2=0}

Online Playground

no-useless-computed-keyenforceForClassMembers

🔖 #12110

クラス構文の不要な Computed Keys も警告するオプションが追加されました。

/* eslint no-useless-computed-key: [error, { enforceForClassMembers: true }] *///✘ BADclassB{["foo"](){}}//✔ GOODclassA{[foo](){}}

Online Playground

no-invalid-thiscapIsConstructor

🔖 #12308

ES5 スタイルのコンストラクタ (名前が大文字で始まる関数) をコンストラクタとして扱わないオプションが追加されました。React では関数コンポーネントに大文字で始まる関数名を使う習慣があるためです。

/* eslint no-invalid-this: [error, { capIsConstructor: false }] *///✘ BADfunctionFoo(){this.value=0}

Online Playground

✒️ eslint --fixをサポートしたルール

特になし。

⚠️ 非推奨になったルール

特になし。

喉頭がんの治療プロトコルをNode.jsでVueに表示しHerokuにデプロイ

$
0
0

概要

プログラムの勉強を始めて4か月ほどの開業医です。

病気のおすすめの診断法や、治療法などを記載した診療ガイドランというものがあります。その中に治療の進め方(治療計画)を示す、治療プロトコルというものがあります。病気の程度や治療の効果によって、治療の進め方が枝分かれしていくものなので、ifとelseさえ知っていればプログラム初心者でも治療プロトコルが簡単に作成できます。

昔私が書いた喉頭がんの診療プロトコルの医学書の内容を基に勉強したばかりのVueを少し使ってNode.jsでプログラミングしHerokuにデプロイしました。

実装

質問に答えていくと喉頭がんの治療の進め方が分かるWEBアプリ。

動作


1.jpg

2.jpg
3.jpg
4.jpg

作成方法

1.ライブラリーのインストール

heroku-sampleというフォルダー作成します。

初期設定します。

npm init -y

必要なライブラリをインストールします。

npm i body-parser express

2.index.jsの作成

index.jsを作成し、こちらのコードをコピペします。

varexpress=require('express');varapp=express();// public というフォルダに入れられた静的ファイルはそのまま表示app.use(express.static(__dirname+'/public'));// bodyParservarbodyParser=require('body-parser');app.use(bodyParser.json());app.post('/post',function(req,res){for(keyinreq.body){console.log(key,'=',req.body[key]);}res.end();});app.listen(process.env.PORT||8080);console.log("server start! (heroku)");

3.index.htmlの作成
index.js と同じ階層に public フォルダを作りその中に index.html を新規作成します。
以下のようなコードを書きました。

<!DOCTYPEhtml><html><head><title>Step01</title>
<scriptsrc="https://unpkg.com/vue"></script>
</head>
<body><h1>今後の治療方針は?</h1>
<h2id="app">{{message}}</h2>
<script>letmes;varstage=window.prompt("喉頭がんのステージをローマ数字で入力");if(stage==""){varsize=window.prompt("bulky tumor?(yes,no)");if(size=="yes"){vareffect=window.prompt("多分割照射法 total 66~72.8Gy+化学療法(NDP or TS-1)施行 \n52Gy時点で治療効果判定\n反応良好?(yes,no)");if(effect=="yes"){varrecurrence=window.prompt("照射継続➡経過観察\n腫瘍残存or再発あり?(yes,no)");if(recurrence=="yes"){mes="喉頭全摘術(+頸部郭清術)施行";}else{mes="経過観察";}}else{mes="喉頭全摘術(+頸部郭清術)施行";}}else{vareffect=window.prompt("通常分割照射法 total 60~68Gy施行 \n52Gy時点で治療効果判定\n反応良好?(yes,no)");if(effect=="yes"){varrecurrence=window.prompt("照射継続➡経過観察\n腫瘍残存or再発あり?(yes,no)");if(recurrence=="yes"){mes="喉頭全摘術(+頸部郭清術)施行";}else{mes="経過観察";}}else{mes="喉頭全摘術(+頸部郭清術)施行";}}}elseif(stage==""){varsize=window.prompt("bulky tumor?(yes,no)");if(size=="yes"){vareffect=window.prompt("多分割照射法 total 66~72.8Gy+化学療法(NDP or TS-1)試行 \n52Gy時点で治療効果判定\n反応良好?(yes,no)");if(effect=="yes"){varrecurrence=window.prompt("照射継続➡経過観察\n腫瘍残存or再発あり?(yes,no)");if(recurrence=="yes"){mes="喉頭全摘術(+頸部郭清術)施行";}else{mes="経過観察";}}else{mes="喉頭全摘術(+頸部郭清術)施行";}}else{vareffect=window.prompt("通常分割照射法 total 60~68Gy施行 \n52Gy時点で治療効果判定\n反応良好?(yes,no)");if(effect=="yes"){varrecurrence=window.prompt("照射継続➡経過観察\n腫瘍残存or再発あり?(yes,no)");if(recurrence=="yes"){mes="喉頭全摘術(+頸部郭清術)施行";}else{mes="経過観察";}}else{mes="喉頭全摘術(+頸部郭清術)施行";}}}elseif(stage==""){vartumor=window.prompt("T3以上?(yes,no)");if(tumor=="yes"){varfix=window.prompt("T3NOで声帯固定?またはT3N1?(yes,no)");if(fix=="yes"){varconstriction=window.prompt("気道狭窄あり?(yes,no)");if(constriction=="yes"){varmalignancy=window.prompt("緊急気管切開\n施行\n(拡大)喉頭全摘術+患側甲状腺切除(+頸部郭清術)(+再建術)\n施行 \n病理検索結果:再発ハイリスク群?(yes,no)");if(malignancy=="yes"){mes="術後(化学)放射線療法施行";}else{mes="経過観察";}}else{varmalignancy=window.prompt("(拡大)喉頭全摘術+患側甲状腺切除(+頸部郭清術)(+再建術)\n施行 \n病理検索結果:再発ハイリスク群?(yes,no)");if(malignancy=="yes"){mes="術後(化学)放射線療法施行";}else{mes="経過観察";}}}else{vareffect=window.prompt("1コース目:通常分割照射法40Gy+化学療法(NDP40mg/m2+5FU700mg/m2)\n2コース目:加速分割照射法 24~27Gy+化学療法(NDP40mg/m2)\n試行 \n52Gy時点で治療効果判定\n反応良好?(yes,no)");if(effect=="yes"){varrecurrence=window.prompt("照射継続➡経過観察\n腫瘍残存or再発あり?(yes,no)");if(recurrence=="yes"){mes="喉頭全摘術(+頸部郭清術)";}else{mes="経過観察";}}else{mes="喉頭全摘術(+頸部郭清術)";}}}else{vareffect=window.prompt("多分割照射法 total 66~72.8Gy+化学療法(NDP or TS-1)\n試行 \n52Gy時点で治療効果判定\n反応良好?(yes,no)");if(effect=="yes"){varrecurrence=window.prompt("照射継続➡経過観察\n腫瘍残存or再発あり?(yes,no)");if(recurrence=="yes"){mes="喉頭全摘術(+頸部郭清術)";}else{mes="経過観察";}}else{mes="喉頭全摘術(+頸部郭清術)";}}}elseif(stage==""){varconstriction=window.prompt("気道狭窄あり?(yes,no)");if(constriction=="yes"){varope=window.prompt("緊急気管切開試行\n腫瘍切除可能?(yes,no)");if(ope=="yes"){varmalignancy=window.prompt("(拡大)喉頭全摘術+患側甲状腺切除(+頸部郭清術)(+再建術)\n施行 \n病理検索結果:再発ハイリスク群?(yes,no)");if(malignancy=="yes"){mes="術後(化学)放射線療法施行";}else{mes="経過観察";}}else{varcondition=window.prompt("全身状態良好?(yes,no)");if(condition=="yes"){mes="放射線療法or化学放射線療法(+胃瘻造設)";}else{mes="緩和療法(+胃瘻造設)";}}}else{varope=window.prompt("腫瘍切除可能?(yes,no)");if(ope=="yes"){varmalignancy=window.prompt("(拡大)喉頭全摘術+患側甲状腺切除(+頸部郭清術)(+再建術)\n施行 \n病理検索結果:再発ハイリスク群?(yes,no)");if(malignancy=="yes"){mes="術後(化学)放射線療法施行";}else{mes="経過観察";}}else{varcondition=window.prompt("全身状態良好?(yes,no)");if(condition=="yes"){mes="放射線療法or化学放射線療法(+胃瘻造設)";}else{mes="緩和療法(+胃瘻造設)";}}}}else{mes="Ⅰ、Ⅱ、Ⅲ、Ⅳのいずれかを入力してください。";}varapp=newVue({el:'#app',data:{message:mes}})</script>
</body>
</html>

以下の部分で少しVue.jsを使って書いています。

varapp=newVue({el:'#app',data:{message:mes}})

4..gitignore を作成
node_modules フォルダはアップロードから除外するため .gitignore というファイルを最上部のindex.jsなどと同じ場所に作成し以下のコードをコピペします。

#Logslogs*.lognpm-debug.log*yarn-debug.log*yarn-error.log*#Runtimedatapids*.pid*.seed*.pid.lock#Directoryforinstrumentedlibsgeneratedbyjscoverage/JSCoverlib-cov#Coveragedirectoryusedbytoolslikeistanbulcoverage#nyctestcoverage.nyc_output#Gruntintermediatestorage(http://gruntjs.com/creating-plugins#storing-task-files).grunt#Bowerdependencydirectory(https://bower.io/)bower_components#node-wafconfiguration.lock-wscript#Compiledbinaryaddons(https://nodejs.org/api/addons.html)build/Release#Dependencydirectoriesnode_modules/jspm_packages/#TypeScriptv1declarationfilestypings/#Optionalnpmcachedirectory.npm#Optionaleslintcache.eslintcache#OptionalREPLhistory.node_repl_history#Outputof'npm pack'*.tgz#YarnIntegrityfile.yarn-integrity#dotenvenvironmentvariablesfile.env#next.jsbuildoutput.next

5.Procfile
一番上の階層にProcfileというファイルを新規作成し以下のコードをコピペします。

web:nodeindex.js

6.herokuにデプロイ
gitリポジトリの対象にする初期設定をします。

git init

gitリポジトリにアプリを追加します。

git add --a

gitリポジトリにコミットします。

git commit -m "commit"

herokuアプリ追加します。自分で考えたアプリケーションネーム(英語)を入力します。

heroku create アプリケーションネーム

herokuにプッシュして完了です。

git push heroku master

6.URL
https://selfcheck.herokuapp.com/

考察

ifとelseだけで書いているので、きれいなコードではありませんが、喉頭がんの診療プロトコルが実装できました。プログラム初心者の方にif文だけでもなんとかなるという参考になれば幸いです。

とりあえずnode.jsでES6記法(importなど)を使ってみたい時

$
0
0

概要

とりあえずnode.jsを勉強してみようと思いコードを書き始めました。
その中でimportを使おうと思ったのですがエラーとなってしまいました。

babelなどを使わないといけないのかと思い、いろいろ調べていたのですが実際には簡単にできます。
試しにnode.jsを書いてみたいという場合に便利です。

どうやるのか

方法1

  1. jsファイルの拡張子を、.jsから.mjsに変更
  2. --experimental-modulesオプションを付けて実行する
node --experimental-modules index.mjs

これだけでES6記法でnode.jsで書いたjsファイルを実行することができます。

https://nodejs.org/api/cli.html#cli_experimental_modules

方法2

  1. esmというモジュールをインストールします
yarn add esm

2. 次のように実行します

node -r esm index.js

これで実行できます。

esmの別の実行方法

なおこのesmというモジュールは、esmモジュールのコマンドを使ってプロジェクトを作成するやり方もあります。

yarn create esm

を実行することで、プロジェクトが作成されます。
そのプロジェクトディレクトリの中にmain.jsというjsファイルができ、その中に実際にコードを書いていきます。

そして実行はそのプロジェクトディレクトリの中で普通に

node index.js

とすればES6の記法で実行できます。

とりあえずES6記法を使いたいという場合は、このプロジェクトをいちいち作るという方法はめんどうかもしれませんので、そのような場合は、上で書いたnode -r esmが簡単です。

https://www.npmjs.com/package/esm

※ちなみに yarn の -r オプションは、「require」の意味です。

まとめ

調べてみたら意外に簡単にES6記法を試すことはできるようです。
ただ、node.js自体で用意されている--experimental-modulesは、その名称からもわかるように、「experimental(試験的)」なので、実際にしっかりと作り込んでいく場合は、babel などを使っていく必要があるのかもしれません。

nvmでdefaultバージョンを設定してもsystemのnodeが使用されてしまう

$
0
0

nvmでnodeのバージョン管理をしたいのだが、nvm alias default v*.*.*でデフォルトのバージョンを設定しても、新しいターミナルを開くとsystemのnode(brewでインストールしたnode)が使用されてしまう問題が起こった。
nvm lsを実行するとdefaultではなく、systemを指してしまうのだ。

$ nvm ls
       v12.13.1
->       system
default -> lts/* (-> v12.13.1)
node -> stable (-> v12.13.1) (default)
stable -> 12.13 (-> v12.13.1) (default)

実行環境

macOS 10.14.6
nvm 0.35.1(node v12.13.1をインストール済み)
brewでインストールしたnode v13.1.0

原因

もともと、yarnをbrewでインストールした時にnodeが一緒にインストールされてしまい、それがターミナルを開くとnvmでインストールしたnodeより優先されていた。
(ちなみにbrewでyarnを入れた理由は、yarnの公式にnpmでyarnをインストールするのはお勧めしないということが書いてあるため。)

解決方法

調べてみると、ここに解決方法があった。

どうやら、下記コマンドでbrewからyarnをアンインストールせずに、nodeだけをアンインストールできるようだ。

brew uninstall node --ignore-dependencies

上記実行後、新しいターミナルを開くと無事nvmのdefaultのnodeバージョンを使うことができるようになった。

$ node -v
v12.13.1
$ nvm ls
->     v12.13.1
default -> lts/* (-> v12.13.1)
node -> stable (-> v12.13.1) (default)
stable -> 12.13 (-> v12.13.1) (default)

なお、参考ページの別のコメントに、.bash_profile.bashrcなどでnvmを読み込んだ後に$PATHを変更していることが原因の場合もあるとの指摘もあったので、この記事の方法で解決できない場合はそちらも確認してみると良いかもしれない。


【自分用】Node.jsアプリの設計の手順

$
0
0

Twitterのようなアプリを設計する際

①要件定義

<機能要件>
投稿を作る。
投稿を編集する。
投稿を削除する。
投稿をお気に入りとする。
投稿に対してコメントをする。

などの基本的な機能を定義することを言います。

<非機能要件>
機能に付随する要件やセキュリティ要件

などを定義する。

②用語定義

上記の要件定義であがった用語を定義する。

用語英語表記意味    
ユーザーuser投稿の利用者
投稿tweet投稿をすること
お気に入りfavorite投稿をお気に入りに登録すること
コメント comment投稿に対してコメントをすること

用語の表す対象の意味付けをしっかりすることで、思わぬ実装ミスを防ぐことができます。

③データモデリング

ER図などを使い用語の関係性などを定義する。
用語設計をすることによって、要件に漏れがないか、仕組みに問題がないかをチェックすることができます。

ユーザー1 --------- *投稿1 ---------*お気に入り

④URL設計

内容を表示するページ構成を設計する。
同じページ上に表示できるものは一緒のページをする。

・トップページ/投稿一覧ページ
・自分の投稿/コメント/お気に入りを表示するページ
・投稿を作成するページ
・投稿を編集するページ

ページURL

パスメソッドページの内容 
/GETトップページ/投稿一覧ページ
/tweet/:tweetidGET自分の投稿/コメント/お気に入りを表示するページ
/tweet/newGET投稿を作成するページ
/tweet/:tweetid/editGET投稿を編集するページ

WEBAPIのURL

パスメソッド処理内容 方法
/tweetPOST投稿をするフォーム
/tweet/:tweetid?edit=1POST投稿を編集するフォーム
/tweet/:tweetid?delete=1POST投稿を削除するAJAX
/tweet/:tweetid/user/:userid/favoritePOST投稿をお気に入りするAJAX
/tweet/:tweetid/user/:userid/commentPOST投稿にコメントをするAJAX

④モジュール設計

Expressなどのフレームワークを使えばモジュール設計は簡単ですが、
ここではMVCに当てはめてみます。

MVC

モジュール設計はMVCフレームワークを使うと整理がしやすいです。

Model

Modelはデータモデリングのことを指し、データの管理を行うモジュールを当てはめます。
Modelモジュールはmodelsディレクトリの配下におきます。

ファイル名models/user.jsmodels/tweet.jsmodels/comment.jsmodels/favorite.js
役割ユーザーの定義と保存ツイートの定義と保存コメントの定義と保存お気に入りの定義と保存
View

Viewは見た目、HTMLなどを生成するテンプレート(pugやEJS)を当てはめます。
Viewはviewsディレクトリの中におきます。

ファイル名index.pugnew.puglayout.pugedit.pug
役割トップ/投稿一覧モジュール新規投稿モジュール基礎モジュール投稿編集モジュール
Controller

ControllerはModelとViewをコントロールする部分で、Node.jsだとルーティングを指します。

ファイル名routes/tweet.jsroutes/comment.jsroutes/fovorite.js
役割ツイートの処理コメントの処理お気に入りの処理

参考:N予備校 プログラミングコース

nodebrewコマンドのメモ

$
0
0

nodebrewめも

「なにインストールしたんやったっけ?」
「切替ってどうやるんやったっけ?」
勉強のときこういうこと多いので備忘録用のめもです。
なのでインストールは終わってる前提です。ご了承ください。。。

インストールできるバージョンを確認したい

$ nodebrew ls-remote

インストールしたい

  • バージョンを指定する方法
  • エイリアスで指定する方法

があるようです。

バージョンを指定する方法

$ nodebrew install [version] # 例えば [version] -> v12.0.0

エイリアスで指定する方法

安定版が欲しい場合

$ nodebrew install stable

最新版が欲しい場合

$ nodebrew install latest

※安定版と最新版の違い
安定版: 安全。
最新版: 新しい機能が盛り込まれている。バグが残ってることもある。
のような感じ

バージョンを指定してNodeを使いたい

$ nodebrew use [version] # 例えば [version] -> v12.0.0

インストールしたバージョンを確認したい/使用中のバージョンを確認したい

$ nodebrew ls

結果には current: v12.0.0のように現在使用しているバージョンも表示される

最後に

Nodebrewを業務で使ってるところってあるんかな、、、

編集終わってからnvmってのを知りました。nodebrewと同じくNode.jsのバージョン管理ができるやつなんかな。調べないと、、、

WebSocket についてまとめてみる

$
0
0

WebSocket とは?

Web 上でクライアント(Web ブラウザ)・サーバー間を常時接続にしておいて、双方向通信を低コストで実現するための技術規格。プロトコル。

WebSocket の何がありがたいのか?

近年 SNS アプリなどではインタラクティブで リアルタイムなやりとりが求められるようになってきました。

例えば、チャットアプリでは、複数のユーザーが同じページを見ているような状況で、ページを更新しなくても誰かの発言が他のユーザーのページにもリアルタイムに更新されるようにしたい、ということがあると思います。
この リアルタイムに更新という機能を実現するためには、誰かが発言したということを サーバーからクライアントに伝える必要があります。このような機能を WebSocket は実現します。

この クライアントからのリクエストがなくても、常時接続しているのでサーバーからクライアントに好きなタイミングで通信ができるということを実現できるところが WebSocket のすごいところです。

HTTP ではダメなの?

Web サイトを閲覧するするときは普通、HTTP を使うかと思います。
この HTTP は、クライアント(ブラウザ)がサーバーにリクエストを送って、一時的にコネクションを張り、サーバーがレスポンスを返すという流れになります。そして一つのコネクションにつき、一つのリクエストしか送れません...。

つまり、基本的に HTTP は クライアントが何らかのリクエストを送らない限り、サーバーはレスポンスを返せないプロトコルなのです。

HTTP で擬似的に双方向通信を実現することはできるが...

WebSocket はこのような HTTP(XMLHttpRequest) の欠点の補おうというニーズから生まれた規格なので、HTTP しか手段がなかった時は HTTP でなんとかしようとしていました。

具体的には、HTTP のコネクションを張りっぱなしにしておいて、クライアントから一定間隔でサーバーにポーリングし続ける。サーバーから情報を送りたいタイミングになって初めてレスポンスを返すといった具合です。
※ XMLHttpReqest の ロングポールといい、Comet (サーバで発生したイベントをクライアントからの要請なしにクライアントに送信することができる技術) の実現に必要だった。

ただ、この方法では以下のような問題が生じました。

  • ブラウザの HTTP 接続のタイムアウト(30秒など)があるため、接続し直すロジックが必要になる。
  • 擬似的に双方向通信を行うため通信が発生するごとに TCP ハンドシェイクを再度行うことが必要になる。
  • HTTP コネクションを長時間占有するため、その間サーバに接続する他のアプリケーションの動作に影響を及ぼす可能性がある。

対して WebSocket は...

対して WebSocket には以下のような特徴があり、HTTP の問題を解決しています。

  • サーバとクライアントが一度コネクションを行った後は、必要な通信を全てそのコネクション上で専用のプロトコルを用いて行い、新たなコネクションを張る必要がない。
  • HTTP コネクションとは異なる軽量プロトコルを使うなどの理由で、通信ロスが減る。
  • 一つのコネクションで全てのデータ送受信が行えるため同一サーバに接続する他のアプリケーションへの影響が少ない。

WebSocket の機能・特徴まとめ

以上のことから WebSocket の機能・特徴をまとめると以下のようになるかと思います。

  • 双方向通信によるサーバプッシュ機能 : 一度コネクションを確立したあとは、サーバとクライアントのどちらからも通信を行うことが可能になる。
  • データ通信量削減 : Payload 意外の情報(ヘッダ)は最小2byte, 最大でも14byteに収まる様になっていてとても小さい。
  • 低コスト : HTTP のように通信のたびにコネクションを張るということはせず、一度コネクションを確立するとそのコネクション上で通信を行う。

WebSocket の通信の流れ

1. WebSocket opening ハンドシェイク

WebSocket opening ハンドシェイクは HTTP 通信で行われます。
クライアントのリクエストには以下のように Upgrade ヘッダConnection ヘッダSec-WebSocket-Version ヘッダSec-WebSocket-Key ヘッダが付けられます。

Upgrade: websocket
Connection: upgrade
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: XXXXXXXXXXXXXXXXXX==

サーバーからのレスポンスヘッダは以下のようになります。

HTTP/1.1 101 OK
Upgrade: websocket
Connection: upgrade
Sec-WebSocket-Accept: ZZZZZZZZZZZZZZZZZZZ==

ステータスコード 101 は「Switching Protocols」で、これでコネクションが確立したことになります。

2. 双方向通信

コネクション確立後は、HTTP ではなく WebSocket プロトコルで通信を行います。
「フレーム」という単位でデータのやりとりが行われます。

実際に使ってみる

実際に WebSocket を使ってみます。
サーバーサイドのコードは以下のような感じです。Node.js を使っています。

// index.jsconstserver=require('ws').Server;constwsServer=newserver({port:8001});wsServer.on('connection',function(ws){wsServer.on('message',function(message){console.log(message);});wsServer.on('close',function(){console.log('bye.');});});

書き終わったら、node index.jsで起動させておきます。

Chrome の Developer tool でもなんでも良いのですが、クライアントのコードは以下のような感じで実行してみます。

constcon=newWebSocket('localhost:8081');con.send('Hello WebSocket!');con.close();

サーバーで以下のようなログが流れるはずです。
簡単ですね。

Hello WebSocket!
bye.

おまけ

セキュア通信は wss で

HTTP と同じく、WebSocket にもセキュア通信用のプロトコルが用意されており wssになっています。

よく利用する Event 処理とメソッド

// Event 処理-onopen// 接続された時-onerror// エラーが発生した時-onmessage// メッセージを受け取った時-onclose// 切断された時// メソッド-send()// データを送信する-close()// 通信を切断する

初めてのAuth0ハンズオン

$
0
0

はじめに

この記事は、初めてAuth0を触る方がAuth0を利用した認証・認可の基本的な実装方法を短時間でご習得頂くことを目的とした簡易チュートリアルです。

事前準備

事前に下記をご準備お願いします。

  • MacまたはWindows PC
  • Chrome
  • GitHubのアカウント、Git CLI
  • Node.js, Node Package Manager
  • SMSが使える携帯電話
  • 通信制限がかかっていないインターネット環境

ハンズオン

Auth0無料アカウントの取得〜テナントの作成

Chromeでhttps://auth0.comにアクセスして画面右上の"SIGN UP"を押します。任意のEmailアドレスでアカウントを作成します。アカウントが作成されると開発テナントが作成され、このアカウントがテナントの管理者となります。

右上の顔写真の右下矢印をクリックして"Create tenant"を選択します。

Tenant Domainに任意の名前を入力して他はデフォルトのまま"CREATE"を押します。テナントの作成をご体感頂くために作成しています。アカウント作成のタイミングで作成された開発テナントをそのままご利用頂いても問題ありません。

Applicationの登録〜認証機能の統合

左ペインの"Applications"をクリックして右上の"CREATE APPLICATION"を押します。

"Name"に任意の名前を入力、"Choose an application type"で"Simgle Page Web Applications"を選択して”CREATE”を押します。

"Settings"タブをクリックして"Allowed Callback URLs", "Allowed Web Origins", "Allowed Logout URLs"に"http://localhost:3000"を入力して画面下の"SAVE CHANGES"を押します。

ApplicationのGitHubリポジトリをローカルPCにクローンします。Auth0は65以上の言語やフレームワークに対応したSDK、それらのSDKを利用したサンプルアプリケーションをGitHubに公開しています。一から実装する必要は無いのでそれらを検索して利用して下さい。

$ git clone https://github.com/auth0-samples/auth0-react-samples.git

auth0-react-samples/01-Login/srcに移動します。

$cd auth0-react-samples/01-Login/src

auth_config.json.exampleをコピーしてauth_config.jsonを作成します。

$cp auth_config.json.example auth_config.json

auth_copnfig.jsonを編集します。"clientID"は"Appications"->"作成したApplication"->"Settings"から確認できます。

{"domain":"kiriko.auth0.com","clientId":"xxxx"}

auth0-react-samples/01-Loginに移動します。

$cd auth0-react-samples/01-Login

npm installを実行して必要なパッケージをインストールします。

$ npm install

npm startを実行してApplicationを起動します。

$ npm start

Chromeでhttp://localhost:3000にアクセスして右上の"Log in"を押します。手順通りに設定していればAuth0 のUniversal Login画面が表示されます。

"Sign up"タブをクリック、任意のEmail/Passwordでユーザを作成します。User Consentの画面で”Accept”を押します。Auth0のダッシュボード・左ペインの"Users"をクリックして作成されたユーザが表示されていれば成功です。

Social Connectionの設定

左ペインの"Connections"->"Social"をクリックしてGoogleのフリップスイッチをクリックします。Googleアカウントでログイン可能なApplicationを選択して"CONTINUE"を押します。

設定画面で全てデフォルトのまま画面下の"SAVE"を押します。Client ID/SecretはAuth0が標準のDevelopment Keyを持っているため指定しなくても問題ありません。Permissionsをカスタマイズする場合はGoogle側のセキュリティ設定が必要になるケースもあります。詳細はGoogle側のAPIドキュメントをご参照下さい。Googleのフリップスイッチがオンになったことを確認します。

Chromeでhttp://localhost:3000にアクセスして右上の"Log in"を押します。Googleのログインボタンが表示されていれば成功です。Googleアカウントでログインして下さい。

Rulesの設定

Rulesは、認証・認可のプロセスで予めJSで定義したロジックを実行することができるAuth0のユニークな機能です。参考:Inside the Auth0 Engine

左ペインの"Rules"をクリック、右上の"CREATE RULE"を押します。

"Access Cpontrol"配下の"Add persistent attributes to user"をクリックします。この記事では例としてユーザにカスタムの属性を追加するルールを試しています。後ほど他のルールもお試し下さい。

画面下の"SAVE CHANGES"を押します。

Chromeでhttp://localhost:3000にアクセスして右上の"Log in"を押して任意のアカウントでログインします。

左ペインの"Users & Roles"->該当のユーザをクリック、"user_metadata"に"color": "blue"が属性として追加されていれば成功です。ログイン認証のプロセス中に設定したルールが実行されカスタム属性が追加されました。

Multi Factor Authenticationの設定

左ペインの"Multifactor Auth"をクリック、"Always require Multi-factor Authentication"フリップスイッチをオンにします。

Chromeでhttp://localhost:3000にアクセスして右上の"Log in"を押します。"アカウントの安全性向上"と表示されたダイアログで”別の方法を試す”をクリックします。”他の方法”と表示されたダイアログで”SMS”をクリックします。この記事では例として二要素目をSMSにしています。後ほど他の要素もお試し下さい。

"アカウントの安全性向上"と表示されたダイアログでご自身の携帯電話番号を入力します。SMSに送られてきた6桁のコードを入力して”続ける”を押します。"この番号を安全な場所に保管しました"をチェックして”続ける”を押します。この記事ではコードは保管せずに進めています。実運用ではリカバリーコードは重要なコードになるため安全な場所に保管して下さい。

Applicationにログインできたら成功です。

この記事では"Always require Multi-factor Authentication"をオンにして常に多要素認証がかかるようにしていますが、先のステップで試したRulesを作成して特定の条件下(例/特定の時間帯、機密性の高い情報へのアクセスが発生したタイミング)で多要素認証がかかるようにすることが可能です。

APIの登録〜API Protectionの概要

Ctr-Cで起動したApplicationを停止して"Always require Multi-factor Authentication"フリップスイッチをオフにします。左ペインの"APIs"をクリック、右上の"CREATE API"を押します。

"Name", "Identifier"に任意の値を入力して”CREATE”を押します。"Identifier"は実在するURLである必要はありません。識別し易いIDを入力して下さい。

"Settings"タブをクリック、画面中頃の"Enable RBAC", "Add Permissions in the Access Token"フリップスイッチをオンにして画面下の"SAVE"を押します。後続のステップで設定するPermissionをaccess_tokenの中に含めて認可情報を制御することが可能になります。

"Permissions"タブをクリック、"read:appointments", "update:appointments"を各々入力して"ADD"を押します。Permissionの書式はOAuth2.0で定義されているxxxx:xxxxである必要があります。

左ペインの"Roles"をクリック、"ADD ROLE"を押します。

"Name"に"Appointments Submitter", "Description"に任意の説明を入力して"CREATE"を押します。

"Permissions"タブをクリックして"ADD PERMISSIONS"を押します。

作成したAPIを選択して全てのPermissonを選択、"ADD PERMISSIONS"を押します。

"Users"タブをクリックして"ADD USERS"を押します。

任意のユーザを選択して"ASSIGN"を押します。アサインされたユーザでApplicationにログインしたタイミングでこのAPIのスコープにアクセスすることが許可された認可情報がaccess_tokenに追加されます。

ターミナルでauth0-react-samples/02-Calling-an-API/srcに移動します。

$cd auth0-react-samples/01-Login/src

auth_config.json.exampleをコピーしてauth_config.jsonを作成します。

$cp auth_config.json.example auth_config.json

auth_copnfig.jsonを編集します。"clientID"は"Appications"->"作成したApplication"->"Settings"から確認できます。

{"domain":"kiriko.auth0.com","clientId":"xxxx"}

Node.jsのパッケージをインストールしてApplicationを起動します。

$pwd~/auth0-react-samples/02-Calling-an-API
$ npm install$ npm run dev

Chromeでhttp://localhost:3000にアクセスしてRoleにアサインしたユーザでログインします。"External API"タブをクリック、"Ping API"ボタンを押します。

Chromeのデベロッパーツールを開いて"Network"->"External"をクリック、"Request Headers"の"ey..."をコピーします。これがaccess_tokenです。

https://jwt.io/にアクセスして”Encoded”の下にコピーしたaccess_tokenをペーストします。”Decoded”の"PAYLOAD"にRoleに割り当てたPermissionが表示されていれば成功です。

次のステップ

最後までお付き合い頂きありがとうございます。この記事はAuth0を初めて触る方に短時間でAuth0をご習得頂くことを目的とした入り口のため、物足りないとお感じになる方もいらっしゃるかと思います。次のステップとして、Auth0のContent Engineering Teamが投稿している技術ブログをお試し頂くことをお勧めします。様々な言語やフレームワーク、実際の現場で起こりうる認証・認可のシナリオに沿ったテーマを準備しています。

google-home-notifierを使ってGoogleHomeに喋らせる

$
0
0

※こちらの記事は株式会社ギフトパッド「システム開発部技術委員会」の11月発表内容です

GoogleHomeは基本的にこちらから「OK!Google」と話しかけることをトリガーに何かしらの処理を行ってくれますが、こちらからの指示がなければただの置物です。
今回は「google-home-notifier」というnpmパッケージを使って、Slackで投稿した内容をGoogleHomeが発言してくれるようにします。

google-home-notifier

https://github.com/noelportugal/google-home-notifier

インストール

npm install google-home-notifier

実装

「google-home-notifier」READMEのサンプルを参考に、通知用のjsを作成
とりあえず喋る言葉を固定にする

constgooglehome=require('google-home-notifier');constlanguage='ja';googlehome.device('Google Homeの名前',language);googlehome.notify('しゃべったあああああああ',function(res){console.log(res);});
node notice.js

GoogleHomeと実行環境が同じネットワークに繋がっていれば、GoogleHomeから喋るようになります。

※現在node_modules/google-home-notifier/package.jsonの「google-tts-api」を「0.0.4」に指定して、npm updateかけないとエラーで止まります。

nodeでwebサーバーを作成する

http://localhost:3000/にリクエストするとGoogleHomeが喋るようにします。

consthttp=require('http');constgooglehome=require('google-home-notifier');googlehome.device('Google Homeの名前','ja');googlehome.ip('Google HomeのIP');http.createServer(function(req,res){req.on("end",function(){googlehome.notify('しゃべったあああああああ',function(res){console.log(res);});});res.writeHead(200,{'Content-Type':'text/plain'});res.end();}).listen(3000,'127.0.0.1');

localhostを外部公開

ngrokを使って外部公開してSlackから叩けるようにします

$ngrok http 3000

Session Status                online
Session Expires               7 hours, 58 minutes 
Version                       2.3.35
Region                        United States (us) 
Web Interface                 http://127.0.0.1:4040
Forwarding                    http://c4c72050.ngrok.io -> http://localhost:3000
Forwarding                    https://c4c72050.ngrok.io -> http://localhost:3000  

Slackとの連携

SlackAPIのOutgoing Webhookを使って、「https://c4c72050.ngrok.io」をエンドポイントに指定

SlackからPOSTされてきたテキストを喋らせるように変更

consthttp=require('http');constgooglehome=require('google-home-notifier');googlehome.device('Google Homeの名前','ja');googlehome.ip('Google HomeのIP');http.createServer(function(req,res){letdata='';req.on('data',function(chunk){data+=chunk;});req.on("end",function(){constqs=require('querystring');constpost=qs.parse(data);consttext=post['user_name']+post['text'];googlehome.notify(text,function(res){console.log(res);});});res.writeHead(200,{'Content-Type':'text/plain'});res.end();}).listen(3000,'127.0.0.1');

今回は動くところまでを目標にしているのでやってませんが、tokenも送られてくるのでチェックすることもできます

これでSlackの投稿をトリガーに、GoogleHomeに喋らせることができます。

参考文献

https://qiita.com/ktetsuo/items/21f4f85a98145772be3d

Viewing all 8820 articles
Browse latest View live