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()
})