ããã«ã¡ã¯ãLayerXããä¸äºç©ç£ãã¸ã¿ã«ã»ã¢ã»ããããã¸ã¡ã³ã(以ä¸MDM)ã«åºåä¸ã®ãµã«ãï¼@MasashiSalvador)ã§ããä¸ä½ 3ã¨SFãã¬ã¸ã³ã®ç°å¸¸è«æç¹éã¨å¿å¾ ã¡ã«ãã¦çãã¦ãã¾ãã
ã¨ã³ã¸ãã¢éå£ããå¹ççãªã¢ã»ããä¼ç¤¾ããä½ãã¨å¬ãã¿ãæ·±ãä»¶ ã«ããã¦@peroyuki_ ãæ¸ãã¦ããããã«ãMDMã§ã¯ãOperationã¨Techãæãåããã¦ãã©ãã¢ã»ããæ¥åããªãã¶ã¤ã³ããããã¨ããåãã«ææ¦ãã¦ãã¾ããå®éã«é¡§å®¢ã¨åãåããOperationãåããªãéãæ·±ã課é¡ãåºãå ¨ä½æãå¾ããã¨ã¯é£ãããããOperationã¨Techã®æãåããã¯ä¸çç¸ã§ã¯ããã¾ããããã®ãããªå°é£ãã«å¯¾å¦ãããããMDMã§ã¯æ¥µåå°ãããªãªã¼ã¹ãããã¨ãå¿ããã¦ãã¾ãã
æã«ã¯ããã¡æ©ãç«ã¡ä¸ããç®æããããã³ã¼ããã¼ã¹èªä½ãåãé¢ãã2,3åã»ã©ã®æ°ããå°ããªãã¼ã ãåãåºããéçºãã¯ã¤ãã¯ã«è¡ããã¨ãããã¾ããäºæ¥ãæ¥åã®ãã©ãã·ã¥ã¢ããã®æ¤è¨¼ãæ©ãè¡ãããã§ããã¾ããå°ããªéçºã®å¯æ¬¡çãªå¹æã¨ãã¦ãã©ã¤ãã©ãªãã¢ã¼ããã¯ãã£ãSaaS飿ºãªã©ã®æè¡æ¤è¨¼ã®ä½å°ãçã¾ãã¾ãã
MDMã§ã¯ãããã¾ã§ã«ããã¤ãã®SaaSã®æ¤è¨¼ã¨ãããã¯ãã®éçºãè¡ã£ã¦ãã¾ãããããæè³å®¶åããããã¯ãã§ãããããã©ãä¸åç£ã ããã®1ã¤ã§ããããã©ãä¸åç£ã«é¢ãã¦ããä¸è¿°ã®ããã«å°ããªéçºãã¼ã ãåãåºããçæéã§ã®ãªãªã¼ã¹ï¼ä¼ç»ããβçå ¬éã¾ã§ç´2ã¶æï¼ãè¡ãã¾ããã
æ¬è¨äºã¯ãããã©ãä¸åç£ãªãªã¼ã¹æã«ãããã¯ã¨ã³ãã®APIã®ãã¬ã¼ã ã¯ã¼ã¯ãgoa(v3)ããgo-swaggerã¸ã®åãæ¿ããè¡ã£ãçµç·¯ã«ã¤ãã¦ã話ãã¾ãã
goaã¨ã¯? go-swaggerã¨ã¯?å·®ã¯ï¼
goa(v3)ã¨ã¯ãä¸è¨ã®ãããªç¬èªDSLãæ¸ããã¨ã§ãhttpãªã¯ã¨ã¹ãã®å¦çï¼ããªãã¼ã·ã§ã³ãªã©ï¼ãã¬ã¹ãã³ã¹ãè¿ãã³ã¼ããèªåçæã§ãããã¬ã¼ã ã¯ã¼ã¯ã§ããå®è£
è
ã¯ãã¸ãã¹ãã¸ãã¯ã®å®è£
ã«éä¸ããã°è¯ããã¨ã«ãªãã¾ããv3ã¯GRPCã®ã¨ã³ããã¤ã³ãã®èªåçæã«ã対å¿ãã¦ããããããªãã«ä¾¿å©ã§ããgoaã®DSLã§APIã®å®ç¾©ãæ¸ãã¨ãå®ç¾©ã«å¯¾å¿ãã swagger.yaml
ãçæããã¾ããswagger.yamlãå
ã«APIå®ç¾©ãswaggerUIãªã©ã§å
±æãããã¨ãã§ãã¾ãã
// https://goa.design/ ããå¼ç¨ var _ = Service("calc", func() { Description("The calc service performs operations on numbers.") Method("add", func() { Payload(func() { Field(1, "a", Int, "Left operand") Field(2, "b", Int, "Right operand") Required("a", "b") }) Result(Int) HTTP(func() { GET("/add/{a}/{b}") }) GRPC(func() { }) }) Files("/openapi.json", "./gen/http/openapi.json") })
go-swagger ãå©ç¨ããå ´åã¯ç¬èªDSLã§ã¯ãªããopenAPI(swagger)ã§APIãå®ç¾©ããswagger.yamlããAPIã®ãªã¯ã¨ã¹ããã¬ã¹ãã³ã¹ãè¿ãã³ã¼ããèªåçæãã¾ãã
å®éãç¾å¨ã®MDMã®ãããã¯ãã§ã¯ãswaggerã®APIå®ç¾©ãã¡ã¤ã«ãä¸è¨ã®ããã«åå²ã
- definitions - index.yaml - Foo.yaml - Bar.yaml - paths - index.yaml - hoges/list.yaml /detail.yaml - root.yaml
ä¸è¨ã®ãããªã³ãã³ãã§swaggerãçµå& validateãããµã¼ããµã¤ãã®ã³ã¼ããèªåçæãã¦ãã¾ãã
yarn swagger-merger -i docs/swagger_files/root.yaml -o swagger.yaml swagger validate swagger.yaml swagger generate server -a mdm -A mdm --exclude-main --strict-additional-properties -t gen -m respmodel -P model.User -f ./swagger.yaml
çæãããroutingå¨ãã®ã³ã¼ã
// api config. api.JSONConsumer = runtime.JSONConsumer() api.JSONProducer = runtime.JSONProducer() authMiddleware := authMidldeWare{buRepo: buRepo} api.AuthorizationAuth = authMiddleware.exec // swaggerã§å®ç¾©ããã¨ã³ããã¤ã³ãã®ãã¹ã¨ãã³ãã©ã¼ãç´ä»ãã // ãã³ãã©ã¼ã®Handle颿°ã®åå®ç¾©ãèªåçæããã api.GetHcHandler = mdmbackoffice.GetHcHandlerFunc(func(params mdmbackoffice.GetHcParams) middleware.Responder { return mdmbackoffice.NewGetHcOK().WithPayload(nil) }) // swaggerã§å®ç¾©ããã¨ã³ããã¤ã³ãã®ãã¹ã¨ãã³ãã©ã¼ãç´ä»ãã // ãã³ãã©ã¼ã®Handle颿°ã®åå®ç¾©ãèªåçæããã smplHandler := sampleHandler.Sample{} api.GetSampleHandler = mdmbackoffice.GetSampleHandlerFunc(func(params mdmbackoffice.GetSampleParams, bu *model.User) middleware.Responder { // æã¨å ´åã«ãããuserã®æ¨©éãã§ãã¯ã®ã³ã¼ããæã¿è¾¼ã return smplHandler.Handle(params, bu) })
ã¬ã¹ãã³ã¹ã®åãèªåçæããã¾ãã
# /project_root/gen/respmodel/ - foo.go # Fooã®responeã®å - bar.go
goaã¨go-swaggerã¯ã©ã¡ãã便å©ã§ãã
- APIå®ç¾©ã®æ¸ãããããæè»æ§
- middlewareã®å·®ãè¾¼ã¿ã®æè»æ§
- Cookieã®æ±ãããã
- ã³ã¼ãçæã®æè»æ§
- goa(v3)ã®åãåºãswaggerãopenapi 3.0ãªã®ã§ãã³ã¼ãçæç³»ãæªæç(ããã¯ã¨ã³ããããã³ãã¨ã³ããï¼
ãªã©ã®ç¹ãå å³ããã¨ãããããã®ã¦ã¼ã¹ã±ã¼ã¹ã§ã¯go-swaggerã®æ¹ãé©ãã¦ãã¾ããã
MDMã®ãããã¯ãã§ã¯ãããã¯ã¨ã³ãã¨ããã³ãã¨ã³ãéã®ã»ãã·ã§ã³ã®ããåãã«Cookieãç¨ãã¦ãã¾ãããç¹å®ã®ç§å¯ä¿æå¥ç´(Confidential Agreement = CA)ãçµãã 顧客ã«ã®ã¿é²è¦§ãããããªã½ã¼ã¹ã®ã¢ã¯ã»ã¹ç®¡çã«ãCloudfrontã®ç½²åä»ãCoookieãå©ç¨ãã¦ãããããCookieã®åãæ±ããèªç¶ã«è¡ããªãgoaã使ãç¶ããã®ãè¦ãããªãã¾ããã
Announcing Goa v3.2.0 ã Add ability to design HTTP cookies #1717 ã«è¨è¼ããã¦ããããã«Cookieã®DSLã¯åå¨ãã¾ãããæè»ãªèªã¿æ¸ãã¯ã§ãã¾ãããresponseã®encoderãç¬èªå®è£ ããç¹å®ã®æ¡ä»¶ä¸ã§ã®ã¿ãããã©ã«ãã®ã³ã¼ããresponseãè¿ãåã«responseã䏿¸ããããããªã³ã¼ããæ¸ãã¯ã¼ã¯ã¢ã©ã¦ã³ããå®è£ ãã¾ããããencoderå ã§contextãåãåããªããªã©ãhandlerã¬ã¤ã¤ã¨ã®æ å ±é£æºããã¾ãããã¾ããã
ãã¼ã ã¡ã³ãã¼ãDSLã調æ»ããæéçã³ã¹ããè¨ããã§ãããããgo-swaggerã¸ã®ä»¥éãæ±ºãã¾ããã
go-swaggerã¸ã®ç§»è¡ã«ããã³ã³ãã¼ãã³ãéåã³Saasã¨ã®é£æº
go-swaggerã¸ç§»è¡ãããã¨ã§ãopenapi 2.0ã®ã³ã¼ãçæç³»ã使ããããã«ãªãã¾ãããããã«ãããããã³ã¨ã³ããã³ã¼ãçæã®æ©æµãåããããããã«ãªãã¾ããã
// package.json "scripts": { "gen-webapp-interface": "rm -rf types/response && openapi-generator-cli generate -g typescript-axios -i swagger.yaml -o ./types/response" }
çæãããAPIã¯ã©ã¤ã¢ã³ããããã³ãã¨ã³ãã§å©ç¨
methods: { async getHogeList (page: number) { const api = new DefaultApi() const userId = this.currentUserId // èªåçæãããapiã®ã¯ã©ã¤ã¢ã³ãã³ã¼ããå¼ã³åºãã const result = await api.getHoges(userId, page).catch((e) => { console.error(e) }) if (!result) { alert('hoge') return } }
èªåçæãããAPIã¯ã©ã¤ã¢ã³ãã®é¢æ°åãé·ãã£ãããã¢ãã«åHogeã«å¯¾ã㦠struct Hoge1ãªã©ã¨ãã³ããªã³ã°ããã¦ãã¾ãç¹ã«èªãã§ããã¨ãã¾ã«éåæãæãã¾ããã人éãæ¸ãã¦ããã³ã¼ãã§ã¯ãªãã®ã§ãæ £ããã°ãããããã®ã ã¨æãã¾ãã
MDMã§ã¯åã³ã³ãã¼ãã³ãã®APIãswaggerã§å®ç¾©ããAPIã¯ã©ã¤ã¢ã³ãã®èªåçæãã¬ã¹ãã³ã¹åã®èªåçææ©æ§ãå©ç¨ãããã¨ã§ã飿ºã«ãããææ°ãæ¸ããã¦ãã¾ããã¾ããBoxãDocusignãªã©å ¬å¼ã®Goã®SDKãåå¨ããªãSaaSã¨ã®é£æºã«ããã¦ããå½¼ããå ¬éãã¦ããswaggerããçæããapiã¯ã©ã¤ã¢ã³ããå©ç¨ãã¦ãã¾ãï¼ç¬èªã®ã¯ã©ã¤ã¢ã³ããéçºããã«æ¸ããOAuthãã¼ã¯ã³ã®ãªãã¬ãã·ã¥æ©æ§ã ãèªåã§ã¢ã¬ã³ã¸ãå¿ è¦ï¼
ç¾ç¶ã®èª²é¡ã»ä»å¾ã®å±æ
swagger(open api2.0)ãç¨ããã³ã¼ãçæã¯ä¾¿å©ãªã®ã§ãããswaggerå´ã®åã¨Goå´ã®åã®å¯¾å¿ä»ããã¾ãã¡ãªã®ã¨ãswaggerã§çæããã¢ãã«ã¨xoã§çæããã¢ãã«(DBã®åãã¼ãã«ã®ã¬ã³ã¼ãã«å¯¾å¿ï¼ãxoã§çæããã¢ãã«ãåãè¾¼ã¿çµã¿åãããã¢ãã«(emmbed model) ã®éã®å¤æã«ãããªãã«ç¥çµã使ã£ã¦ãã¾ã£ã¦ãã¾ãã
// api/emodel/hoge.go type Hoge struct { Hoge *model.Hoge HogeImages *model.HogeImage `gorm:"foreignkey:HogeID;references:id"` } // api/gen/hoge.xo.go # èªåçæ // hogeimage.xo.go import ( "database/sql" "encoding/csv" "time" "github.com/LayerXcom/mdm/api/lib/csvconv" ) const ( TableHoge = "hoges" // ColumnHoge* represents a column name. CHogeID = "id" .... ... api/gen/respmodel/hoge.go // swagger:model Hoge type Hoge struct { // access description // Required: true Description *string `json:"description"` // address // Required: true Address *string `json:"address"` } ...
ã¾ããopenapi 3.0ã¨ã³ã¼ãçææ©æ§ã®æªæçãï¼Goã®ã¯ã©ã¤ã¢ã³ãçæã¯ãåå®ç¾©ãæ¬ è½ãã¦ãã¦ãã«ãã¨ã©ã¼ãåºãã¬ãã«ï¼ãéã¿ãã¨ãä»å¾ã®ã¡ã³ããããªãã£ã«è¥å¹²ã®ä¸å®ãæãã¾ãããã®ãããMDMã§ããGraphQLã«ããLayerX ã¤ã³ãã¤ã¹ ã¯ã¼ã¯ããã¼æ©è½ã®ã¢ãã«è¨è¨ ã«ããããã«GraphQLåã³ã³ã¼ãçææ©æ§ã®å©ç¨ã¸ã®ã·ãããç¨ãã§ãã¾ãã
ãããã«
ã¹ã¢ã¼ã«ãªãã¼ã ã§ç´ æ©ãéçºããã®ã好ããªæ¹ããç´ æ©ãåãã¤ã¤ãæå
ãå®ããåºããã®ã好ããªæ¹ããMDMãå«ãLayerXã§ã¯çµ¶è³ä»²éãåéä¸ã§ãã
100xã®ãã®å
ã¸ã Technologyã¨Operationã® <harmony />
ãå®ç¾ãããæ¹ãè¡ãã¾ãããï¼åããå´ã¸ï¼
ã¾ãã¯ã話ã ãã§ãï¼ãå¾ ã¡ãã¦ããã¾ãï¼ herp.careers