u-ryo's blog

various information for coding...

Unit Test on Jhipster

| Comments

JHipsterでのunit testでいくつかハマったのでメモ。

  • unit test実行はgradle test

  • 単一test classを指定するにはgradle test --tests FQDN.class.name

  • test class内のlog.debug(...)を出力するには、build.gradletest.testLogging.showStandardStreams = trueを記述し、またsrc/test/resources/application.ymlに以下を記述(src/main/resources/application-dev.ymlには記述あり)

    1
    2
    3
    4
    5
    
    logging:
        level:
            ROOT: DEBUG
            bz.mydns.walt.canmatch: DEBUG
            io.github.jhipster: DEBUG
    

  • test classにおいてRESTで認証したuserを表したい時には@WithMockUser(import org.springframework.security.test.context.support.WithMockUser;)

  • testされる側のREST methodでは、java.security.Principalを勝手に引数に加えることでlogin userを取得出来、それをtestする場合にはtest classのMockMvcrestUserInfoMockMvc.perform(post("/api/user-infos")...)などとする時にorg.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.userを使って.user("foo")とすると、認証された"foo"というuserでのaccessをmock出来るが、org.springframework.security.core.context.SecurityContextHolder.getContext().getAuthentication()というstatic methodでjava.security.Principalが取れ、それに.getName()で名前を取得可能。その方がspringっぽいのかな。test class側では、認証accessが必要なmethodに上記の@WithMockUserをつければ良い。@WithMockUser(username="foo", password="pw", roles={"USER"})などとすれば色々指定可能だが基本的には不要

参照

Push Notification on Angular5

| Comments

もう先月ですが、Web Push Notificationを勉強して自分で試すことで、 理解を深めました。作ってもみました

やりたいアプリはJhipsterAngular使ってるんですが、 ではそういえば、AngularではWeb Pushってどうやるんだろう?! ということからまた勉強が始まりました。 調べてみると、簡単に使えるようになっているんですね(Angular CLI 1.6以降は)。

多くの導きのおかげで、angular5にてどのようにservice workerを導入するか、 はわかってきました。

要するに、アプリをgenerateする時に、Angular CLI(1.6+、現時点では1.7.3)で、

1
ng new application-name --service-worker

とするか、既にgenerateしてしまったアプリに対しては、 (Angular公式) Service Workerを始めるにあるように、

  1. yarn add @angular/service-worker
  2. ng set apps.0.serviceWorker=true または .angular-cli.jsonを編集してappsの下レベルに"serviceWorker": trueを挿入
  3. src/app/app.module.tsにService Workerをimportして登録(但しその際、base hrefを/の他にしたいならServiceWorkerModule.register('./ngsw-worker.js', {enabled: environment.production})./ngsw-worker.jsを相対指定しないとダメ)
  4. src/ngsw-config.jsonを作成(雛形をコピペ)
  5. ng build --prodとしてbuild(ng serveではダメ)(base hrefを/の他にしたいなら--base-href /another/directory/が必要。←最後が/でないとダメ)

PWAとしては、あとmanifest.jsonが必要?(Progressive Web Apps using the Angular Service Worker, AngularアプリをPWAにする方法)

これで、当該projectをweb browserで表示させると、 service workerが読み込まれ、 offline cacheが効くようになる、筈... なのですが、どう試しても、 offline modeにしてからreloadすると、504 Gateway Timeout (from ServiceWorker)になってしまいます。 PWCatでは上手くcacheされていることから、 多分これはAngularのbugと思います。

なのでoffline cacheは今は諦めて、push notificationの方策を探りました。 AngularのService Workerの話は、公式も含めて、 cacheやupdateばかりでpush notificationについては触れられてないんですね。 そういう中、A new Angular Service Worker — creating automatic progressive web apps. Part 1: theory, A new Angular Service Worker — creating automatic progressive web apps. Part 2: practiceという記事があり、 PWAtterとそのserverがありましたので、 非常に参考にさせてもらえました。 というかほぼそのままコピペして使わせてもらってます。 PWAtterはtwitterを拾うので物凄い勢いでpush notificationが来て、 明示的に消さないと消えないので、かなりうざいのですが、 これでAngularでPush Notificationが実装できるようになりました。 非Angularで作った時は、notificationの形についてはあまり気にしてなくて、 clickTargetとかNotification object specを無視していましたが、 Angularではngsw-worker.jsNOTIFICATION_OPTION_NAMESがあって、 そこで有効なproperty namesが規定されており、 NOTIFICATION_OPTION_NAMES.filter(name => desc.hasOwnProperty(name))として filterしているので、変なproperty書いても効かないんですね。 ちゃんとNotification object specに則った形のJSONをsendするようにすると、 push notificationが表示されるようになりました。

