Share
Embed
REST is not only (web) API interface
RESTだけじゃなくて他にもインターフェイスは色々あるという話です。
「そうだGo、京都。」で発表。
参考リンクは https://gist.github.com/lufia/79a17579be449cf4e9aad790b58db284 に置きました。
kadota kyohei
April 29, 2017
Transcript
46 slides
-
ࣗݾհ • ଟګฏ • Favorites • OS: Plan 9 •
Editor: acme • Shell: rc • ϑΣϯϦϧͰಇ͍͍ͯ·͢ -
(ࢀߟ)APNsͷϖΠϩʔυ { "aps": { "alert": { "title": "αϯϓϧ", "body": "͜Εςετ௨Ͱ͢",
"action-loc-key": "PLAY" }, "badge": 5 }, "attr1": "bar", "attr2": [ "bang", "whiz" ] } -
(ࢀߟ)FCMͷϖΠϩʔυ { "to": "(τʔΫϯจࣈྻ)", "notification": { "title": "αϯϓϧ", "body": "͜Εςετ௨Ͱ͢",
"icon": "myicon" }, "data": { "attr1": "bar", "attr2": "bang" } } -
ૹ৴API (v1) POST /devices/:device_token Content-Type: application/json X-Priority: high X-TTL: 300
{ "title": "αϯϓϧ", "body": "͜Εςετ௨Ͱ͢", "icon": "myicon", "data": { "attr1": "bar", "attr2": "bang" } } σόΠεຖʹURIׂΓͯ ݻ༗ͷϔομͰ༩͑Δ -
ૹ৴API (v2) POST /messages Content-Type: application/json [ { "tokens": ["token1",
"token2"], "title": "αϯϓϧ", "body": “͜Εςετ௨Ͱ͢", "priority": "high" }, { "tokens": ["token3", "token4", "token5"], "title": "αϯϓϧ2", "body": "͜Ε2ͭͷςετ௨Ͱ͢" } ] URI·ͱΊΔ -
gRPC • HTTP/2 • Protocol Buffer version 3 • GoogleʹΑͬͯ։ൃ͞Εͨ(ݱࡏOSS)
• Go, Java, C++, Ruby, C#, Node.js, PHP ... -
GoͰgRPC // ίϚϯυͷΠϯετʔϧ $ brew install protobuf $ go get
-u github.com/golang/protobuf/protoc-gen-go // ΤσΟλͰprotoϑΝΠϧΛॻ͘ $ acme service.proto // ίʔυੜ $ protoc --go_out=plugins=grpc:. service.proto gRPCίʔυੜ ग़ྗσΟϨΫτϦ -
GoͰgRPC // service.proto service PushService { rpc Send (Message) returns
(stream Result); } message Message { repeated string tokens = 1; Priority priority = 2; string payload = 3; int32 ttl = 4; } enum Priority { HIGH = 0; NORMAL = 1; } -
GoͰgRPC import ( "log" pb "example.com/push/proto" "google.golang.org/grpc" ) type Server
struct{} func (*Server) Send(msg *pb.Message, stream pb.PushService_SendServer) error { log.Println(msg.Payload) var res pb.Result stream.Send(&res) return nil } ΠϯλʔϑΣΠεΛຬͨͤ͞Δ -
GoͰgRPC import ( "log" pb "example.com/push/proto" "google.golang.org/grpc" ) type Server
struct{} func (*Server) Send(msg *pb.Message, stream pb.PushService_SendServer) error { log.Println(msg.Payload) var res pb.Result stream.Send(&res) return nil } -
GoͰgRPC import ( "log" pb "example.com/push/proto" "google.golang.org/grpc" ) func main()
{ l, err := net.Listen("tcp", ":8080") if err != nil { log.Fatalln(err) } s := grpc.NewServer() pb.RegisterPushServiceServer(s, &Server{}) s.Serve(l) } -
GoͰgRPC service PushService { rpc Send (Message) returns (stream Result);
} ୯ൃϨεϙϯε ෳϨεϙϯε ୯ൃϦΫΤετ 6OBSZ31$ 4FSWFSTUSFBNJOH31$ ෳϦΫΤετ $MJFOUTUSFBNJOH31$ #JEJSFDUJPOBMTUSFBNJOH31$ छྨʹΑͬͯϝιουͷγάωνϟ͕มΘΔ -
ࢀߟࢿྉ • Protocol Buffer Language Guide • protoc-gen-go parameters •
gRPC Basics - Creating the server • GRPCͷ࣮ફͱݱঢ়Ͱͷརɾܽ -
GraphQLΫΤϦ POST /graphql HTTP/1.1 { repository { owner organization issues
{ title body html_url comments { body created_at updated_at } created_at updated_at } } } ※ GitHub෩ͷAPIͰ͢ HTTP/1.1 200 OK { "owner": "lufia", "organization": null, "issues": [ { "title": "add Plan 9 support", "body": "I should support Plan 9.", "html_url": "https://github.com/lufia/taskfs/issues/1", "comments": [ { "body": "9P implementations", "created_at": "2016-12-11T23:14:00+09:00", "updated_at": "2016-12-11T23:14:00+09:00" } ], "created_at": "2016-12-11T23:13:00+09:00", "updated_at": "2016-12-11T23:13:00+09:00" }, { ... } ] } -
GraphQLΫΤϦ POST /graphql HTTP/1.1 { repository { owner organization issues
{ title body html_url comments { body created_at updated_at } created_at updated_at } } } ※ GitHub෩ͷAPIͰ͢ ࠷ॳ͔͜͜Β -
GraphQLΫΤϦ POST /graphql HTTP/1.1 { repository { owner organization issues
{ title body html_url comments { body created_at updated_at } created_at updated_at } } } ※ GitHub෩ͷAPIͰ͢ repositoryΩʔΛ୳͢ -
GraphQLΫΤϦ POST /graphql HTTP/1.1 { repository { owner organization issues
{ title body html_url comments { body created_at updated_at } created_at updated_at } } } ※ GitHub෩ͷAPIͰ͢ repositoryΦϒδΣΫτ -
GraphQLΫΤϦ POST /graphql HTTP/1.1 { repository { owner organization issues
{ title body html_url comments { body created_at updated_at } created_at updated_at } } } ※ GitHub෩ͷAPIͰ͢ ownerΩʔΛ୳͢ -
GraphQLΫΤϦ POST /graphql HTTP/1.1 { repository { owner organization issues
{ title body html_url comments { body created_at updated_at } created_at updated_at } } } ※ GitHub෩ͷAPIͰ͢ organizationΩʔΛ୳͢ -
GoͰGraphQL(ϑΟʔϧυ) import ( "github.com/graphql-go/graphql" ) var RepositoryType = graphql.NewObject(graphql.ObjectConfig{ Name:
"Repository", Fields: graphql.Fields{ "owner": &graphql.Field{ ... }, "organization": &graphql.Field{ ... }, "issues": graphql.Field{ ... }, }, }) -
GoͰGraphQL(ͷղܾ) type Issue struct{ ... } var IssueType = graphql.NewObject(graphql.ObjectConfig{
Name: "Issue", Fields: graphql.Fields{ "title": &graphql.Field{ Type: graphql.NonNull(graphql.String), Description: "λΠτϧ", Resolve: func(p graphql.ResolveParams) (interface{}, error) { if v, ok := p.Source.(*Issue); !ok { return "", nil } return v.Title, nil }, }, "body": &graphql.Field{ ... }, "html_url": &graphql.Field{ ... }, }, }) -
GoͰGraphQL func main() { schema, err := graphql.NewSchema(graphql.SchemaConfig{ Query: QueryType,
}) http.HandleFunc("/graphql", func(w http.ResponseWriter, r *http.Request){ q, err := ioutil.ReadAll(r.Body) res := graphql.Do(graphql.Params{ Schema: schema, RequestString: string(q), }) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(res) }) http.ListenAndServe(":8080", nil) } طଘAPIʹͤΔͷͰ͓खܰ -
ࢀߟࢿྉ • GraphQL (ݴޠ༷) • Introduction to GraphQL • GraphQLೖ
- ͍ͨ͘ͳΔGraphQL • GitHub GraphQL API (Early Access) -
FUSE • Filesystem in Userspace • Plan 9 • FUSE,
FUSE for macOS, Dokan -
FUSEͷಈ࡞ • ϢʔβϓϩηεͰϑΝΠϧγεςϜ • γεςϜίʔϧʹదͳԠΛฦ͢ • ͍͍ͩͨશ෦Ͱ30ݸఔ • ඞཁͳ͚࣮ͩ͢Δ •
࣮ࡍͷ௨৴ͳΜͰ͍͍(HTTP, MQTT, 9P) -
GoͰFUSE import ( "github.com/hanwen/go-fuse/fuse" "github.com/hanwen/go-fuse/fuse/nodefs" ) type Root struct {
nodefs.Node } func (root *Root) GetAttr(out *fuse.Attr, file nodefs.File, ctx *fuse.Context) fuse.Status { out.Mode = fuse.S_IFDIR | 0755 out.Atime = uint64(time.Now().Unix()) out.Mtime = uint64(time.Now().Unix()) return fuse.OK } func main() { root := &Root{Node: nodefs.NewDefaultNode()} s, _, err := nodefs.MountRoot("/mnt/taskfs", root, &opts) s.Serve() } rootσΟϨΫτϦͱΧʔωϧʹ͑Δ ଞͷϝιουΛ࣮͍ͯ͠ͳ͍ͷͰɺۭͷσΟϨΫτϦΛϚϯτ͢Δ͚ͩ -
GoͰFUSE func (root *Root) Lookup(out *fuse.Attr, name string, ctx *fuse.Context)
(*nodefs.Inode, fuse.Status) { _, status := root.readDir() if status != fuse.OK { return nil, status } c := root.lookupName(name) return c.GetAttr(out, nil, ctx) } func (root *Root) OpenDir(ctx *fuse.Context) ([]fuse.DirEntry, fuse.Status){ kids, err := root.readDir() a := make([]fuse.DirEntry, len(kids)) for i, kid := range kids { kid.Stat().FillDirEntry(&a[i]) } return a, fuse.OK } σΟϨΫτϦΤϯτϦΛදݱ͢Δ߹͜ͷลΓͷϝιουΛ࣮͢Δ -
GoͰFUSE func (ctl *Ctl) Open(flags uint32, ctx *fuse.Context) (nodefs.File, fuse.Status)
{ p, err := ctl.ReadFile() if err != nil { return nil, fuse.EIO } return nodefs.NewDataFile(p), fuse.OK } func (ctl *Ctl) Truncate(file nodefs.File, size uint64, ctx *fuse.Context) fuse.Status { return fuse.OK } func (ctl *Ctl) Write(file nodefs.File, data []byte, off int64, ctx *fuse.Context) (uint32, fuse.Status) { err := ctl.WriteFile(data) if err != nil { return 0, fuse.EINVAL } return uint32(len(data)), fuse.OK } ϑΝΠϧૢ࡞Λ͢Δ߹ͷϝιουͳͲ