# integration team questionnaire 2022 June ## overview This exam contains following sections * coding practice * golang insight * communicating technical issue ## coding practice * extract and transform data is one of the foundation feature of API gateway. * the coding practice is to write simple transformation to show your coding/design skills using golang ### sample json ``` { "version": "1.0", "rules": [ { "resource": { "path": "/api/data/documents" }, "allowOrigins"["http://this.example.com","http://that.example.com"], "allowMethods": [ "GET" ], "allowCredentials": true } ] } ``` ### expected result * read json file with camel case key field and write out coverted key field name as snake_case * using stand library only. don’t use any 3rd party library/framework * allowed language: golang :::success ``` type Rule struct { Resource struct { Path string `json:"path"` } `json:"resource"` AllowOrigins []string `json:"allowOrigins"` AllowMethods []string `json:"allowMethods"` AllowCredentials bool `json:"allowCredentials"` } type InputStruct struct { Version string `json:"version"` Rules []Rule `json:"rules"` } type NewRule struct { Resource struct { Path string `json:"path"` } `json:"resource"` AllowOrigins []string `json:"allow_origins"` AllowMethods []string `json:"allow_methods"` AllowCredentials bool `json:"allow_credentials"` } ``` ``` func main() { //為了打印結果,在生產環境可直接將interface resposne給前端 log.Println(string(ResultStructTagsModify().([]byte))) } func ResultStructTagsModify() interface{} { input := `{ "version": "1.0", "rules": [ { "resource": {"path": "/api/data/documents"}, "allowOrigins": [ "http://this.example.com", "http://that.example.com"], "allowMethods": [ "GET" ], "allowCredentials": true } ] }` var inputData InputStruct if err := json.Unmarshal([]byte(input), &inputData); err != nil { log.Panic(err) } newRules := make([]NewRule, 0) for _, rule := range inputData.Rules { var newRule NewRule newRule.Resource = rule.Resource newRule.AllowOrigins = rule.AllowOrigins newRule.AllowMethods = rule.AllowMethods newRule.AllowCredentials = rule.AllowCredentials newRules = append(newRules, newRule) } newStruct := struct { Version string `json:"version"` Rules []NewRule `json:"rules"` }{ Version: inputData.Version, Rules: newRules, } result, err := json.Marshal(newStruct) if err != nil { log.Panic(err) } return result } ``` ::: ### food for thought * what will you do to handle generic/arbitrary json format? :::success <font color="black">使用interface{} type 或者以 reflect.typeOf() 來判斷型別</font> ::: ## golang insight ### OOP ``` // You can edit this code! // Click here and start typing. package main import "fmt" type data struct { num int key *string items map[string]bool } func (this data) updateData(value int) { this.num = value *this.key = "updated" this.items["updateData"] = true } func main() { key := "first" d := data{1, &key, make(map[string]bool)} fmt.Printf("num=%v key=%v items=%v\n", d.num, *d.key, d.items) d.updateData(8) fmt.Printf("num=%v key=%v items=%v\n", d.num, *d.key, d.items) } ``` * what would be the output? :::success ``` num=1 key=first items=map[] num=8 key=updated items=map[updateData:true] ``` ::: * what would you modify to make better result? why? :::success ``` type data struct { Num int Key *string Items map[string]bool } type IData interface { InitData() UpdateData() } func (r *data) InitData() { fmt.Printf("num=%v key=%v items=%v\n", r.Num, &r.Key, r.Items) } func (r *data) UpdateData() { fmt.Printf("num=%v key=%v items=%v\n", r.Num, &r.Key, r.Items) } func main() { var d IData // initial key := `first` d = &data{1, &key, map[string]bool{}} d.InitData() // update key = "updated" d = &data{ Num: 8, Key: &key, Items: map[string]bool{"updated": true}, } d.UpdateData() } ``` <font color="black"> 將initial, update 改寫成物件形式,可以提升可讀性、及方便管理functions。 </font> ::: ### loop variable ``` func main() { samples := []string{"cloud", "d2nova", "evox"} for _, name := range samples { go func() { fmt.Println(name) }() } // wait before exiting time.Sleep(3 * time.Second) } ``` * what would the possible output of this code snippet :::success <font color="black"> evox<br>evox<br>evox </font> ::: * what is the root cause/explanation the result? is that a bug or feature of golang? :::success <font color="black"> Race Condition<br> 執行過程中創建的goroutine同時對name做存取 </font> ::: * what would you do to change the code for better result? why? :::success ``` func main() { var wg sync.WaitGroup samples := []string{"cloud", "d2nova", "evox"} for _, name := range samples { wg.Add(1) go func(n string) { fmt.Println(n) wg.Done() }(name) } wg.Wait() } ``` <font color='black'> 改以依序傳入name value,就可以避免race condition, 同時將time.sleep()改用waitGroup,資料量大時waitGroup能保證線程安全; 資料量小時,資料處理完成也不需等待time.sleep()結束。 </font> --- ``` 結果: cloud evox d2nova ``` ::: ## written/oral communication ### oauth2 * what is oauth2? write down your understanding. :::success <font color="black"> 可設有oauth server 獨立於主要的業務邏輯API之外, 對oauth server 發出請求可以得到包含access token, expired, scope 等資訊。<br> 接著clients 將access token攜帶於reqest中向主要的業務邏輯API發出請求。<br><br> 我的實際經驗也有使用過Google Oauth,係向google 驗證登入之email address是否為合法的,frontend可以取得一個token,將之傳遞給backend,由backend使用該token向google驗證並取回登入資訊。由backend使用該token向google驗證並取回登入資訊。 </font> ::: * if need to explain to 3rd party developer to allow them to access your api, what will you do? :::success <font color="black"> 可使用Swagger API格式或者使用類似hackmd或者Gitbook有支援markdown格式的編輯器撰寫API spec. 下例是曾經寫過的使用markdown撰寫的API spec </font> ::: ### Register :::info POST v1/User/Register >> >> |Body|Type|Description|Format| |:--:|:--:|:---------:|:----:| |Email*|String|信箱|x-www-form-urlencoded,JSON| |Pwd*|String|密碼|x-www-form-urlencoded,JSON| |Name*|String|真實姓名|x-www-form-urlencoded,JSON| |NickName*|String|暱稱|x-www-form-urlencoded,JSON| |Birthday|String|生日|x-www-form-urlencoded,JSON| :::