2019年12月31日 星期二

nodejs 錯誤處理

當在開發nodejs 程式 規模越來越大時
功能可能就被切割到多個function內
各階段的錯誤處理 有統一標準的作法
會便於建立標準的工作流程
整理一下關於錯誤的處理的注意要點

1.完全不處理 系統會自動以throw將錯誤丟出
2.若要對error資訊做處理或是加工 錯誤資訊
  請用try catch包起來
3.丟出錯誤 請統一用Error物件丟出
  catch攔截到的一定是Error物件
  發生錯誤自動throw出去的也一定是Error物件
4.當使用catch攔截到error物件處理後
  若寫了catch攔截錯誤
  在catch內執行throw將錯誤往外丟
  否則程式會繼續往下走
5.自訂Error物件
  e= new Error('messagxxxxxe') 可透過e.message 取得資訊
  系統throw出的錯誤訊息都會是在e.message
  若要加入自訂的資料屬性可以用 ex: e.info = sql ..
  Error物件屬性只有message是通用跨browser通用
6.系統已經加上error 處理的middleware
  若錯誤沒有被處理回到route上
  會被error handler接收

參考Error物件

2019年12月24日 星期二

supertest server執行時間過長 timeout問題排除

supertest 做api 的route測試
通常會在before內建立連線
再交由後續it進行測試

建立連線的過程
通常可能會是
const server = app.listen(5000,...)
api = supertest(server)
產生api 物件做後續的測試
如果產生server時間過長 (預設限制是2000ms)
則會因timeout而讓測試無法進行
在執行 mocha時 可以透過設定 加長timeout的時間
mocha --timeout 5000

2019年12月2日 星期一

mongodb建立使用者錯誤 Error: couldn't add user: Use of SCRAM-SHA-256 requires undigested passwords

mongo db 預設是不需要密碼連線的
要使用密碼連線須 先建立使用者帳號
原本3.x版建立使用者

db.createUser(
{
    user: "ozzysun",
    pwd: "xxxx",
    roles: [{ role: "root", db: "admin" }]
})

在4.x版後會有錯誤發生
須加上宣告密碼encode方式
db.createUser(
{
    user: "ozzysun",
    pwd: "xxx",
    roles: [{ role: "root", db: "admin" }],
    mechanisms:[  
      "SCRAM-SHA-1"
    ]
})

2019年11月29日 星期五

nginx 設定提供不同版本api使用

目前提供給外部使用的API Server均透過nginx
以 location /api proxy到 nodejs 的服務上
為了要提供全新架構的api 服務
將以v1,v2區分新舊版本server
以下是nginxg上設定範例

location ^~ /api {
        rewrite ^/api(/.*)$ $1 break;
        #若要改預設主機 改這裡即可
        proxy_pass http://api_server_v1;
}
location ^~ /api/v1 {
        rewrite ^/api/v1(/.*)$ $1 break;
        proxy_pass http://api_server_v1;
}

location ^~ /api/v2 {
        rewrite ^/api/v2(/.*)$ $1 break;
        proxy_pass http://api_server_v2;
}
upstream api_server_v1 {
        server 127.0.0.1:12345;
}
upstream api_server_v2 {
        server 127.0.0.1:3138;
}

設定的結果
http://localhost/api/xxx 會導到v1主機
http://localhost/api/v1/xxx 會導到v1主機
http://localhost/api/v2/xxx 會導到v2主機

2019年11月28日 星期四

Linux上 cordova 9 plugin問題紀錄

在linux上build cordova app 使用plugin時遇到的幾個問題處理紀錄一下

1.Error:Using "requireCordovaModule" to load non-cordova module
  發生在ios 的plugin上
  只要將context.requireCordovaModule 改require即可
2.There was a conflict trying to modify attributes with
  plugin要改寫AndroidMenifest時,寫入時間點互相沖到
  導致未異動到AndroidMenifest
  可以使用--force 強制安裝
  cordova plugin add xxx --force
  之後把安裝有問題的plugin移除 重新安裝即可

2019年11月22日 星期五

在cordova 使用 oogle map 發生 getCurrentPosition PositionError:timeout expired問題

使用getCurrentPosition抓取使用者位置
一直都很正常使用
最近發現使用coodva build app 一直無法抓到使用者位置
但在web上卻很正常
看到的錯誤為 PositionError:timeout expired
查了一下 只要在option內開啟enableHighAccuracy 即可解決

navigator.geolocation.getCurrentPosition(
  function(msg){
    console.log(msg)
  },
  function(error){
    console.log(error)
  },
  {
    enableHighAccuracy: false,
    maximumAge: 60000,
    timeout: 45000
  })

2019年11月21日 星期四

Linux 開機 mount fstab設定

