github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/service/store/client/client.go (about) 1 package client 2 3 import ( 4 goctx "context" 5 "fmt" 6 "io" 7 "reflect" 8 "time" 9 10 pb "github.com/tickoalcantara12/micro/v3/proto/store" 11 "github.com/tickoalcantara12/micro/v3/service/client" 12 "github.com/tickoalcantara12/micro/v3/service/context" 13 "github.com/tickoalcantara12/micro/v3/service/context/metadata" 14 "github.com/tickoalcantara12/micro/v3/service/errors" 15 "github.com/tickoalcantara12/micro/v3/service/store" 16 ) 17 18 type srv struct { 19 options store.Options 20 21 // The database to use 22 Database string 23 24 // The table to use 25 Table string 26 27 // Addresses of the nodes 28 Nodes []string 29 30 // store service client 31 Client pb.StoreService 32 } 33 34 func (s *srv) Close() error { 35 return nil 36 } 37 38 func (s *srv) Init(opts ...store.Option) error { 39 for _, o := range opts { 40 o(&s.options) 41 } 42 s.Database = s.options.Database 43 s.Table = s.options.Table 44 s.Nodes = s.options.Nodes 45 s.Client = pb.NewStoreService("store", client.DefaultClient) 46 return nil 47 } 48 49 func (s *srv) Context() goctx.Context { 50 ctx := context.DefaultContext 51 md := make(metadata.Metadata) 52 if len(s.Database) > 0 { 53 md["Micro-Database"] = s.Database 54 } 55 56 if len(s.Table) > 0 { 57 md["Micro-Table"] = s.Table 58 } 59 return metadata.MergeContext(ctx, md, true) 60 } 61 62 // Sync all the known records 63 func (s *srv) List(opts ...store.ListOption) ([]string, error) { 64 options := store.ListOptions{ 65 Database: s.Database, 66 Table: s.Table, 67 } 68 69 for _, o := range opts { 70 o(&options) 71 } 72 73 listOpts := &pb.ListOptions{ 74 Database: options.Database, 75 Table: options.Table, 76 Prefix: options.Prefix, 77 Suffix: options.Suffix, 78 Limit: uint64(options.Limit), 79 Offset: uint64(options.Offset), 80 Order: string(options.Order), 81 } 82 83 stream, err := s.Client.List(s.Context(), &pb.ListRequest{Options: listOpts}, client.WithAddress(s.Nodes...), client.WithAuthToken()) 84 if err != nil && errors.Equal(err, errors.NotFound("", "")) { 85 return nil, store.ErrNotFound 86 } else if err != nil { 87 return nil, err 88 } 89 defer stream.Close() 90 91 var keys []string 92 93 for { 94 rsp, err := stream.Recv() 95 if err == io.EOF { 96 break 97 } 98 if err != nil { 99 return keys, err 100 } 101 102 for _, key := range rsp.Keys { 103 keys = append(keys, key) 104 } 105 } 106 107 return keys, nil 108 } 109 110 // Read a record with key 111 func (s *srv) Read(key string, opts ...store.ReadOption) ([]*store.Record, error) { 112 options := store.ReadOptions{ 113 Database: s.Database, 114 Table: s.Table, 115 } 116 117 for _, o := range opts { 118 o(&options) 119 } 120 121 readOpts := &pb.ReadOptions{ 122 Database: options.Database, 123 Table: options.Table, 124 Prefix: options.Prefix, 125 Suffix: options.Suffix, 126 Limit: uint64(options.Limit), 127 Offset: uint64(options.Offset), 128 Order: string(options.Order), 129 } 130 131 rsp, err := s.Client.Read(s.Context(), &pb.ReadRequest{ 132 Key: key, 133 Options: readOpts, 134 }, client.WithAddress(s.Nodes...), client.WithAuthToken()) 135 if err != nil && errors.Equal(err, errors.NotFound("", "")) { 136 return nil, store.ErrNotFound 137 } else if err != nil { 138 return nil, err 139 } 140 141 records := make([]*store.Record, 0, len(rsp.Records)) 142 143 for _, val := range rsp.Records { 144 metadata := make(map[string]interface{}) 145 146 for k, v := range val.Metadata { 147 switch v.Type { 148 // TODO: parse all types 149 default: 150 metadata[k] = v 151 } 152 } 153 154 records = append(records, &store.Record{ 155 Key: val.Key, 156 Value: val.Value, 157 Expiry: time.Duration(val.Expiry) * time.Second, 158 Metadata: metadata, 159 }) 160 } 161 162 return records, nil 163 } 164 165 // Write a record 166 func (s *srv) Write(record *store.Record, opts ...store.WriteOption) error { 167 options := store.WriteOptions{ 168 Database: s.Database, 169 Table: s.Table, 170 } 171 172 for _, o := range opts { 173 o(&options) 174 } 175 176 writeOpts := &pb.WriteOptions{ 177 Database: options.Database, 178 Table: options.Table, 179 } 180 181 metadata := make(map[string]*pb.Field) 182 183 for k, v := range record.Metadata { 184 metadata[k] = &pb.Field{ 185 Type: reflect.TypeOf(v).String(), 186 Value: fmt.Sprintf("%v", v), 187 } 188 } 189 190 _, err := s.Client.Write(s.Context(), &pb.WriteRequest{ 191 Record: &pb.Record{ 192 Key: record.Key, 193 Value: record.Value, 194 Expiry: int64(record.Expiry.Seconds()), 195 Metadata: metadata, 196 }, 197 Options: writeOpts}, client.WithAddress(s.Nodes...), client.WithAuthToken()) 198 if err != nil && errors.Equal(err, errors.NotFound("", "")) { 199 return store.ErrNotFound 200 } 201 202 return err 203 } 204 205 // Delete a record with key 206 func (s *srv) Delete(key string, opts ...store.DeleteOption) error { 207 options := store.DeleteOptions{ 208 Database: s.Database, 209 Table: s.Table, 210 } 211 212 for _, o := range opts { 213 o(&options) 214 } 215 216 deleteOpts := &pb.DeleteOptions{ 217 Database: options.Database, 218 Table: options.Table, 219 } 220 221 _, err := s.Client.Delete(s.Context(), &pb.DeleteRequest{ 222 Key: key, 223 Options: deleteOpts, 224 }, client.WithAddress(s.Nodes...), client.WithAuthToken()) 225 if err != nil && errors.Equal(err, errors.NotFound("", "")) { 226 return store.ErrNotFound 227 } 228 229 return err 230 } 231 232 func (s *srv) String() string { 233 return "service" 234 } 235 236 func (s *srv) Options() store.Options { 237 return s.options 238 } 239 240 // NewStore returns a new store service implementation 241 func NewStore(opts ...store.Option) store.Store { 242 var options store.Options 243 for _, o := range opts { 244 o(&options) 245 } 246 247 return &srv{ 248 options: options, 249 Database: options.Database, 250 Table: options.Table, 251 Nodes: options.Nodes, 252 Client: pb.NewStoreService("store", client.DefaultClient), 253 } 254 }