あとは、Notificationをclickしたら消えたり指定のpageに飛ぶように、 と思ったんですが、どうやったらいいのか。 notification.actionとか色々指定してみたものの、一向に何も起きず。 何でかなーって思ったら、どうやらnot yet implementedなんですね。 現に、node_modules/@angular/service-worker/ngsw-worker.jsに、 addEventListener('push', (event)... 等はあっても、 addEventListener('notificationclick', (event)... は無いんですね。(- -; workaroundとしては、以前の知見を活かして、 似たようなことを書けばそれだけでpush notificationを実現できることを確認しました。 push notificationの動作確認済のアプリを作成しました。

既に公式Angularでもissuesに似たような話はありましたので、 comment付けておきました。

ホントはPull Request作りたかった、です。 上記前者の方は、どこでどうやって書き込んでいるのかわからず、codeに出来ませんでした。 後者は、angular/packages/service-worker/worker/src/driver.tsangular/packages/service-worker/worker/src/service-worker.d.tsだというのはわかって、 書いてみたんですが、 いや難しいですね。 まずeventってnotification以下のJSONを含んでいるわけですが、 同時にevent.notification.close()っていうmethodもあるわけで、 こういうのを厳密にobjectとして表現しなきゃいけないようになってるんですね。 凄いなぁ。そういうのが曖昧模糊渾然一体としているのがjavascriptなのに。 また、clients.openWindow(...)clientsもいきなり出て来るobjectで、 出自がよくわからないのですけど、そういう曖昧さを許さないように出来ています。 他を見てどうやらthis.scope.clientsとすればよさそうなのはわかったんですが、 それでも./build.shをかけると driver.ts(246,28): error TS2339: Property 'openWindow' does not exist on type 'Clients'.と言われて、お手上げでした。 notification.close()の方も、 error TS2339: Property 'notification' does not exist on type 'Object | Client'と言われて、 どうしていいかわからず。 生成されるべきcodeはわかってるのに、悔しい、です。 折角Code of ConductCONTRIBUTING.md読んでcoding ruleやcommit message formatを学んだりCLA(Contributor License Agreement)登録したりDEVELOPER.md読んでbuildやtestの仕方学んだりしたのにー。 でもまぁ確かに、飛び先のURLはどこに書くべきか、とか、 clickしたら飛ばないでただ閉じるように/閉じないようにしたい、とかするには、 eventListener('notificationclick',(event)=>{...});を 直接いじらないとならないし、 仕様を含めたもっと別の方策が必要だと思います本当は。

favicon等iconを作るのに、Real Favicon Generator、いいですね。

GitHub Pages

| Comments

GitHub Pagesって、各user一つしか持てないのかとずっと思ってて。 それをmy blogに当ててきたから、 もう持てないのかと。 でも、違うんですね。 各project毎に持てるんですね。 しかも、昔はgh-pagesとかっていうbranch切らないとならなかったのが (今でもこの方法はある)、 その他にmaster branchそのまま、 もしくはmaster branchのdocs directory以下を GitHub Pagesとしてhttpsで公開できるんですね。

試しにdocsでやってみました。

Web Push Notification for PWA

| Comments

PWAの売りとしてWeb Push Notificationが出来るのはつとに聞いていましたが、 具体的にどうやるんだろう? と思ってました。 仕組みから考えて、WebSocketが必須なのかな? とかって勝手に思ってたんですが、違うんですね! 「やってみた」系の記事見て、Firebaseを使うものばっかりだったので、 えーっ! Firebase hosting必須なの?! と思いきや、 VAPID(Voluntary Application Server Identification)も あるということで一安心。 あーでも、別にFCM(Firebase Cloud Messaging)をhostingじゃなく使うことも出来るので、 その方が楽なのかな。 browser毎じゃなくてuser毎に送れるっていうし。 でもvendor lock onも嫌なので、 VAPIDからやってみました。

Web Pushの仕組みは、PWAのプッシュ通知の仕組みがよくまとまってました。 PWAとWeb Push Notificationは別なんですね。

browser上からは、Push Demoなどですぐ試せます。

自分の手に馴染ませないとよくわからないので、作ってみました

  1. push server側で、VAPIDのkey(public, private)を決めておく。key generationは、web-pushclient.jsをcommand lineから使って$ node_modules/web-push/src/cli.js generate-vapid-keysとせよ、とか書いてあるものも他に多くありましたが、GoogleのAdding Push Notifications to a Web AppにあるようにPush Companionで簡単に作れますね。試しに作ってみたものではscriptで毎回自動生成してclient(=web browser, html and javascript側)に読ませてみるようにしてみましたが、実運用上はベタに固定値書いておいた方が楽だしわかりやすいし再起動の度に値変わらないからその方がいいのかな。
  2. あとserver側は各clientのnotificationEndPoint、publicKey、authを知ってないといけない。これらはclientがserviceWorkerRegistration.pushManager.subscribe(subscribeParams)した時に得られる。subscribeParameterとは、server側のpublicKeyをurlB64ToUint8Array()したものとuserVisibleOnly: trueは固定値。そうして得られた3つの値をserver側に引き渡さねばならないのだが、そのまま渡すのではなく、btoa(String.fromCharCode.apply(null, new Uint8Array(key)))などとしてString化する必要がある。
  3. 上記でもらうnotificationEndPointって、mozillaならhttps://updates.push.services.mozilla.com/wpush/v2/...、chrome(chromium)ならhttps://fcm.googleapis.com/fcm/...というように、browser毎にmaker別のpush server持ってるんですね。ってことは、browserは裏ではこうしたsiteと常にconnection張ってるということ?
  4. もっと深い原理(具体的にどういうheader/encryptionでpush serviceに送っているか)はWeb Pushの実装まとめ(Chrome/Firefox/Android共通)参照。
  5. client側では、main.jsで、まずNotification.requestPermission()で許可を得る。許可を求めるtimingは、page load直後かと思いきや、最初にいきなり許可求めると拒否られることが多いということで、button式に。button押したら許可求める、と。
  6. 許可が得られたらnavigator.serviceWorker.register('/sw.js')でService Worker scriptを登録。navigator.serviceWorker.readyで利用可能になるのを待つ。これのreturn value(の型)はServiceWorkerRegistrationでありnavigator.serviceWorkerではないので注意。これからpushManagerを得ることになる。
  7. その後、push serverからserverのpublicKeyをGET。
  8. subscribeParamsを作ってserviceWorkerRegistration.pushManager.getSubscription(subscribeParams)してみる。既に登録があったらunsubscribeしないとならない。上述のように、parameterではserver側のpublicKeyをurlB64ToUint8Array()したものとuserVisibleOnly: trueは固定値。
  9. それからserviceWorkerRegistration.pushManager.subscribe(subscribeParams)でsubscribeする。
  10. そうすると、endpoint、key、authが得られるので、それらをpush server側に通知。
  11. sw.jsでは、push通知が喜た場合push eventがfireされるので、event.waitUntil(registration.showNotification(...)する。このregistrationがどこから来るのかよくわからなかったが、ともあれ明示的にwaitUntilしてshowNotificationしないと表示されない(pushが来て勝手に表示されるわけではない)。
  12. 表示されたNotificationをclickするとnotificationClick eventがfireされるので、clickしたらどこかへ遷移したい場合にはこのevent listenerをsw.jsに書いておく必要がある。
  13. PWA用に、sw.jsinstall eventとfetch event listenerでfile chacheの作成とその利用をcodeする(installでcacheに加え、fetchではcacheにあったらそれを、なければfetchするようにする)。
  14. notificationをpushするには、push server側でまずnew PushService(publicKey, privateKey, "http://localhost")してから、clientから貰った情報でpush.send(new Notification(...))する。中では色々と暗号化しているが、nl.martijndwarsのwebpush-javaを使えばVAPIDのkey生成やnotificationのsendもone methodでよろしくやってくれる。他の言語もweb-push-libsに各種あり。

実際に自分で真似して書いてみて、 値やcodeを色々変えて試してみることで、 大分わかってきました。

Brain Science

| Comments

出されている仕事先の事務女性が前社長夫人で、 今頃になってパワハラとか受けて辞めそうだと聞いていて、 そういう今日このごろに刺さった記事がありました。 ヒトの脳は「他人を裁く」ようにできている 「暴走する正義漢」を止める方法はない そこから、 「シャーデンフロイデ」(≒メシウマ)、 脳科学者中野信子さん、と調べて読んでいきました。