目前工作電腦使用了Window與Linux雙系統
在window os的那顆硬碟 切出了一個partition 並格式化為exfat
做為雙系統共用的partition
希望可以開機自動mount到指定的目錄使用
1.列出所有的partition 確認要mount 的是在哪個device
  ex:查到sda5 就知道要去mount /dev/sda5
  cat /proc/partitions

2.查device的UUID
  blkid

3.建立要mount點的目錄
  ex:在 /media下建立win_exfat當作mount點
  cd /media
  sudo -s
  mkdir win_exfat // 這是我要使用的目錄名稱
4.手動mount 到media下的目錄
  mount -t exfat /dev/sdb5 /media/win_exfat
5.要開機自動mount須 修改/etc/fstab
  依照其他的設定 加入一筆設定 依序幾個參數分別是
  file system: 對應到實體device ex: UUID=02D9-DDF8
  mount point:要mount使用的路徑 /media/win_exfat
  type:格式 exfat
  options:defaults
  dump:0
  pass:0

fstab設定參考

2019年11月20日 星期三

我的1988 未央歌

1988 那是個大學錄取率只有30%的年代
考得很爛 不到兩百分的分數 連交志願卡的資格都沒有
全班考上的沒有幾個
放榜後 大家都到補習班重考

高雄的補習街在七賢路
一整班上百人上課的大教室
天花板上整排明亮的日光燈
教室裡一排排的 長條桌椅
是還記得的畫面

高四的生活 像是高中生活的延續
只是原本同班同學 散落在同一條路上不同的補習班
交通工具 由腳踏車 換成黃黑色的小兜風
那是剛喜歡上 Bon Jovi Euroupe的年代

高雄火車站 還有老營長唱片行 放著Final Countdown
和平路上位在地下室的Subway 點一杯飲料可以坐上一個下午
那是個可以看到珍貴搖滾MTV的地方

剛接觸到西洋搖滾樂的滋味
國語唱片裡 大概只有羅大佑與紅螞蟻能讓我們服氣

那年 黃舒駿的第一張專輯 馬不停蹄的憂傷 發行
號稱要打倒羅大佑 看著那樣的宣傳 頗不以為然
不過這張唱片 卻與這段1988的記憶 緊密的結合在一起
馬不停蹄的憂傷 未央歌 都成為這段時光的音樂


2019年11月7日 星期四

Gulp4 Did you forget to signal async completion?錯誤

在gulp task內以aysnc執行多項工作
在完成後希望可以用process.exit關閉視窗
process.exit的呼叫必須是在done()之後才執行
否則報錯

sampe code

gulp.task('myjob', async (done) => {
  for(var i=0; i < 10; i++){
    // loop do something
    const response = await doSomething(i);
    console.log(response)
  }
  done()
  process.exit()
})

2019年11月5日 星期二

vue.config.js設定publicPath

使用vue-cli4建置新專案內
webpack的設定已經被包裝在整個架構下
大多數預設值即可符合開發需求
若需要手動執行webpack設定
則是寫在根目錄的vue.config.js內
通常會需要設定的部分包括
publicPath 預設是以/為路徑
因此產生出來的html檔案內所有的css 或js 路徑
都會是對應到根目錄的/js/xxx或 /css/xx的位置
如果你build出來的檔案是要放在web server的根目錄下
也就是http://localhost/下執行 這樣可以正常顯示

但如果是放在如http://localhost/abc/def/xxx目錄下
就會有找不到相關檔案的問題

必須將publicPath設定為'./'才會讓build出的檔案
可以放在任意目錄下執行

以下為範例

 const path = require('path')  
 module.exports = {  
  publicPath: './', // 預設為/ 所有html下的js css 連結都會是/js/xx,在輸出到web server 路徑會有問題,需用./  
  outputDir: 'www', // 輸出路徑  
  assetsDir: '', // 要輸出的asset目錄,設定hello,則會輸出成 hello/js/xxx,  
  devServer: {  
   port: 1234,  
   open: true  
  },  
  chainWebpack: config => {  
   config.module  
    .rule('yaml')  
    .test(/\.ya?ml$/)  
    .use('js-yaml-loader')  
    .loader('js-yaml-loader')  
    .end()  
   config.resolve.alias  
    .set('@@', path.resolve(__dirname)) // @@ 代替根目錄  
  }  
 }  

2019年10月22日 星期二

nvm nodejs版本升級 並安裝原有package

通常使用nvm install 新的版本 就可以升級
如果升級同時把原本已經安裝在global上的package
也一併安裝到新的node上 可以使用

 nvm install 新版本 --reinstall-packages-from=舊版本  

可能需要重新安裝與rebuild的部分

 rm -rf node_modules  
 rm package-lock.json  
 npm rebuild node-sass  
 npm install gulp-cli -g  
參考連結

android device沒有權限使用 no permissions (user in plugdev group; are your udev rules wrong?)

