├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── complete ├── books.proto ├── books │ └── books.pb.go ├── client.go ├── client.js └── server.js ├── start ├── books │ └── books.pb.go └── client.go ├── step-1-list-books ├── books.proto └── server.js ├── step-2-insert-books ├── books.proto └── server.js ├── step-3-get-and-delete-books ├── books.proto └── server.js ├── step-4-stream-books ├── books.proto └── server.js └── step-5-node-client ├── books.proto └── client.js /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Want to contribute? Great! First, read this page (including the small print at the end). 2 | 3 | ### Before you contribute 4 | Before we can use your code, you must sign the 5 | [Google Individual Contributor License Agreement] 6 | (https://cla.developers.google.com/about/google-individual) 7 | (CLA), which you can do online. The CLA is necessary mainly because you own the 8 | copyright to your changes, even after your contribution becomes part of our 9 | codebase, so we need your permission to use and distribute your code. We also 10 | need to be sure of various other things—for instance that you'll tell us if you 11 | know that your code infringes on other people's patents. You don't have to sign 12 | the CLA until after you've submitted your code for review and a member has 13 | approved it, but you must do it before we can put your code into our codebase. 14 | Before you start working on a larger contribution, you should get in touch with 15 | us first through the issue tracker with your idea so that we can help out and 16 | possibly guide you. Coordinating up front makes it much easier to avoid 17 | frustration later on. 18 | 19 | ### Code reviews 20 | All submissions, including submissions by project members, require review. We 21 | use Github pull requests for this purpose. 22 | 23 | ### The small print 24 | Contributions made by corporations are covered by a different agreement than 25 | the one above, the 26 | [Software Grant and Corporate Contributor License Agreement] 27 | (https://cla.developers.google.com/about/google-corporate). -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2016 Google Inc. 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Google Cloud Platform - Building a gRPC service with Node.js 2 | 3 | This repository contains the source material for the Node.js gRPC codelab 4 | released at Google Cloud Platform Events. [Access the codelab](https://codelabs.developers.google.com/codelabs/cloud-grpc/index.html). 5 | 6 | *This is not an official Google product* 7 | -------------------------------------------------------------------------------- /complete/books.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2016, Google, Inc. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // Unless required by applicable law or agreed to in writing, software 7 | // distributed under the License is distributed on an "AS IS" BASIS, 8 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | // See the License for the specific language governing permissions and 10 | // limitations under the License. 11 | 12 | syntax = "proto3"; 13 | 14 | package books; 15 | 16 | service BookService { 17 | rpc List (Empty) returns (BookList) {} 18 | rpc Insert (Book) returns (Empty) {} 19 | rpc Get (BookIdRequest) returns (Book) {} 20 | rpc Delete (BookIdRequest) returns (Empty) {} 21 | rpc Watch (Empty) returns (stream Book) {} 22 | } 23 | 24 | message Empty {} 25 | 26 | message Book { 27 | int32 id = 1; 28 | string title = 2; 29 | string author = 3; 30 | } 31 | 32 | message BookList { 33 | repeated Book books = 1; 34 | } 35 | 36 | message BookIdRequest { 37 | int32 id = 1; 38 | } 39 | -------------------------------------------------------------------------------- /complete/books/books.pb.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016, Google, Inc. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // Unless required by applicable law or agreed to in writing, software 7 | // distributed under the License is distributed on an "AS IS" BASIS, 8 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | // See the License for the specific language governing permissions and 10 | // limitations under the License. 11 | 12 | // Code generated by protoc-gen-go. 13 | // source: books.proto 14 | // DO NOT EDIT! 15 | 16 | /* 17 | Package books is a generated protocol buffer package. 18 | 19 | It is generated from these files: 20 | books.proto 21 | 22 | It has these top-level messages: 23 | Empty 24 | Book 25 | BookList 26 | BookIdRequest 27 | */ 28 | package books 29 | 30 | import proto "github.com/golang/protobuf/proto" 31 | import fmt "fmt" 32 | import math "math" 33 | 34 | import ( 35 | context "golang.org/x/net/context" 36 | grpc "google.golang.org/grpc" 37 | ) 38 | 39 | // Reference imports to suppress errors if they are not otherwise used. 40 | var _ = proto.Marshal 41 | var _ = fmt.Errorf 42 | var _ = math.Inf 43 | 44 | // This is a compile-time assertion to ensure that this generated file 45 | // is compatible with the proto package it is being compiled against. 46 | // A compilation error at this line likely means your copy of the 47 | // proto package needs to be updated. 48 | const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package 49 | 50 | type Empty struct { 51 | } 52 | 53 | func (m *Empty) Reset() { *m = Empty{} } 54 | func (m *Empty) String() string { return proto.CompactTextString(m) } 55 | func (*Empty) ProtoMessage() {} 56 | func (*Empty) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } 57 | 58 | type Book struct { 59 | Id int32 `protobuf:"varint,1,opt,name=id" json:"id,omitempty"` 60 | Title string `protobuf:"bytes,2,opt,name=title" json:"title,omitempty"` 61 | Author string `protobuf:"bytes,3,opt,name=author" json:"author,omitempty"` 62 | } 63 | 64 | func (m *Book) Reset() { *m = Book{} } 65 | func (m *Book) String() string { return proto.CompactTextString(m) } 66 | func (*Book) ProtoMessage() {} 67 | func (*Book) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } 68 | 69 | type BookList struct { 70 | Books []*Book `protobuf:"bytes,1,rep,name=books" json:"books,omitempty"` 71 | } 72 | 73 | func (m *BookList) Reset() { *m = BookList{} } 74 | func (m *BookList) String() string { return proto.CompactTextString(m) } 75 | func (*BookList) ProtoMessage() {} 76 | func (*BookList) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } 77 | 78 | func (m *BookList) GetBooks() []*Book { 79 | if m != nil { 80 | return m.Books 81 | } 82 | return nil 83 | } 84 | 85 | type BookIdRequest struct { 86 | Id int32 `protobuf:"varint,1,opt,name=id" json:"id,omitempty"` 87 | } 88 | 89 | func (m *BookIdRequest) Reset() { *m = BookIdRequest{} } 90 | func (m *BookIdRequest) String() string { return proto.CompactTextString(m) } 91 | func (*BookIdRequest) ProtoMessage() {} 92 | func (*BookIdRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } 93 | 94 | func init() { 95 | proto.RegisterType((*Empty)(nil), "books.Empty") 96 | proto.RegisterType((*Book)(nil), "books.Book") 97 | proto.RegisterType((*BookList)(nil), "books.BookList") 98 | proto.RegisterType((*BookIdRequest)(nil), "books.BookIdRequest") 99 | } 100 | 101 | // Reference imports to suppress errors if they are not otherwise used. 102 | var _ context.Context 103 | var _ grpc.ClientConn 104 | 105 | // This is a compile-time assertion to ensure that this generated file 106 | // is compatible with the grpc package it is being compiled against. 107 | const _ = grpc.SupportPackageIsVersion4 108 | 109 | // Client API for BookService service 110 | 111 | type BookServiceClient interface { 112 | List(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*BookList, error) 113 | Insert(ctx context.Context, in *Book, opts ...grpc.CallOption) (*Empty, error) 114 | Get(ctx context.Context, in *BookIdRequest, opts ...grpc.CallOption) (*Book, error) 115 | Delete(ctx context.Context, in *BookIdRequest, opts ...grpc.CallOption) (*Empty, error) 116 | Watch(ctx context.Context, in *Empty, opts ...grpc.CallOption) (BookService_WatchClient, error) 117 | } 118 | 119 | type bookServiceClient struct { 120 | cc *grpc.ClientConn 121 | } 122 | 123 | func NewBookServiceClient(cc *grpc.ClientConn) BookServiceClient { 124 | return &bookServiceClient{cc} 125 | } 126 | 127 | func (c *bookServiceClient) List(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*BookList, error) { 128 | out := new(BookList) 129 | err := grpc.Invoke(ctx, "/books.BookService/List", in, out, c.cc, opts...) 130 | if err != nil { 131 | return nil, err 132 | } 133 | return out, nil 134 | } 135 | 136 | func (c *bookServiceClient) Insert(ctx context.Context, in *Book, opts ...grpc.CallOption) (*Empty, error) { 137 | out := new(Empty) 138 | err := grpc.Invoke(ctx, "/books.BookService/Insert", in, out, c.cc, opts...) 139 | if err != nil { 140 | return nil, err 141 | } 142 | return out, nil 143 | } 144 | 145 | func (c *bookServiceClient) Get(ctx context.Context, in *BookIdRequest, opts ...grpc.CallOption) (*Book, error) { 146 | out := new(Book) 147 | err := grpc.Invoke(ctx, "/books.BookService/Get", in, out, c.cc, opts...) 148 | if err != nil { 149 | return nil, err 150 | } 151 | return out, nil 152 | } 153 | 154 | func (c *bookServiceClient) Delete(ctx context.Context, in *BookIdRequest, opts ...grpc.CallOption) (*Empty, error) { 155 | out := new(Empty) 156 | err := grpc.Invoke(ctx, "/books.BookService/Delete", in, out, c.cc, opts...) 157 | if err != nil { 158 | return nil, err 159 | } 160 | return out, nil 161 | } 162 | 163 | func (c *bookServiceClient) Watch(ctx context.Context, in *Empty, opts ...grpc.CallOption) (BookService_WatchClient, error) { 164 | stream, err := grpc.NewClientStream(ctx, &_BookService_serviceDesc.Streams[0], c.cc, "/books.BookService/Watch", opts...) 165 | if err != nil { 166 | return nil, err 167 | } 168 | x := &bookServiceWatchClient{stream} 169 | if err := x.ClientStream.SendMsg(in); err != nil { 170 | return nil, err 171 | } 172 | if err := x.ClientStream.CloseSend(); err != nil { 173 | return nil, err 174 | } 175 | return x, nil 176 | } 177 | 178 | type BookService_WatchClient interface { 179 | Recv() (*Book, error) 180 | grpc.ClientStream 181 | } 182 | 183 | type bookServiceWatchClient struct { 184 | grpc.ClientStream 185 | } 186 | 187 | func (x *bookServiceWatchClient) Recv() (*Book, error) { 188 | m := new(Book) 189 | if err := x.ClientStream.RecvMsg(m); err != nil { 190 | return nil, err 191 | } 192 | return m, nil 193 | } 194 | 195 | // Server API for BookService service 196 | 197 | type BookServiceServer interface { 198 | List(context.Context, *Empty) (*BookList, error) 199 | Insert(context.Context, *Book) (*Empty, error) 200 | Get(context.Context, *BookIdRequest) (*Book, error) 201 | Delete(context.Context, *BookIdRequest) (*Empty, error) 202 | Watch(*Empty, BookService_WatchServer) error 203 | } 204 | 205 | func RegisterBookServiceServer(s *grpc.Server, srv BookServiceServer) { 206 | s.RegisterService(&_BookService_serviceDesc, srv) 207 | } 208 | 209 | func _BookService_List_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 210 | in := new(Empty) 211 | if err := dec(in); err != nil { 212 | return nil, err 213 | } 214 | if interceptor == nil { 215 | return srv.(BookServiceServer).List(ctx, in) 216 | } 217 | info := &grpc.UnaryServerInfo{ 218 | Server: srv, 219 | FullMethod: "/books.BookService/List", 220 | } 221 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 222 | return srv.(BookServiceServer).List(ctx, req.(*Empty)) 223 | } 224 | return interceptor(ctx, in, info, handler) 225 | } 226 | 227 | func _BookService_Insert_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 228 | in := new(Book) 229 | if err := dec(in); err != nil { 230 | return nil, err 231 | } 232 | if interceptor == nil { 233 | return srv.(BookServiceServer).Insert(ctx, in) 234 | } 235 | info := &grpc.UnaryServerInfo{ 236 | Server: srv, 237 | FullMethod: "/books.BookService/Insert", 238 | } 239 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 240 | return srv.(BookServiceServer).Insert(ctx, req.(*Book)) 241 | } 242 | return interceptor(ctx, in, info, handler) 243 | } 244 | 245 | func _BookService_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 246 | in := new(BookIdRequest) 247 | if err := dec(in); err != nil { 248 | return nil, err 249 | } 250 | if interceptor == nil { 251 | return srv.(BookServiceServer).Get(ctx, in) 252 | } 253 | info := &grpc.UnaryServerInfo{ 254 | Server: srv, 255 | FullMethod: "/books.BookService/Get", 256 | } 257 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 258 | return srv.(BookServiceServer).Get(ctx, req.(*BookIdRequest)) 259 | } 260 | return interceptor(ctx, in, info, handler) 261 | } 262 | 263 | func _BookService_Delete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 264 | in := new(BookIdRequest) 265 | if err := dec(in); err != nil { 266 | return nil, err 267 | } 268 | if interceptor == nil { 269 | return srv.(BookServiceServer).Delete(ctx, in) 270 | } 271 | info := &grpc.UnaryServerInfo{ 272 | Server: srv, 273 | FullMethod: "/books.BookService/Delete", 274 | } 275 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 276 | return srv.(BookServiceServer).Delete(ctx, req.(*BookIdRequest)) 277 | } 278 | return interceptor(ctx, in, info, handler) 279 | } 280 | 281 | func _BookService_Watch_Handler(srv interface{}, stream grpc.ServerStream) error { 282 | m := new(Empty) 283 | if err := stream.RecvMsg(m); err != nil { 284 | return err 285 | } 286 | return srv.(BookServiceServer).Watch(m, &bookServiceWatchServer{stream}) 287 | } 288 | 289 | type BookService_WatchServer interface { 290 | Send(*Book) error 291 | grpc.ServerStream 292 | } 293 | 294 | type bookServiceWatchServer struct { 295 | grpc.ServerStream 296 | } 297 | 298 | func (x *bookServiceWatchServer) Send(m *Book) error { 299 | return x.ServerStream.SendMsg(m) 300 | } 301 | 302 | var _BookService_serviceDesc = grpc.ServiceDesc{ 303 | ServiceName: "books.BookService", 304 | HandlerType: (*BookServiceServer)(nil), 305 | Methods: []grpc.MethodDesc{ 306 | { 307 | MethodName: "List", 308 | Handler: _BookService_List_Handler, 309 | }, 310 | { 311 | MethodName: "Insert", 312 | Handler: _BookService_Insert_Handler, 313 | }, 314 | { 315 | MethodName: "Get", 316 | Handler: _BookService_Get_Handler, 317 | }, 318 | { 319 | MethodName: "Delete", 320 | Handler: _BookService_Delete_Handler, 321 | }, 322 | }, 323 | Streams: []grpc.StreamDesc{ 324 | { 325 | StreamName: "Watch", 326 | Handler: _BookService_Watch_Handler, 327 | ServerStreams: true, 328 | }, 329 | }, 330 | Metadata: fileDescriptor0, 331 | } 332 | 333 | func init() { proto.RegisterFile("books.proto", fileDescriptor0) } 334 | 335 | var fileDescriptor0 = []byte{ 336 | // 243 bytes of a gzipped FileDescriptorProto 337 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x74, 0x90, 0x4f, 0x4b, 0xc3, 0x40, 338 | 0x14, 0xc4, 0x9b, 0xa6, 0x1b, 0x75, 0x62, 0x15, 0x1e, 0x1e, 0x42, 0xbc, 0xc8, 0x82, 0xb5, 0x78, 339 | 0x08, 0x5a, 0xbf, 0x81, 0x28, 0x52, 0xf0, 0xa4, 0x07, 0xcf, 0xfd, 0xf3, 0xa0, 0xc1, 0xea, 0xd6, 340 | 0xcd, 0xab, 0xe0, 0xc7, 0xf5, 0x9b, 0xb8, 0xbb, 0x15, 0xd9, 0x04, 0x3c, 0x85, 0x97, 0x99, 0xf9, 341 | 0xcd, 0xb0, 0xc8, 0xe7, 0xc6, 0xbc, 0x36, 0xd5, 0xc6, 0x1a, 0x31, 0xa4, 0xc2, 0xa1, 0xf7, 0xa0, 342 | 0xee, 0xdf, 0x36, 0xf2, 0xa5, 0xaf, 0x31, 0xb8, 0x75, 0x7f, 0x08, 0xe8, 0xd7, 0xcb, 0x22, 0x39, 343 | 0x4b, 0xc6, 0x8a, 0x86, 0x50, 0x52, 0xcb, 0x9a, 0x8b, 0xbe, 0x3b, 0x0f, 0xe8, 0x08, 0xd9, 0x6c, 344 | 0x2b, 0x2b, 0x63, 0x8b, 0xd4, 0xdf, 0x7a, 0x84, 0x7d, 0x1f, 0x79, 0xac, 0x1b, 0xa1, 0x12, 0x3b, 345 | 0xa0, 0x4b, 0xa6, 0xe3, 0x7c, 0x92, 0x57, 0xbb, 0x2e, 0xaf, 0xeb, 0x53, 0x0c, 0xfd, 0x77, 0xba, 346 | 0x7c, 0xe2, 0x8f, 0x2d, 0x3b, 0x73, 0xd4, 0x31, 0xf9, 0x4e, 0x90, 0x7b, 0xf5, 0x99, 0xed, 0x67, 347 | 0xbd, 0x60, 0xba, 0xc0, 0x20, 0x00, 0x0f, 0x7f, 0x09, 0x61, 0x5d, 0x79, 0x1c, 0xf1, 0xbc, 0xac, 348 | 0x7b, 0x74, 0x8e, 0x6c, 0xfa, 0xde, 0xb0, 0x15, 0x8a, 0xcb, 0xca, 0x56, 0xce, 0xd9, 0x2e, 0x91, 349 | 0x3e, 0xb0, 0xd0, 0x49, 0xe4, 0xf9, 0x1b, 0x52, 0xb6, 0x66, 0xf6, 0xa8, 0x42, 0x76, 0xc7, 0x6b, 350 | 0x16, 0xfe, 0xc7, 0xde, 0x65, 0x8f, 0xa0, 0x5e, 0x66, 0xb2, 0x58, 0x75, 0xc6, 0xb6, 0xa9, 0x57, 351 | 0xc9, 0x3c, 0x0b, 0x4f, 0x7e, 0xf3, 0x13, 0x00, 0x00, 0xff, 0xff, 0xf5, 0x70, 0xef, 0x1d, 0x81, 352 | 0x01, 0x00, 0x00, 353 | } 354 | -------------------------------------------------------------------------------- /complete/client.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016, Google, Inc. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // Unless required by applicable law or agreed to in writing, software 7 | // distributed under the License is distributed on an "AS IS" BASIS, 8 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | // See the License for the specific language governing permissions and 10 | // limitations under the License. 11 | 12 | package main 13 | 14 | import ( 15 | "encoding/json" 16 | "flag" 17 | "fmt" 18 | "io" 19 | "log" 20 | "strconv" 21 | "time" 22 | 23 | pb "./books" 24 | 25 | "golang.org/x/net/context" 26 | "google.golang.org/grpc" 27 | ) 28 | 29 | var ( 30 | address = flag.String("address", "127.0.0.1:50051", "Address of service") 31 | ) 32 | 33 | // GetClient attempts to dial the specified address flag and returns a service 34 | // client and its underlying connection. If it is unable to make a connection, 35 | // it dies. 36 | func GetClient() (*grpc.ClientConn, pb.BookServiceClient) { 37 | conn, err := grpc.Dial(*address, grpc.WithTimeout(5*time.Second), grpc.WithInsecure()) 38 | if err != nil { 39 | log.Fatalf("did not connect: %v", err) 40 | } 41 | return conn, pb.NewBookServiceClient(conn) 42 | } 43 | 44 | func main() { 45 | flag.Parse() 46 | ctx := context.Background() 47 | cmd, ok := commands[flag.Arg(0)] 48 | if !ok { 49 | usage() 50 | } else { 51 | cmd.do(ctx, flag.Args()[1:]...) 52 | } 53 | } 54 | 55 | func usage() { 56 | fmt.Println(`client.go is a command-line client for this codelab's gRPC service 57 | 58 | Usage: 59 | client.go list List all books 60 | client.go insert <author> Insert a book 61 | client.go get <id> Get a book by its ID 62 | client.go delete <id> Delete a book by its ID 63 | client.go watch Watch for inserted books`) 64 | } 65 | 66 | var commands = map[string]struct { 67 | name, desc string 68 | do func(context.Context, ...string) 69 | usage string 70 | }{ 71 | "get": { 72 | name: "get", 73 | desc: "Retrieves the indicated book", 74 | do: doGet, 75 | usage: "client.go get <id>", 76 | }, 77 | "list": { 78 | name: "list", 79 | desc: "Lists all books", 80 | do: doList, 81 | usage: "client.go list", 82 | }, 83 | "insert": { 84 | name: "insert", 85 | desc: "Inserts the provided book", 86 | do: doInsert, 87 | usage: "client.go insert <id> <title> <author>", 88 | }, 89 | "delete": { 90 | name: "delete", 91 | desc: "Deletes the indicated book", 92 | do: doDelete, 93 | usage: "client.go delete <id>", 94 | }, 95 | "watch": { 96 | name: "watch", 97 | desc: "Watches for inserted books", 98 | do: doWatch, 99 | usage: "client.go watch", 100 | }, 101 | } 102 | 103 | // printRespAsJson attempts to marshal the provided interface into its JSON 104 | // representation, then prints to stdout. 105 | func printRespAsJson(r interface{}) { 106 | b, err := json.MarshalIndent(r, "", " ") 107 | if err != nil { 108 | log.Fatalf("printResp (%v): %v", r, err) 109 | } 110 | fmt.Println(string(b)) 111 | } 112 | 113 | // doGet is a basic wrapper around the corresponding book service's RPC. 114 | // It parses the provided arguments, calls the service, and prints the 115 | // response. If any errors are encountered, it dies. 116 | func doGet(ctx context.Context, args ...string) { 117 | if len(args) != 1 { 118 | log.Fatalf("usage: client.go get <id>") 119 | } 120 | id, err := strconv.ParseInt(args[0], 10, 64) 121 | if err != nil { 122 | log.Fatalf("Provided ID %v invalid: %v", args[0], err) 123 | } 124 | conn, client := GetClient() 125 | defer conn.Close() 126 | r, err := client.Get(ctx, &pb.BookIdRequest{int32(id)}) 127 | if err != nil { 128 | log.Fatalf("Get book (%v): %v", id, err) 129 | } 130 | fmt.Println("Server response:") 131 | printRespAsJson(r) 132 | } 133 | 134 | // doDelete is a basic wrapper around the corresponding book service's RPC. 135 | // It parses the provided arguments, calls the service, and prints the 136 | // response. If any errors are encountered, it dies. 137 | func doDelete(ctx context.Context, args ...string) { 138 | if len(args) != 1 { 139 | log.Fatalf("usage: client.go delete <id>") 140 | } 141 | id, err := strconv.ParseInt(args[0], 10, 64) 142 | if err != nil { 143 | log.Fatalf("Provided ID %v invalid: %v", args[0], err) 144 | } 145 | conn, client := GetClient() 146 | defer conn.Close() 147 | r, err := client.Delete(ctx, &pb.BookIdRequest{int32(id)}) 148 | if err != nil { 149 | log.Fatalf("Get book (%v): %v", id, err) 150 | } 151 | fmt.Println("Server response:") 152 | printRespAsJson(r) 153 | } 154 | 155 | // doList is a basic wrapper around the corresponding book service's RPC. 156 | // It parses the provided arguments, calls the service, and prints the 157 | // response. If any errors are encountered, it dies. 158 | func doList(ctx context.Context, args ...string) { 159 | conn, client := GetClient() 160 | defer conn.Close() 161 | rs, err := client.List(ctx, &pb.Empty{}) 162 | if err != nil { 163 | log.Fatalf("List books: %v", err) 164 | } 165 | fmt.Printf("Server sent %v book(s).\n", len(rs.GetBooks())) 166 | printRespAsJson(rs) 167 | } 168 | 169 | // doInsert is a basic wrapper around the corresponding book service's RPC. 170 | // It parses the provided arguments, calls the service, and prints the 171 | // response. If any errors are encountered, it dies. 172 | func doInsert(ctx context.Context, args ...string) { 173 | if len(args) != 3 { 174 | log.Fatalf("usage client.go insert <id> <title> <author>") 175 | } 176 | id, err := strconv.ParseInt(args[0], 10, 64) 177 | if err != nil { 178 | log.Fatalf("Provided ID %v invalid: %v", args[0], err) 179 | } 180 | book := &pb.Book{ 181 | Id: int32(id), 182 | Title: args[1], 183 | Author: args[2], 184 | } 185 | conn, client := GetClient() 186 | defer conn.Close() 187 | r, err := client.Insert(ctx, book) 188 | if err != nil { 189 | log.Fatalf("Insert book (%v): %v", book, err) 190 | } 191 | fmt.Println("Server response:") 192 | printRespAsJson(r) 193 | } 194 | 195 | // doWatch is a basic wrapper around the corresponding book service's RPC. 196 | // It parses the provided arguments, calls the service, and prints the 197 | // response. If any errors are encountered, it dies. 198 | func doWatch(ctx context.Context, args ...string) { 199 | conn, client := GetClient() 200 | defer conn.Close() 201 | stream, err := client.Watch(ctx, &pb.Empty{}) 202 | if err != nil { 203 | log.Fatalf("Watch books: %v", err) 204 | } 205 | for { 206 | book, err := stream.Recv() 207 | if err == io.EOF { 208 | break 209 | } 210 | if err != nil { 211 | log.Fatalf("Watch books stream: %v", err) 212 | } 213 | fmt.Println("Server stream data received:") 214 | printRespAsJson(book) 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /complete/client.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016, Google, Inc. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | var grpc = require('grpc'); 15 | 16 | var booksProto = grpc.load('books.proto'); 17 | 18 | var client = new booksProto.books.BookService('127.0.0.1:50051', grpc.Credentials.createInsecure()); 19 | 20 | function printResponse(error, response) { 21 | if (error) 22 | console.log('Error: ', error); 23 | else 24 | console.log(response); 25 | } 26 | 27 | function listBooks() { 28 | client.list({}, function(error, books) { 29 | printResponse(error, books); 30 | }); 31 | } 32 | 33 | function insertBook(id, title, author) { 34 | var book = { 35 | id: parseInt(id), 36 | title: title, 37 | author: author 38 | }; 39 | client.insert(book, function(error, empty) { 40 | printResponse(error, empty); 41 | }); 42 | } 43 | 44 | function getBook(id) { 45 | client.get({ 46 | id: parseInt(id) 47 | }, function(error, book) { 48 | printResponse(error, book); 49 | }); 50 | } 51 | 52 | function deleteBook(id) { 53 | client.delete({ 54 | id: parseInt(id) 55 | }, function(error, empty) { 56 | printResponse(error, empty); 57 | }); 58 | } 59 | 60 | function watchBooks() { 61 | var call = client.watch({}); 62 | call.on('data', function(book) { 63 | console.log(book); 64 | }); 65 | } 66 | 67 | var processName = process.argv.shift(); 68 | var scriptName = process.argv.shift(); 69 | var command = process.argv.shift(); 70 | 71 | if (command == 'list') 72 | listBooks(); 73 | else if (command == 'insert') 74 | insertBook(process.argv[0], process.argv[1], process.argv[2]); 75 | else if (command == 'get') 76 | getBook(process.argv[0]); 77 | else if (command == 'delete') 78 | deleteBook(process.argv[0]); 79 | else if (command == 'watch') 80 | watchBooks(); -------------------------------------------------------------------------------- /complete/server.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016, Google, Inc. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | var grpc = require('grpc'); 15 | 16 | var booksProto = grpc.load('books.proto'); 17 | 18 | var events = require('events'); 19 | var bookStream = new events.EventEmitter(); 20 | 21 | // In-memory array of book objects 22 | var books = [{ 23 | id: 123, 24 | title: 'A Tale of Two Cities', 25 | author: 'Charles Dickens' 26 | }]; 27 | 28 | var server = new grpc.Server(); 29 | server.addService(booksProto.books.BookService.service, { 30 | list: function(call, callback) { 31 | callback(null, books); 32 | }, 33 | insert: function(call, callback) { 34 | var book = call.request; 35 | books.push(book); 36 | bookStream.emit('new_book', book); 37 | callback(null, {}); 38 | }, 39 | get: function(call, callback) { 40 | for (var i = 0; i < books.length; i++) 41 | if (books[i].id == call.request.id) 42 | return callback(null, books[i]); 43 | callback({ 44 | code: grpc.status.NOT_FOUND, 45 | details: 'Not found' 46 | }); 47 | }, 48 | delete: function(call, callback) { 49 | for (var i = 0; i < books.length; i++) { 50 | if (books[i].id == call.request.id) { 51 | books.splice(i, 1); 52 | return callback(null, {}); 53 | } 54 | } 55 | callback({ 56 | code: grpc.status.NOT_FOUND, 57 | details: 'Not found' 58 | }); 59 | }, 60 | watch: function(stream) { 61 | bookStream.on('new_book', function(book){ 62 | stream.write(book); 63 | }); 64 | } 65 | }); 66 | 67 | server.bind('0.0.0.0:50051', grpc.ServerCredentials.createInsecure()); 68 | server.start(); 69 | -------------------------------------------------------------------------------- /start/books/books.pb.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016, Google, Inc. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // Unless required by applicable law or agreed to in writing, software 7 | // distributed under the License is distributed on an "AS IS" BASIS, 8 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | // See the License for the specific language governing permissions and 10 | // limitations under the License. 11 | 12 | // Code generated by protoc-gen-go. 13 | // source: books.proto 14 | // DO NOT EDIT! 15 | 16 | /* 17 | Package books is a generated protocol buffer package. 18 | 19 | It is generated from these files: 20 | books.proto 21 | 22 | It has these top-level messages: 23 | Empty 24 | Book 25 | BookList 26 | BookIdRequest 27 | */ 28 | package books 29 | 30 | import proto "github.com/golang/protobuf/proto" 31 | import fmt "fmt" 32 | import math "math" 33 | 34 | import ( 35 | context "golang.org/x/net/context" 36 | grpc "google.golang.org/grpc" 37 | ) 38 | 39 | // Reference imports to suppress errors if they are not otherwise used. 40 | var _ = proto.Marshal 41 | var _ = fmt.Errorf 42 | var _ = math.Inf 43 | 44 | // This is a compile-time assertion to ensure that this generated file 45 | // is compatible with the proto package it is being compiled against. 46 | // A compilation error at this line likely means your copy of the 47 | // proto package needs to be updated. 48 | const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package 49 | 50 | type Empty struct { 51 | } 52 | 53 | func (m *Empty) Reset() { *m = Empty{} } 54 | func (m *Empty) String() string { return proto.CompactTextString(m) } 55 | func (*Empty) ProtoMessage() {} 56 | func (*Empty) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } 57 | 58 | type Book struct { 59 | Id int32 `protobuf:"varint,1,opt,name=id" json:"id,omitempty"` 60 | Title string `protobuf:"bytes,2,opt,name=title" json:"title,omitempty"` 61 | Author string `protobuf:"bytes,3,opt,name=author" json:"author,omitempty"` 62 | } 63 | 64 | func (m *Book) Reset() { *m = Book{} } 65 | func (m *Book) String() string { return proto.CompactTextString(m) } 66 | func (*Book) ProtoMessage() {} 67 | func (*Book) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } 68 | 69 | type BookList struct { 70 | Books []*Book `protobuf:"bytes,1,rep,name=books" json:"books,omitempty"` 71 | } 72 | 73 | func (m *BookList) Reset() { *m = BookList{} } 74 | func (m *BookList) String() string { return proto.CompactTextString(m) } 75 | func (*BookList) ProtoMessage() {} 76 | func (*BookList) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } 77 | 78 | func (m *BookList) GetBooks() []*Book { 79 | if m != nil { 80 | return m.Books 81 | } 82 | return nil 83 | } 84 | 85 | type BookIdRequest struct { 86 | Id int32 `protobuf:"varint,1,opt,name=id" json:"id,omitempty"` 87 | } 88 | 89 | func (m *BookIdRequest) Reset() { *m = BookIdRequest{} } 90 | func (m *BookIdRequest) String() string { return proto.CompactTextString(m) } 91 | func (*BookIdRequest) ProtoMessage() {} 92 | func (*BookIdRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } 93 | 94 | func init() { 95 | proto.RegisterType((*Empty)(nil), "books.Empty") 96 | proto.RegisterType((*Book)(nil), "books.Book") 97 | proto.RegisterType((*BookList)(nil), "books.BookList") 98 | proto.RegisterType((*BookIdRequest)(nil), "books.BookIdRequest") 99 | } 100 | 101 | // Reference imports to suppress errors if they are not otherwise used. 102 | var _ context.Context 103 | var _ grpc.ClientConn 104 | 105 | // This is a compile-time assertion to ensure that this generated file 106 | // is compatible with the grpc package it is being compiled against. 107 | const _ = grpc.SupportPackageIsVersion4 108 | 109 | // Client API for BookService service 110 | 111 | type BookServiceClient interface { 112 | List(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*BookList, error) 113 | Insert(ctx context.Context, in *Book, opts ...grpc.CallOption) (*Empty, error) 114 | Get(ctx context.Context, in *BookIdRequest, opts ...grpc.CallOption) (*Book, error) 115 | Delete(ctx context.Context, in *BookIdRequest, opts ...grpc.CallOption) (*Empty, error) 116 | Watch(ctx context.Context, in *Empty, opts ...grpc.CallOption) (BookService_WatchClient, error) 117 | } 118 | 119 | type bookServiceClient struct { 120 | cc *grpc.ClientConn 121 | } 122 | 123 | func NewBookServiceClient(cc *grpc.ClientConn) BookServiceClient { 124 | return &bookServiceClient{cc} 125 | } 126 | 127 | func (c *bookServiceClient) List(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*BookList, error) { 128 | out := new(BookList) 129 | err := grpc.Invoke(ctx, "/books.BookService/List", in, out, c.cc, opts...) 130 | if err != nil { 131 | return nil, err 132 | } 133 | return out, nil 134 | } 135 | 136 | func (c *bookServiceClient) Insert(ctx context.Context, in *Book, opts ...grpc.CallOption) (*Empty, error) { 137 | out := new(Empty) 138 | err := grpc.Invoke(ctx, "/books.BookService/Insert", in, out, c.cc, opts...) 139 | if err != nil { 140 | return nil, err 141 | } 142 | return out, nil 143 | } 144 | 145 | func (c *bookServiceClient) Get(ctx context.Context, in *BookIdRequest, opts ...grpc.CallOption) (*Book, error) { 146 | out := new(Book) 147 | err := grpc.Invoke(ctx, "/books.BookService/Get", in, out, c.cc, opts...) 148 | if err != nil { 149 | return nil, err 150 | } 151 | return out, nil 152 | } 153 | 154 | func (c *bookServiceClient) Delete(ctx context.Context, in *BookIdRequest, opts ...grpc.CallOption) (*Empty, error) { 155 | out := new(Empty) 156 | err := grpc.Invoke(ctx, "/books.BookService/Delete", in, out, c.cc, opts...) 157 | if err != nil { 158 | return nil, err 159 | } 160 | return out, nil 161 | } 162 | 163 | func (c *bookServiceClient) Watch(ctx context.Context, in *Empty, opts ...grpc.CallOption) (BookService_WatchClient, error) { 164 | stream, err := grpc.NewClientStream(ctx, &_BookService_serviceDesc.Streams[0], c.cc, "/books.BookService/Watch", opts...) 165 | if err != nil { 166 | return nil, err 167 | } 168 | x := &bookServiceWatchClient{stream} 169 | if err := x.ClientStream.SendMsg(in); err != nil { 170 | return nil, err 171 | } 172 | if err := x.ClientStream.CloseSend(); err != nil { 173 | return nil, err 174 | } 175 | return x, nil 176 | } 177 | 178 | type BookService_WatchClient interface { 179 | Recv() (*Book, error) 180 | grpc.ClientStream 181 | } 182 | 183 | type bookServiceWatchClient struct { 184 | grpc.ClientStream 185 | } 186 | 187 | func (x *bookServiceWatchClient) Recv() (*Book, error) { 188 | m := new(Book) 189 | if err := x.ClientStream.RecvMsg(m); err != nil { 190 | return nil, err 191 | } 192 | return m, nil 193 | } 194 | 195 | // Server API for BookService service 196 | 197 | type BookServiceServer interface { 198 | List(context.Context, *Empty) (*BookList, error) 199 | Insert(context.Context, *Book) (*Empty, error) 200 | Get(context.Context, *BookIdRequest) (*Book, error) 201 | Delete(context.Context, *BookIdRequest) (*Empty, error) 202 | Watch(*Empty, BookService_WatchServer) error 203 | } 204 | 205 | func RegisterBookServiceServer(s *grpc.Server, srv BookServiceServer) { 206 | s.RegisterService(&_BookService_serviceDesc, srv) 207 | } 208 | 209 | func _BookService_List_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 210 | in := new(Empty) 211 | if err := dec(in); err != nil { 212 | return nil, err 213 | } 214 | if interceptor == nil { 215 | return srv.(BookServiceServer).List(ctx, in) 216 | } 217 | info := &grpc.UnaryServerInfo{ 218 | Server: srv, 219 | FullMethod: "/books.BookService/List", 220 | } 221 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 222 | return srv.(BookServiceServer).List(ctx, req.(*Empty)) 223 | } 224 | return interceptor(ctx, in, info, handler) 225 | } 226 | 227 | func _BookService_Insert_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 228 | in := new(Book) 229 | if err := dec(in); err != nil { 230 | return nil, err 231 | } 232 | if interceptor == nil { 233 | return srv.(BookServiceServer).Insert(ctx, in) 234 | } 235 | info := &grpc.UnaryServerInfo{ 236 | Server: srv, 237 | FullMethod: "/books.BookService/Insert", 238 | } 239 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 240 | return srv.(BookServiceServer).Insert(ctx, req.(*Book)) 241 | } 242 | return interceptor(ctx, in, info, handler) 243 | } 244 | 245 | func _BookService_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 246 | in := new(BookIdRequest) 247 | if err := dec(in); err != nil { 248 | return nil, err 249 | } 250 | if interceptor == nil { 251 | return srv.(BookServiceServer).Get(ctx, in) 252 | } 253 | info := &grpc.UnaryServerInfo{ 254 | Server: srv, 255 | FullMethod: "/books.BookService/Get", 256 | } 257 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 258 | return srv.(BookServiceServer).Get(ctx, req.(*BookIdRequest)) 259 | } 260 | return interceptor(ctx, in, info, handler) 261 | } 262 | 263 | func _BookService_Delete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 264 | in := new(BookIdRequest) 265 | if err := dec(in); err != nil { 266 | return nil, err 267 | } 268 | if interceptor == nil { 269 | return srv.(BookServiceServer).Delete(ctx, in) 270 | } 271 | info := &grpc.UnaryServerInfo{ 272 | Server: srv, 273 | FullMethod: "/books.BookService/Delete", 274 | } 275 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 276 | return srv.(BookServiceServer).Delete(ctx, req.(*BookIdRequest)) 277 | } 278 | return interceptor(ctx, in, info, handler) 279 | } 280 | 281 | func _BookService_Watch_Handler(srv interface{}, stream grpc.ServerStream) error { 282 | m := new(Empty) 283 | if err := stream.RecvMsg(m); err != nil { 284 | return err 285 | } 286 | return srv.(BookServiceServer).Watch(m, &bookServiceWatchServer{stream}) 287 | } 288 | 289 | type BookService_WatchServer interface { 290 | Send(*Book) error 291 | grpc.ServerStream 292 | } 293 | 294 | type bookServiceWatchServer struct { 295 | grpc.ServerStream 296 | } 297 | 298 | func (x *bookServiceWatchServer) Send(m *Book) error { 299 | return x.ServerStream.SendMsg(m) 300 | } 301 | 302 | var _BookService_serviceDesc = grpc.ServiceDesc{ 303 | ServiceName: "books.BookService", 304 | HandlerType: (*BookServiceServer)(nil), 305 | Methods: []grpc.MethodDesc{ 306 | { 307 | MethodName: "List", 308 | Handler: _BookService_List_Handler, 309 | }, 310 | { 311 | MethodName: "Insert", 312 | Handler: _BookService_Insert_Handler, 313 | }, 314 | { 315 | MethodName: "Get", 316 | Handler: _BookService_Get_Handler, 317 | }, 318 | { 319 | MethodName: "Delete", 320 | Handler: _BookService_Delete_Handler, 321 | }, 322 | }, 323 | Streams: []grpc.StreamDesc{ 324 | { 325 | StreamName: "Watch", 326 | Handler: _BookService_Watch_Handler, 327 | ServerStreams: true, 328 | }, 329 | }, 330 | Metadata: fileDescriptor0, 331 | } 332 | 333 | func init() { proto.RegisterFile("books.proto", fileDescriptor0) } 334 | 335 | var fileDescriptor0 = []byte{ 336 | // 243 bytes of a gzipped FileDescriptorProto 337 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x74, 0x90, 0x4f, 0x4b, 0xc3, 0x40, 338 | 0x14, 0xc4, 0x9b, 0xa6, 0x1b, 0x75, 0x62, 0x15, 0x1e, 0x1e, 0x42, 0xbc, 0xc8, 0x82, 0xb5, 0x78, 339 | 0x08, 0x5a, 0xbf, 0x81, 0x28, 0x52, 0xf0, 0xa4, 0x07, 0xcf, 0xfd, 0xf3, 0xa0, 0xc1, 0xea, 0xd6, 340 | 0xcd, 0xab, 0xe0, 0xc7, 0xf5, 0x9b, 0xb8, 0xbb, 0x15, 0xd9, 0x04, 0x3c, 0x85, 0x97, 0x99, 0xf9, 341 | 0xcd, 0xb0, 0xc8, 0xe7, 0xc6, 0xbc, 0x36, 0xd5, 0xc6, 0x1a, 0x31, 0xa4, 0xc2, 0xa1, 0xf7, 0xa0, 342 | 0xee, 0xdf, 0x36, 0xf2, 0xa5, 0xaf, 0x31, 0xb8, 0x75, 0x7f, 0x08, 0xe8, 0xd7, 0xcb, 0x22, 0x39, 343 | 0x4b, 0xc6, 0x8a, 0x86, 0x50, 0x52, 0xcb, 0x9a, 0x8b, 0xbe, 0x3b, 0x0f, 0xe8, 0x08, 0xd9, 0x6c, 344 | 0x2b, 0x2b, 0x63, 0x8b, 0xd4, 0xdf, 0x7a, 0x84, 0x7d, 0x1f, 0x79, 0xac, 0x1b, 0xa1, 0x12, 0x3b, 345 | 0xa0, 0x4b, 0xa6, 0xe3, 0x7c, 0x92, 0x57, 0xbb, 0x2e, 0xaf, 0xeb, 0x53, 0x0c, 0xfd, 0x77, 0xba, 346 | 0x7c, 0xe2, 0x8f, 0x2d, 0x3b, 0x73, 0xd4, 0x31, 0xf9, 0x4e, 0x90, 0x7b, 0xf5, 0x99, 0xed, 0x67, 347 | 0xbd, 0x60, 0xba, 0xc0, 0x20, 0x00, 0x0f, 0x7f, 0x09, 0x61, 0x5d, 0x79, 0x1c, 0xf1, 0xbc, 0xac, 348 | 0x7b, 0x74, 0x8e, 0x6c, 0xfa, 0xde, 0xb0, 0x15, 0x8a, 0xcb, 0xca, 0x56, 0xce, 0xd9, 0x2e, 0x91, 349 | 0x3e, 0xb0, 0xd0, 0x49, 0xe4, 0xf9, 0x1b, 0x52, 0xb6, 0x66, 0xf6, 0xa8, 0x42, 0x76, 0xc7, 0x6b, 350 | 0x16, 0xfe, 0xc7, 0xde, 0x65, 0x8f, 0xa0, 0x5e, 0x66, 0xb2, 0x58, 0x75, 0xc6, 0xb6, 0xa9, 0x57, 351 | 0xc9, 0x3c, 0x0b, 0x4f, 0x7e, 0xf3, 0x13, 0x00, 0x00, 0xff, 0xff, 0xf5, 0x70, 0xef, 0x1d, 0x81, 352 | 0x01, 0x00, 0x00, 353 | } 354 | -------------------------------------------------------------------------------- /start/client.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016, Google, Inc. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // Unless required by applicable law or agreed to in writing, software 7 | // distributed under the License is distributed on an "AS IS" BASIS, 8 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | // See the License for the specific language governing permissions and 10 | // limitations under the License. 11 | 12 | package main 13 | 14 | import ( 15 | "encoding/json" 16 | "flag" 17 | "fmt" 18 | "io" 19 | "log" 20 | "strconv" 21 | "time" 22 | 23 | pb "./books" 24 | 25 | "golang.org/x/net/context" 26 | "google.golang.org/grpc" 27 | ) 28 | 29 | var ( 30 | address = flag.String("address", "0.0.0.0:50051", "Address of service") 31 | ) 32 | 33 | // GetClient attempts to dial the specified address flag and returns a service 34 | // client and its underlying connection. If it is unable to make a connection, 35 | // it dies. 36 | func GetClient() (*grpc.ClientConn, pb.BookServiceClient) { 37 | conn, err := grpc.Dial(*address, grpc.WithTimeout(5*time.Second), grpc.WithInsecure()) 38 | if err != nil { 39 | log.Fatalf("did not connect: %v", err) 40 | } 41 | return conn, pb.NewBookServiceClient(conn) 42 | } 43 | 44 | func main() { 45 | flag.Parse() 46 | ctx := context.Background() 47 | cmd, ok := commands[flag.Arg(0)] 48 | if !ok { 49 | usage() 50 | } else { 51 | cmd.do(ctx, flag.Args()[1:]...) 52 | } 53 | } 54 | 55 | func usage() { 56 | fmt.Println(`client.go is a command-line client for this codelab's gRPC service 57 | 58 | Usage: 59 | client.go list List all books 60 | client.go insert <id> <title> <author> Insert a book 61 | client.go get <id> Get a book by its ID 62 | client.go delete <id> Delete a book by its ID 63 | client.go watch Watch for inserted books`) 64 | } 65 | 66 | var commands = map[string]struct { 67 | name, desc string 68 | do func(context.Context, ...string) 69 | usage string 70 | }{ 71 | "get": { 72 | name: "get", 73 | desc: "Retrieves the indicated book", 74 | do: doGet, 75 | usage: "client.go get <id>", 76 | }, 77 | "list": { 78 | name: "list", 79 | desc: "Lists all books", 80 | do: doList, 81 | usage: "client.go list", 82 | }, 83 | "insert": { 84 | name: "insert", 85 | desc: "Inserts the provided book", 86 | do: doInsert, 87 | usage: "client.go insert <id> <title> <author>", 88 | }, 89 | "delete": { 90 | name: "delete", 91 | desc: "Deletes the indicated book", 92 | do: doDelete, 93 | usage: "client.go delete <id>", 94 | }, 95 | "watch": { 96 | name: "watch", 97 | desc: "Watches for inserted books", 98 | do: doWatch, 99 | usage: "client.go watch", 100 | }, 101 | } 102 | 103 | // printRespAsJson attempts to marshal the provided interface into its JSON 104 | // representation, then prints to stdout. 105 | func printRespAsJson(r interface{}) { 106 | b, err := json.MarshalIndent(r, "", " ") 107 | if err != nil { 108 | log.Fatalf("printResp (%v): %v", r, err) 109 | } 110 | fmt.Println(string(b)) 111 | } 112 | 113 | // doGet is a basic wrapper around the corresponding book service's RPC. 114 | // It parses the provided arguments, calls the service, and prints the 115 | // response. If any errors are encountered, it dies. 116 | func doGet(ctx context.Context, args ...string) { 117 | if len(args) != 1 { 118 | log.Fatalf("usage: client.go get <id>") 119 | } 120 | id, err := strconv.ParseInt(args[0], 10, 64) 121 | if err != nil { 122 | log.Fatalf("Provided ID %v invalid: %v", args[0], err) 123 | } 124 | conn, client := GetClient() 125 | defer conn.Close() 126 | r, err := client.Get(ctx, &pb.BookIdRequest{int32(id)}) 127 | if err != nil { 128 | log.Fatalf("Get book (%v): %v", id, err) 129 | } 130 | fmt.Println("Server response:") 131 | printRespAsJson(r) 132 | } 133 | 134 | // doDelete is a basic wrapper around the corresponding book service's RPC. 135 | // It parses the provided arguments, calls the service, and prints the 136 | // response. If any errors are encountered, it dies. 137 | func doDelete(ctx context.Context, args ...string) { 138 | if len(args) != 1 { 139 | log.Fatalf("usage: client.go delete <id>") 140 | } 141 | id, err := strconv.ParseInt(args[0], 10, 64) 142 | if err != nil { 143 | log.Fatalf("Provided ID %v invalid: %v", args[0], err) 144 | } 145 | conn, client := GetClient() 146 | defer conn.Close() 147 | r, err := client.Delete(ctx, &pb.BookIdRequest{int32(id)}) 148 | if err != nil { 149 | log.Fatalf("Delete book (%v): %v", id, err) 150 | } 151 | fmt.Println("Server response:") 152 | printRespAsJson(r) 153 | } 154 | 155 | // doList is a basic wrapper around the corresponding book service's RPC. 156 | // It parses the provided arguments, calls the service, and prints the 157 | // response. If any errors are encountered, it dies. 158 | func doList(ctx context.Context, args ...string) { 159 | conn, client := GetClient() 160 | defer conn.Close() 161 | rs, err := client.List(ctx, &pb.Empty{}) 162 | if err != nil { 163 | log.Fatalf("List books: %v", err) 164 | } 165 | fmt.Printf("Server sent %v book(s).\n", len(rs.GetBooks())) 166 | printRespAsJson(rs) 167 | } 168 | 169 | // doInsert is a basic wrapper around the corresponding book service's RPC. 170 | // It parses the provided arguments, calls the service, and prints the 171 | // response. If any errors are encountered, it dies. 172 | func doInsert(ctx context.Context, args ...string) { 173 | if len(args) != 3 { 174 | log.Fatalf("usage client.go insert <id> <title> <author>") 175 | } 176 | id, err := strconv.ParseInt(args[0], 10, 64) 177 | if err != nil { 178 | log.Fatalf("Provided ID %v invalid: %v", args[0], err) 179 | } 180 | book := &pb.Book{ 181 | Id: int32(id), 182 | Title: args[1], 183 | Author: args[2], 184 | } 185 | conn, client := GetClient() 186 | defer conn.Close() 187 | r, err := client.Insert(ctx, book) 188 | if err != nil { 189 | log.Fatalf("Insert book (%v): %v", book, err) 190 | } 191 | fmt.Println("Server response:") 192 | printRespAsJson(r) 193 | } 194 | 195 | // doWatch is a basic wrapper around the corresponding book service's RPC. 196 | // It parses the provided arguments, calls the service, and prints the 197 | // response. If any errors are encountered, it dies. 198 | func doWatch(ctx context.Context, args ...string) { 199 | conn, client := GetClient() 200 | defer conn.Close() 201 | stream, err := client.Watch(ctx, &pb.Empty{}) 202 | if err != nil { 203 | log.Fatalf("Watch books: %v", err) 204 | } 205 | for { 206 | book, err := stream.Recv() 207 | if err == io.EOF { 208 | break 209 | } 210 | if err != nil { 211 | log.Fatalf("Watch books stream: %v", err) 212 | } 213 | fmt.Println("Server stream data received:") 214 | printRespAsJson(book) 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /step-1-list-books/books.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2016, Google, Inc. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // Unless required by applicable law or agreed to in writing, software 7 | // distributed under the License is distributed on an "AS IS" BASIS, 8 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | // See the License for the specific language governing permissions and 10 | // limitations under the License. 11 | 12 | syntax = "proto3"; 13 | 14 | package books; 15 | 16 | service BookService { 17 | rpc List (Empty) returns (BookList) {} 18 | } 19 | 20 | message Empty {} 21 | 22 | message Book { 23 | int32 id = 1; 24 | string title = 2; 25 | string author = 3; 26 | } 27 | 28 | message BookList { 29 | repeated Book books = 1; 30 | } 31 | -------------------------------------------------------------------------------- /step-1-list-books/server.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016, Google, Inc. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | var grpc = require('grpc'); 15 | 16 | var booksProto = grpc.load('books.proto'); 17 | 18 | // In-memory array of book objects 19 | var books = [{ 20 | id: 123, 21 | title: 'A Tale of Two Cities', 22 | author: 'Charles Dickens' 23 | }]; 24 | 25 | var server = new grpc.Server(); 26 | server.addService(booksProto.books.BookService.service, { 27 | list: function(call, callback) { 28 | callback(null, books); 29 | } 30 | }); 31 | 32 | server.bind('0.0.0.0:50051', grpc.ServerCredentials.createInsecure()); 33 | server.start(); -------------------------------------------------------------------------------- /step-2-insert-books/books.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2016, Google, Inc. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // Unless required by applicable law or agreed to in writing, software 7 | // distributed under the License is distributed on an "AS IS" BASIS, 8 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | // See the License for the specific language governing permissions and 10 | // limitations under the License. 11 | 12 | syntax = "proto3"; 13 | 14 | package books; 15 | 16 | service BookService { 17 | rpc List (Empty) returns (BookList) {} 18 | rpc Insert (Book) returns (Empty) {} 19 | } 20 | 21 | message Empty {} 22 | 23 | message Book { 24 | int32 id = 1; 25 | string title = 2; 26 | string author = 3; 27 | } 28 | 29 | message BookList { 30 | repeated Book books = 1; 31 | } 32 | -------------------------------------------------------------------------------- /step-2-insert-books/server.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016, Google, Inc. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | var grpc = require('grpc'); 15 | 16 | var booksProto = grpc.load('books.proto'); 17 | 18 | // In-memory array of book objects 19 | var books = [{ 20 | id: 123, 21 | title: 'A Tale of Two Cities', 22 | author: 'Charles Dickens' 23 | }]; 24 | 25 | var server = new grpc.Server(); 26 | server.addService(booksProto.books.BookService.service, { 27 | list: function(call, callback) { 28 | callback(null, books); 29 | }, 30 | insert: function(call, callback) { 31 | books.push(call.request); 32 | callback(null, {}); 33 | } 34 | }); 35 | 36 | server.bind('0.0.0.0:50051', grpc.ServerCredentials.createInsecure()); 37 | server.start(); -------------------------------------------------------------------------------- /step-3-get-and-delete-books/books.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2016, Google, Inc. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // Unless required by applicable law or agreed to in writing, software 7 | // distributed under the License is distributed on an "AS IS" BASIS, 8 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | // See the License for the specific language governing permissions and 10 | // limitations under the License. 11 | 12 | syntax = "proto3"; 13 | 14 | package books; 15 | 16 | service BookService { 17 | rpc List (Empty) returns (BookList) {} 18 | rpc Insert (Book) returns (Empty) {} 19 | rpc Get (BookIdRequest) returns (Book) {} 20 | rpc Delete (BookIdRequest) returns (Empty) {} 21 | } 22 | 23 | message Empty {} 24 | 25 | message Book { 26 | int32 id = 1; 27 | string title = 2; 28 | string author = 3; 29 | } 30 | 31 | message BookList { 32 | repeated Book books = 1; 33 | } 34 | 35 | message BookIdRequest { 36 | int32 id = 1; 37 | } 38 | -------------------------------------------------------------------------------- /step-3-get-and-delete-books/server.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016, Google, Inc. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | var grpc = require('grpc'); 15 | 16 | var booksProto = grpc.load('books.proto'); 17 | 18 | // In-memory array of book objects 19 | var books = [{ 20 | id: 123, 21 | title: 'A Tale of Two Cities', 22 | author: 'Charles Dickens' 23 | }]; 24 | 25 | var server = new grpc.Server(); 26 | server.addService(booksProto.books.BookService.service, { 27 | list: function(call, callback) { 28 | callback(null, books); 29 | }, 30 | insert: function(call, callback) { 31 | books.push(call.request); 32 | callback(null, {}); 33 | }, 34 | get: function(call, callback) { 35 | for (var i = 0; i < books.length; i++) 36 | if (books[i].id == call.request.id) 37 | return callback(null, books[i]); 38 | callback({ 39 | code: grpc.status.NOT_FOUND, 40 | details: 'Not found' 41 | }); 42 | }, 43 | delete: function(call, callback) { 44 | for (var i = 0; i < books.length; i++) { 45 | if (books[i].id == call.request.id) { 46 | books.splice(i, 1); 47 | return callback(null, {}); 48 | } 49 | } 50 | callback({ 51 | code: grpc.status.NOT_FOUND, 52 | details: 'Not found' 53 | }); 54 | } 55 | }); 56 | 57 | server.bind('0.0.0.0:50051', grpc.ServerCredentials.createInsecure()); 58 | server.start(); -------------------------------------------------------------------------------- /step-4-stream-books/books.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2016, Google, Inc. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // Unless required by applicable law or agreed to in writing, software 7 | // distributed under the License is distributed on an "AS IS" BASIS, 8 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | // See the License for the specific language governing permissions and 10 | // limitations under the License. 11 | 12 | syntax = "proto3"; 13 | 14 | package books; 15 | 16 | service BookService { 17 | rpc List (Empty) returns (BookList) {} 18 | rpc Insert (Book) returns (Empty) {} 19 | rpc Get (BookIdRequest) returns (Book) {} 20 | rpc Delete (BookIdRequest) returns (Empty) {} 21 | rpc Watch (Empty) returns (stream Book) {} 22 | } 23 | 24 | message Empty {} 25 | 26 | message Book { 27 | int32 id = 1; 28 | string title = 2; 29 | string author = 3; 30 | } 31 | 32 | message BookList { 33 | repeated Book books = 1; 34 | } 35 | 36 | message BookIdRequest { 37 | int32 id = 1; 38 | } 39 | -------------------------------------------------------------------------------- /step-4-stream-books/server.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016, Google, Inc. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | var grpc = require('grpc'); 15 | 16 | var booksProto = grpc.load('books.proto'); 17 | 18 | var events = require('events'); 19 | var bookStream = new events.EventEmitter(); 20 | 21 | // In-memory array of book objects 22 | var books = [{ 23 | id: 123, 24 | title: 'A Tale of Two Cities', 25 | author: 'Charles Dickens' 26 | }]; 27 | 28 | var server = new grpc.Server(); 29 | server.addService(booksProto.books.BookService.service, { 30 | list: function(call, callback) { 31 | callback(null, books); 32 | }, 33 | insert: function(call, callback) { 34 | var book = call.request; 35 | books.push(book); 36 | bookStream.emit('new_book', book); 37 | callback(null, {}); 38 | }, 39 | get: function(call, callback) { 40 | for (var i = 0; i < books.length; i++) 41 | if (books[i].id == call.request.id) 42 | return callback(null, books[i]); 43 | callback({ 44 | code: grpc.status.NOT_FOUND, 45 | details: 'Not found' 46 | }); 47 | }, 48 | delete: function(call, callback) { 49 | for (var i = 0; i < books.length; i++) { 50 | if (books[i].id == call.request.id) { 51 | books.splice(i, 1); 52 | return callback(null, {}); 53 | } 54 | } 55 | callback({ 56 | code: grpc.status.NOT_FOUND, 57 | details: 'Not found' 58 | }); 59 | }, 60 | watch: function(stream) { 61 | bookStream.on('new_book', function(book){ 62 | stream.write(book); 63 | }); 64 | } 65 | }); 66 | 67 | server.bind('0.0.0.0:50051', grpc.ServerCredentials.createInsecure()); 68 | server.start(); 69 | -------------------------------------------------------------------------------- /step-5-node-client/books.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2016, Google, Inc. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // Unless required by applicable law or agreed to in writing, software 7 | // distributed under the License is distributed on an "AS IS" BASIS, 8 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | // See the License for the specific language governing permissions and 10 | // limitations under the License. 11 | 12 | syntax = "proto3"; 13 | 14 | package books; 15 | 16 | service BookService { 17 | rpc List (Empty) returns (BookList) {} 18 | rpc Insert (Book) returns (Empty) {} 19 | rpc Get (BookIdRequest) returns (Book) {} 20 | rpc Delete (BookIdRequest) returns (Empty) {} 21 | rpc Watch (Empty) returns (stream Book) {} 22 | } 23 | 24 | message Empty {} 25 | 26 | message Book { 27 | int32 id = 1; 28 | string title = 2; 29 | string author = 3; 30 | } 31 | 32 | message BookList { 33 | repeated Book books = 1; 34 | } 35 | 36 | message BookIdRequest { 37 | int32 id = 1; 38 | } 39 | -------------------------------------------------------------------------------- /step-5-node-client/client.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016, Google, Inc. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | var grpc = require('grpc'); 15 | 16 | var booksProto = grpc.load('books.proto'); 17 | 18 | var client = new booksProto.books.BookService('0.0.0.0:50051', grpc.credentials.createInsecure()); 19 | 20 | function printResponse(error, response) { 21 | if (error) 22 | console.log('Error: ', error); 23 | else 24 | console.log(response); 25 | } 26 | 27 | function listBooks() { 28 | client.list({}, function(error, books) { 29 | printResponse(error, books); 30 | }); 31 | } 32 | 33 | function insertBook(id, title, author) { 34 | var book = { 35 | id: parseInt(id), 36 | title: title, 37 | author: author 38 | }; 39 | client.insert(book, function(error, empty) { 40 | printResponse(error, empty); 41 | }); 42 | } 43 | 44 | function getBook(id) { 45 | client.get({ 46 | id: parseInt(id) 47 | }, function(error, book) { 48 | printResponse(error, book); 49 | }); 50 | } 51 | 52 | function deleteBook(id) { 53 | client.delete({ 54 | id: parseInt(id) 55 | }, function(error, empty) { 56 | printResponse(error, empty); 57 | }); 58 | } 59 | 60 | function watchBooks() { 61 | var call = client.watch({}); 62 | call.on('data', function(book) { 63 | console.log(book); 64 | }); 65 | } 66 | 67 | var processName = process.argv.shift(); 68 | var scriptName = process.argv.shift(); 69 | var command = process.argv.shift(); 70 | 71 | if (command == 'list') 72 | listBooks(); 73 | else if (command == 'insert') 74 | insertBook(process.argv[0], process.argv[1], process.argv[2]); 75 | else if (command == 'get') 76 | getBook(process.argv[0]); 77 | else if (command == 'delete') 78 | deleteBook(process.argv[0]); 79 | else if (command == 'watch') 80 | watchBooks(); 81 | --------------------------------------------------------------------------------