在接Android device 透過adb devices 列出可以使用的設備時
發現雖然找到了設備
但卻沒有權限可以使用
找到解法如下
1.使用lsusb找出設備的idVendor 與idProduct

 lsusb  
 Bus 001 Device 009: ID 2207:0010  
 以我的設備資訊 idVendor=2207 idProduct=0010  

2.在udev增加一筆android 設定規則
 sudo vi /etc/udev/rules.d/51-android.rules  
 內容  
 SUBSYSTEM=="usb", ATTR{idVendor}=="2207", ATTR{idProduct}=="0010", MODE="0666", GROUP="plugdev"  

3.讓設定生效

 sudo udevadm control --reload-rules  


若沒反應將設設備拔除再重新插入即可
參考來源

unable to preventDefault inside passive event listener invocation 錯誤處理

在addEventListener 強制加上{ passive: false } 即可
以iscroll 為例修改

 me.addEvent = function(el, type, fn, capture) {  
  // el.addEventListener(type, fn, !!capture) 改為  
  el.addEventListener(type, fn, { passive: false })  
 }  

2019年10月14日 星期一

Linux 刪除舊kernal 降低boot 容量

當常態性的使用系統更新 如果boot分配的不夠大
可能會有boot爆掉的狀況
除了重新分配硬碟外
刪除舊的kernal 可能是比較快可以降低boot容量的作法
以下找到的操作程序

 1.列出所有的kernal  
  dpkg --get-selections|grep linux-image  
 2.目前使用的kernal  
  uname -r  
 3.刪除舊kernal  
  sudo apt-get purge xxxxxxx  
 4.刪除kernal src  
  /usr/src 下  
  rm -rf xxxxxx  
 5. 察看硬碟狀況  
  df -lh  

參考網址

2019年10月4日 星期五

[Violation] Added non-passive event listener to a scroll-blocking 警告


修正方式
1.npm install default-passive-events -S
2.在進入點entry.js加上
  import 'default-passive-events'

2019年9月30日 星期一

Linux shell顯示git branch

在shell上直接顯示git branch是很方便的資訊
以下是在不同的shell上的設定
.bashrc
 parse_git_branch() {  
    git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/ (\1)/'  
 }  
 export PS1="\u@\h \[\033[32m\]\w\[\033[33m\]\$(parse_git_branch)\[\033[00m\] $ "  

.zshrc
 setopt prompt_subst  
 autoload -Uz vcs_info  
 zstyle ':vcs_info:*' stagedstr 'M'   
 zstyle ':vcs_info:*' unstagedstr 'M'   
 zstyle ':vcs_info:*' check-for-changes true  
 zstyle ':vcs_info:*' actionformats '%F{5}[%F{2}%b%F{3}|%F{1}%a%F{5}]%f '  
 zstyle ':vcs_info:*' formats \  
  '%F{5}[%F{2}%b%F{5}] %F{2}%c%F{3}%u%f'<pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"><code>parse_git_branch() {  
    git branch 2&gt; /dev/null &#124; sed -e '/^[^*]/d' -e 's/* \(.*\)/ (\1)/'  
 }  
 export PS1=&quot;\u@\h \[\033[32m\]\w\[\033[33m\]\$(parse_git_branch)\[\033[00m\] $ &quot;  
 </code></pre>  
 zstyle ':vcs_info:git*+set-message:*' hooks git-untracked  
 zstyle ':vcs_info:*' enable git   
 +vi-git-untracked() {  
  if [[ $(git rev-parse --is-inside-work-tree 2> /dev/null) == 'true' ]] && \  
  [[ $(git ls-files --other --directory --exclude-standard | sed q | wc -l | tr -d ' ') == 1 ]] ; then  
  hook_com[unstaged]+='%F{1}%f'  
 fi  
 }  
 precmd () { vcs_info }  
 PROMPT='%F{5}[%F{2}%n%F{5}] %F{3}%3~ ${vcs_info_msg_0_} %f%# '  

2019年9月27日 星期五

Linux 開發環境設定紀錄

開始在適應使用Linux mint作為開發系統
使用上除了桌面與觸控板手勢的操作沒有mac好用外
基本上都還算上手
建置web開發環境 與執行原本的專案
發現了專案內有一些require大小寫有誤的地方
在linux下都會跳出錯誤訊息確認修正
環境的設定上 部份的小問題處理
在這作個紀錄

開發的系統移到mint上

1.ruby compass 使用安裝問題
You need to have Ruby and Compass installed and in your system PATH
問題處理
 sudo apt-get install gcc ruby-dev rubygems  
 sudo gem install compass  

2.系統問題
Error: ENOSPC: System limit for number of file watchers reached
設定可watch數量
 vi /etc/sysctl.conf  
 fs.inotify.max_user_watches=524288  

3.shell切換使用
 執行 /bin/bash | zsh  
 設定 vi ~/.bashrc 或 .zshrc  
 應用 source ~/.bashrc  
 設定預設 chsh -s /bin/zsh  

4. Android Studio 問題
使用AVD 發生
/dev/kvm permission denied.錯誤
ubuntu18後需要
 1.sudo apt install qemu-kvm.  
 2.sudo adduser $USER kvm //將自己帳號加入到kvm群組內  
 3.重新啟動電腦 就可以用了  

5.升級node 10 gulp錯誤
錯誤 gulp[5761]: ../src/node_contextify.cc:635:static void node::contextify
執行 npm i natives 修正

6.JAVA_HOME
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64

2019年9月22日 星期日

在Linux上build Kitematic

最近開始將工作環境從mac搬到Linux上
之前在mac上使用docker會使用kitematic這個很方便的管理界面
到github官方站台上只有winodw與mac的release版本可以下載
Linux則是只有source code需要自己build 執行程式

下載source code 解開發現原來kitematic是一個electron應用程式
直接執行Makefile是有問題的,原因是makefile內引用的npm script有錯
只能選擇自己依照標準electron專案的步驟來build
所有需要的script都已經放在package.json內
只要依照順序執行即可
 npm install // 安裝專案需要的node module  
 npm install electron-packager // 因為需要打包成執行檔 需要安裝electron打包程式  
 npm run release:debian:x64 // 依照自己的作業系統 在package.json內找到適合的script執行  
打包好的程式就會產生在dist目錄下

注意 如果build好的kitematic 執行有錯誤

we can't find a native setup

只要改用sudo kitematic執行即可

2019年9月13日 星期五

中秋

騎著摩托車 載著妹妹 我們要去果園過中秋
沿著88快速道路下的車道 往潮洲騎去
妹妹 還是一路上 唱著歌

平常山上 大概都是老爸一個人在這
今年老媽提早一天來 要包粽子
小弟 也回來了
跟著大弟的車到山上
妹妹已經是 小孩裡的大姊姊
平常安靜的山上 除了收音機的歌聲
廚房做菜的聲音 小孩的吵鬧聲
還有我們兄弟 跟老爸聊天的聲響
4點多開始烤蕃薯 烤玉米
一直到8點多 放完煙火鞭炮
中秋 全家人可以團聚的快樂時光






2019年9月10日 星期二

sequelize 建立沒有primaryKey的 table model

當建立的 table model ,table並沒有任何欄位有設定primaryKey
model會預設建立一個id的欄位當作primary key
可能在做查詢時會因為這樣不存在的id而出錯
若建立的model 無pk存在 加入一段檢查
移除id attribute 即可

let havePK = false
fields.forEach(field => {
  if (field.primaryKey) havePK = true
})
const model = sequelize.define('tableName', fields, option)
if (!havePK) model.removeAttribute('id')

2019年9月8日 星期日

Side Project : node-graphql-api 後端資料服務

長期以來自己開發主要都著力都於前端
SPA的架構下,前後端是完全乾淨分離的開發模式
這架構下資料的來源 都仰賴API的服務提供

以前接案 後端的部分都是使用php寫API服務
之後技術發展移到nodejs上
剛好公司也因為沒有完整的API系統
自己也就跳下來使用nodejs建構一個基礎的架構方便使用
原本只是方便自己使用的架構
也衍生變成提供公司內多專案共通使用的API服務

在既有的包袱與人員的限制下
許多自己想要實作的部分 變的很難在現有專案上實作
因此才會以自己的side project
全新打造一個自己理想中的系統

project預期目標
1.提供可以多人共同開發的系統架構
  開發者只需關專於API的邏輯書寫,其他系統都會幫你搞定
2.標準Restful API資料服務
3.Graphql 資料服務
4.單元測試
   所有功能與api都透過單元測試確保穩定性與程式品質
5.封裝可執行檔
  可以執行檔做最簡單的部署到客戶端主機
6.提供通用資料查詢
  系統本身提供通用Restful API與 Graphql
  在安裝設定後,即可對資料庫table做CRUD處理

專案進行 持續維護中
node-api github位置
https://github.com/ozzysun/node-graphql-api






Side Project開發計畫

最近這段時間 開始在寫自己的side project
其實早該開始,只是一直以來總希望自己在技術想實作的部分
可以直接在公司的專案上可以實現
不過現實跟理想總有差距

為什麼會去寫side project?
有開發上的發想,不是就在公司的專案內實作就好了?
也許有些自己想玩的東西 不在公司計畫內,
亦或會有所限制 施展不開來,在自己的Side Project上
就可以百分之百 把自己的想法 想像都實作出來
利用自己的一點時間 把一些放在腦袋裡的想法
一個個的實作出來 也是蠻有趣的
有興趣的github連結在這
https://github.com/ozzysun

2019年9月6日 星期五

透過FCM API發送 firebase clound message(FCM) 推播訊息

最近在要使用FCM發送推播給客戶端
當然測試可以直接在firebase console上發送
不過最終的應用還是會希望在自己的後台程式在指定的時間
發送推播訊息給指定控制的設備

FCM 本身有提供 API可以執行
以下說明
1. api url:https://fcm.googleapis.com/fcm/send
2.驗證
  在heaer以Authorization 欄位帶入伺服器金鑰
  可以在firebase設定/cloudMessage/金鑰 或 舊版伺服器金鑰
  firebase每個帳戶可以開多個專案(project))每個專案有各自的伺服器金鑰
  每個專案下可以開多個應用程式,這些應用程式共用這專案下的資訊
2.傳送的目標
to 與registration_ids只能擇一使用
傳送給單一device使用to,一次傳送多個則使用registration_ids
其實是可以統一都使用一次傳送多個則使用registration_ids 即可

device的token 是在device在連上fcm service時可以取到
以此token用來辨識device

3.傳送訊息類型
notification背景接收的推播
  也就是app在背景或未被開啟時會接收到的訊息
  訊息有固定的格式
data前景接收
 推播傳送的資料可以自訂key-value方式設定



ex:
method: post
header:
  Content-Type:application/json
  Authorization: key= "" 這裏可以透過firebase設定/cloudMessage/金鑰 或 舊版伺服器金鑰
body
  "to": 要傳送的device Token,
  "registration_ids":[], // 當要傳送多個device
  "collapse_key" : "type_a",
  "data":{
      "user": "oz",
      "info": "test"
  },
  "notification" : {
      "body" : "test4!", 
      "title" : "test4",
      "icon" : ""
  }

2019年9月5日 星期四

sequelize-auto 建立json model

nodejs上ORM主要是以sequelize為最主要使用
sequelize-auto則是用來將現有的db table 轉成data model的工具
可建立 js module當作ORM操作時使用的model物件

最近因為除了拿來用data model使用外
還會需要利用 這取得table schehema做其他應用
像在graphql就可以拿來產生object type
希望除了輸出js module當作model物件使用外
還希望可以輸出json格式 做其他應用
原sequelize-auto並無這部分功能
這幾天改寫這部分
有需要者 歡迎取用
https://github.com/ozzysun/sequelize-auto
使用說明

建立js module
sequelize-auto -o "./models" -d dbName -h localhost -u username -p 3306 -x yourpassword -e mysql

建立json
sequelize-auto -o "./models" -d dbName -h localhost -u username -p 3306 -x yourpassword -e mysql -j

將產生的json轉成js module
const { getModel } = require('sequelize/lib/utils')
const model = await getModel(sequelize, jsonfilePath)

2019年9月4日 星期三

Graphql 巢狀結構查詢設計

要設計巢狀結構查詢
除了最上層會是建議query外 其他子結點都會是一個個的物件類型
query與物件的定義差別是
物件下並不會定義type而是有fields 來定義這個物件下包含的多個field定義
query下就會直接定義type 不會有fields的定義

多層次巢狀的物件結構 真正的資料操作可能只在某一層
但要注意的是 在巢狀結構下
上層的物件無論是否有作用 resolve一定要return
下層的物件才會被執行resolve
假設希望設計一隻通用的查詢功能
可以指定db主機,指定db,指定table做通用查詢的功能

1. table物件
fields即是完全對應到db table欄位
 tableObject {  
  name: table名稱,  
  fields: [  
   {  
    name: 欄位名稱1,  
    type: String|.. 對應欄位本身columnType  
   }..  
  ]  
 }  

2. db物件:
fields對應到db內所有的table
查詢取到的list 內容每一筆資料內容即是tableObj所定義的欄位
所以這裏的type會是tableObject 的list
這裏的resolve是真正執行db操作的地方
 DbObject {  
  name: db名稱  
  fields: [  
   {  
    name: tableName,  
    type: [tableObject]  
    resolve:() => {  
     真正執行的地方在這裡  
    }  
   }  
  ]  
 }  

3. host主機物件
fields內對應到主機內所有的db
每個field都是對應到一台db,所以類型會是dbType
 HostObject {  
  name: db名稱  
  fields: [  
   {  
    name: dbName,  
    type: DbObject,  
    resolve: async(parent, args, context, info) => {  
     // 這裡不需要做處理 但一定要回傳  
     return true  
    }  
   }  
  ]  
 }  

4.建立查詢
查詢即是這個程式的進入點type是主機物件
resolve沒有作用但必須要有return 才會往下解析
 mainQuery {  
  name: host,  
  type: HostObject,  
  resolve: () => {  
   return true  
  }  
 }  

2019年9月2日 星期一

graphql巢狀結構resolve處理

當schema有巢狀結構,需要在子元件上做resolve
例如 db/table/column 這樣的結構
ex:
db.js
{
  name: 'db',
  type: [table],
  resolve: async(parent, args, context, info) => {
    return 'xxxx'
  }
}
table.js
{
  name: 'table',
  type: [column],
  resolve: async(parent, args, context, info) => {
    // parent 是接收db resolve回傳的資料
    return 'yyyy'
  }
}
column.js
{
  name: 'column',
  type: String,
  resolve: async(parent, args, context, info) => {
    // parent 是接收table resolve回傳的資料
    // 真正執行程序
    return 'zzz'
  }
}

在每個type下都有自己的resolve做處理
巢狀結構會由最上層開始執行resolve
執行完的return內容會傳給子物件resolve內的parent接收
以上例子 真正的執行運算是在column上
即使上層物件不需要做任何resolve處理
也必須要有return值 否則子物件的resolve不會被觸發

pkg 動態require module

使用pkg可以封裝nodejs服務建立可執行應用程式
新版的pkg已經支援es6
不再需要如同舊版要先透過babel轉成es5才能封裝

封裝時會依照檔案內有require的module全部封裝進來
但如果是要需要動態require的部分
例如依照程式邏輯判斷需要require的檔案
這部分如果不例外處理 在runtime時無法判斷真正要require的檔案

這部分需要做以下幾點的處理
1.package.json內
加入提示pkg要動態載入的script目錄

"pkg": {
  "scripts": [
    "./src/routes/**/**/*.js",
    "./src/graphql/**/**/*.js"
  ],
  "targets": [
    "node10-macos-x64",
    "node10-win-x64"
  ]
}

2.不能直接引用require
需要把require method另外存放使用

ex:
global.requireExternal = require
就可以在要require動態模組時改用
global.requireExternal

3.注意檔案路徑
為確保在跑nodejs或封裝成應用程式
要require的模組路徑必須以__dirname組成正確完整路徑
ex: filePath = `${__dirname}/src/query`

4.sample code

global.require = require
files.forEach(filename => {
  const requirePath = `${__dirname}/folder/${filename}`
  const MyClass = global.require(requirePath)
  new MyClass()
})

2019年9月1日 星期日

以GraphqlSchema 建立Schema Object物件

在使用graphql 建立要使用的schema與resolve schema部分大多數都會以GraphQL schema language定義
// 使用GraphQL Schema Definition Language(SDL)定義schema
const typeDefs = gql`
  type Query {
    hello: String
  }
`;
// 建立resolve
const resolvers = {
  Query: {
    hello: () => 'Hello world!',
  },
};
// server運作使用
const server = new ApolloServer({ typeDefs, resolvers });
把定義好的typeDefs 與resolvers丟給server就可以跑了

但是如果建立schema的程序希望可以不要手寫SDL
可能是透過程序自動產生 就無法做到
ApolloServer 除了吃typeDefs, resolvers 參數外
直接產生好schema
透過
const server = new ApolloServer({ schema }); 執行
也是可以的

以下範例是說明完全利用Graphql.js來建立schma物件

const graphql = require('graphql')
const queryType = new GraphQLObjectType({
  name: 'Query', // 要建立的type名稱,整個graphql至少要有一個Query, 其他可以是自建類型名稱
  fields: {
    hello: { // 設定有一個hello的查詢
      type: graphql.GraphQLString, // 類型為基本scarla類型,可透過graphql取得
      args: { // 若這查詢可以提供輸入參數
        name: { //參數名稱
          type: graphql.GraphQLString, //參數類型
        },
        age: {
          type: graphql.GraphQLInt
        }
      },
      resolve: (parent, args, context, info) => { // 執行資料處理,args會拿到
        return `im....${args.name} age=${args.age}` 
      }
    },
    oz: {
      name: 'oz',
      type: graphql.GraphQLString
    }
  }
})
const schema = new GraphQLSchema({
  query: queryType
})
const server = new ApolloServer({ schema })
這樣就可以完全用js去建立產生schema運作

以上建立的查詢在client做以下查詢

{
  firstName(name: "oz")
  age
}
得到回應結果

{
  "data": {
    "firstName": "[oz]",
    "age": null
  }
}
api參考https://graphql.org/graphql-js/type/#graphqlobjecttype

2019年8月27日 星期二

在Android Studio開啟Cordova專案

使用cordova建立專案
通常都是透過指令直接操作
但也會有必須要使用Android Studio執行的狀況
以下是在Android Studio內執行Cordova 專案的流程

1.選擇import project(Gradle,Eclips)
2.選擇目錄:選取Cordova專案下的platform/android
  注意不要選取整個codova專案
3.出現錯誤 The minSdk version should not be declared in the android manifest file
  解法
  移除 android/CordovaLib/AndroidManifest.xml與
  android/app/src/main/AndroidManifest.xml內的
  <uses-sdk android:targetSdkVersion="28" android:minSdkVersion="19" />
4.重新執行File/Sync Project with Gradle 即可 執行

2019年8月21日 星期三

Summer's Rain

連續兩個多禮拜的雨
這個夏天 雨也下的夠久的了

退伍後到台北工作 即使沒帶多少行李
還是會把吉他帶在身邊
即使在辭掉工作回高雄一段時間
再回到台北 還是會把吉他帶著

曾有一小段時間在師大對面的樂器行學琴
下班後就從五股背著吉他騎車到和平東路

第一次去 老師 問我想彈什麼樣的音樂
我就把這張CD拿出來
這是我最喜歡的樂團吉他手
我想要這個~

2019年8月16日 星期五

vue-cli環境變數使用

vue-cli在執行許多的程式會需要在不同的情況(model)下
用到不同的環境變數
1.因此預設會有這3種mode
  development|test|production
2.不同mode使用的環境變數 會以下列檔名儲存
.env              # 任何mode都會載入的變數
.env.local        # 任何mode都會載入的變數,只限開發者自己使用的變數,檔案不進git
.env.[mode]       # 指定mode會載入的變數
.env.[mode].local # 指定mode會載入的變數,只限開發者自己使用的變數,檔案不進git
3.env檔案內 變數的設定方式
  以key=value方式寫
  如果是要讓client端可以拿去使用的變數名稱
  必須以VUE_APP_開頭命名
  否則都只有在node環境下才讀得到變數值
4.client端應用
  例 public/index.html本身就是一個樣板
  可用樣板語言,就可以拿client端可用的環境變數來做判斷
  使用或顯示
5.環境變數除了寫在特定的env檔案內以外
  也可以寫在vue.config.js設定檔內

2019年8月14日 星期三

vue-cli 3 webpack設定yaml讀取

目前已經將原本在框架下使用json設定檔案都改成yaml檔案
畢竟yml的結構比json要好上很多
在後端的nodejs上透過js-yaml即可讀取
前端上vue要使用
則需要透過webpack的js-yaml-loader的設定支援讀取
使用上可以很簡單的用
import data from 'js-yaml-loader!./conf/apis.yml'
這樣免設定的方式執行 但還是不太建議直接這樣用

如果是要設定到webpack的config上
使用vue-cli 3 會發現已經完全看不到webpack的設定
所有的設定已經都包裹在整個架構內
如果需要調整或增加webpack設定
可以在根目錄下建立vue.config.js檔案
用來覆寫或增加上定內容

參考vue-cli設定
https://cli.vuejs.org/guide/webpack.html#simple-configuration

以下sample code範例為
修改連線port 並加入yaml loader

module.exports = {
  devServer: {
    port: 1234
  },
  chainWebpack: config => {
    config.module
      .rule('yaml')
      .test(/\.ya?ml$/)
      .use('js-yaml-loader')
      .loader('js-yaml-loader')
      .end()
  }
}

2019年7月29日 星期一

nodejs 使用mocha+supertest做api測試

因為是使用mocha做單元測試
是可以把測試api 與測試function的部分
一起執行
幾個地方注意
1.當需要在執行API測試前後做一些資料處理
  例如塞入token 這部分都會是放在
  beforeEach與afterEach內執行
2.預期會是在執行beforeEach後才會去執行it測試
  但當beforeEach需要執行非同步的功能
  需帶入done callback 當作在執行完成時
  往下執行的通知點
3.beforeEach內要執行async method
  若直接用async/await執行會有問題時
  改用promise方式執行 可以解決
4.API測試 若回傳為json物件
  可以透過res.body拿到 object型態的資料
  若透過res.text 拿到的資料類型會是text 需要自己轉成物件
5.API測試 若回傳為text
  透過res.text 拿到取得的資料,若去拿res.body會是個空物件{}

使用參考
https://github.com/visionmedia/supertest
sample Code

describe('測試 sample 目錄', () => {
  beforeEach((done) => {
    // 測試前需要執行的工作
    runServer().then(response => {
      server = response.server
      api = supertest(server)
      done()
    })
  })
  afterEach((done) => {
    // 測試結束前需要執行的工作
    server.close()
    done()
  })
  it('sample/text 測試', (done) => {
    api.get('/sample/text')
      .expect('Content-Type', /text/)
      .expect(200)
      .end((err, response) => {
        if (err) return done(err)
        console.log('response text====')
        console.log(typeof response.text)
        console.log(response.body)
        done()
      })
  })
  it('sample/json 測試', (done) => {
    api.get('/sample/json')
      .expect('Content-Type', /json/)
      .expect(200)
      .end((err, response) => {
        if (err) return done(err)
        console.log('response json====')
        console.log(response.body)
        done()
      })
  })
})

2019年7月24日 星期三

nodejs 絕對路徑

執行 nodejs 可能會對 文件路徑與指令路徑有混淆的狀況
__dirname 目前為文件所在目錄
process.cwd() 執行指令所在的目錄
舉例來說
檔案目錄結構為
/myproject/bin/run
/myproject/src/utils/folder/01.js

在bin目錄下 執行 run ../src/utils/folder/01.js
__dirname = '/myproject/src/utils/folder'
process.cwd() = '/myproject/bin'

2019年7月21日 星期日

升級gulp4 'Did you forget to signal async completion?'錯誤解法

1.改為 async + await

gulp.task('build', async() => {
  await execBuild()
})
2.使用回傳的done
gulp.task('build', (done) => {
  execBuild()
  done()
})

2019年7月6日 星期六

暑假 長濱

在長濱住了幾天
這個在台東與花蓮交界處的小鎮
熱鬧的地方大概就是那短短的一條街
從台中過來開飯館的大姐
從高雄移居到這裡開民宿的資深工程師
還有從台南來到這裡開書店的書粥
聽說今年是最後一屆 的鹽寮海或瘋市集
自由的氣息 感覺像是嬉皮的天堂
















2019年6月27日 星期四

鍵盤造成iscroll問題

在手機上使用mobile web或 codova開發app
當focus在input上
鍵盤浮起時window.innerHeight是會被改變
當blur收起鍵盤後 innerHeight又會回復正常

如果content有使用iscroll
innerHeight的異動 可能就造成scroll異常
必須要在innerHeight異動時 重新依照取到的外部高度
設定iscroll wrapper的height 才能正常運作

2019年6月20日 星期四

Android 7後多語系的異動造成亂碼問題

最近遇到原本在Android上透過Socket做列印
安裝在Andorid7以上Device上 印出一堆的亂碼
查了一下
是7.0以後語系預設會讀到zh-Hant-TW
因此原本設定在res內的values-zh 就變成沒有被讀取
必須把目錄名稱改成values-zh-rTW才會正確顯示
而在Java檔案內需要判斷locale的部分
Locale.getDefault()拿到的值也會不同
如果需要做判斷做後續工作,這部分也需要被修改
附帶 Locale.getDefault()拿到的是Locale物件
需用toString轉換過 才能做字串比對
參考連結
https://litotom.com/2017/05/02/android7-locale-language/

2019年6月18日 星期二

使用babel-polyfill讓ES6 API可在舊版webview運作

現在的專案都是以ES6寫babel轉換成ES5
但預設並不會對於本身ES6 本身的API
像window.Set 必須在WebView 版本38以上才有支援
https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Set
所以即使是用babel轉換過的程式碼
在比較舊的Browser上 可能會無法運作
例如Android4.4上的WebView 版本為30
就無法執行

若要強制讓babel去轉換API就需要透過polyfill
注意 這裡是在程式裡面用所以是以--save安裝 而非 --save-dev
npm install babel-polyfill --save

在程式進入點(entry.js)的最上方
import 'babel-polyfill'

即可正常運作

2019年6月11日 星期二

感冒

這幾天感冒 發燒
請了兩天的假 感覺還是很虛弱
一直在昏睡的狀況
看著房間裡 大毛巾掛在架子上
廚房桌子上 洗好的碗 放在桌上
平常很懶的妹妹 知道爸爸生病
東西都自己收好 整理好

吃了藥 9點就跟妹說 爸爸要先睡覺了
閉上眼睛 躺著
妹妹把手臂 放在我的肚子上
就像是平常 他睡覺要我抱她那般
換他來抱爸爸
只感覺 他不自在地動來動去
張開眼睛看他 不自在趴著
妹妹 你睡好 爸爸抱你
然後妹妹側過身去
拉著我的手 環抱在他的肚子上
這一刻 是當爸爸的我感到無比幸福的時光

2019年6月1日 星期六

小紅豆

小紅豆是妹妹養的第一隻鬥魚
2017年原本阿姨給妹妹一隻蝦胖養
後來蝦胖死了 妹妹哭得好傷心
我們去買了妹妹的小紅豆


小紅豆 原本是養在蘋果汁玻璃罐內
慢慢長大了 給他換了大的瓶子
之間 也養了小藍莓
但 也只幾個月就走了



2019/5/29
這幾天 小紅豆 不再吃東西了
都只是靠在水面上 有一下沒一下的呼吸著
我知道 大概是要走了
兩年的時間 是小紅豆在我們家的日子
在妹妹的書架上
2019 6/1
假日跟妹妹再去水族館
再帶回我們第二代的小紅豆 跟小藍莓
看著他們活潑的游著
希望 可以像以前小紅豆一樣
長得健健又康康