github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/service/store/handler/handler.go (about) 1 package handler 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "reflect" 8 "strings" 9 "sync" 10 "time" 11 12 pb "github.com/tickoalcantara12/micro/v3/proto/store" 13 "github.com/tickoalcantara12/micro/v3/service/errors" 14 "github.com/tickoalcantara12/micro/v3/service/store" 15 "github.com/tickoalcantara12/micro/v3/util/auth/namespace" 16 ) 17 18 const ( 19 defaultDatabase = namespace.DefaultNamespace 20 defaultTable = namespace.DefaultNamespace 21 internalTable = "store" 22 ) 23 24 type Store struct { 25 // local Stores cache 26 sync.RWMutex 27 Stores map[string]bool 28 } 29 30 // List all the keys in a table 31 func (h *Store) List(ctx context.Context, req *pb.ListRequest, stream pb.Store_ListStream) error { 32 // set defaults 33 if req.Options == nil { 34 req.Options = &pb.ListOptions{} 35 } 36 if len(req.Options.Database) == 0 { 37 req.Options.Database = defaultDatabase 38 } 39 if len(req.Options.Table) == 0 { 40 req.Options.Table = defaultTable 41 } 42 43 // authorize the request 44 if err := namespace.AuthorizeAdmin(ctx, req.Options.Database, "store.Store.List"); err != nil { 45 return err 46 } 47 48 // setup the store 49 if err := h.setupTable(req.Options.Database, req.Options.Table); err != nil { 50 return errors.InternalServerError("store.Store.List", err.Error()) 51 } 52 53 // setup the options 54 opts := []store.ListOption{ 55 store.ListFrom(req.Options.Database, req.Options.Table), 56 } 57 if len(req.Options.Prefix) > 0 { 58 opts = append(opts, store.ListPrefix(req.Options.Prefix)) 59 } 60 if len(req.Options.Suffix) > 0 { 61 opts = append(opts, store.ListSuffix(req.Options.Suffix)) 62 } 63 if req.Options.Offset > 0 { 64 opts = append(opts, store.ListOffset(uint(req.Options.Offset))) 65 } 66 if req.Options.Limit > 0 { 67 opts = append(opts, store.ListLimit(uint(req.Options.Limit))) 68 } 69 if len(req.Options.Order) > 0 { 70 order := store.OrderAsc 71 if req.Options.Order == string(store.OrderDesc) { 72 order = store.OrderDesc 73 } 74 opts = append(opts, store.ListOrder(order)) 75 } 76 77 // list from the store 78 vals, err := store.DefaultStore.List(opts...) 79 if err != nil && err == store.ErrNotFound { 80 return errors.NotFound("store.Store.List", err.Error()) 81 } else if err != nil { 82 return errors.InternalServerError("store.Store.List", err.Error()) 83 } 84 85 // serialize the response 86 // TODO: batch sync 87 rsp := new(pb.ListResponse) 88 for _, val := range vals { 89 rsp.Keys = append(rsp.Keys, val) 90 } 91 92 err = stream.Send(rsp) 93 if err == io.EOF { 94 return nil 95 } 96 if err != nil { 97 return errors.InternalServerError("store.Store.List", err.Error()) 98 } 99 return nil 100 } 101 102 // Read records from the store 103 func (h *Store) Read(ctx context.Context, req *pb.ReadRequest, rsp *pb.ReadResponse) error { 104 // set defaults 105 if req.Options == nil { 106 req.Options = &pb.ReadOptions{} 107 } 108 if len(req.Options.Database) == 0 { 109 req.Options.Database = defaultDatabase 110 } 111 if len(req.Options.Table) == 0 { 112 req.Options.Table = defaultTable 113 } 114 115 // authorize the request 116 if err := namespace.AuthorizeAdmin(ctx, req.Options.Database, "store.Store.Read"); err != nil { 117 return err 118 } 119 120 // setup the store 121 if err := h.setupTable(req.Options.Database, req.Options.Table); err != nil { 122 return errors.InternalServerError("store.Store.Read", err.Error()) 123 } 124 125 // setup the options 126 opts := []store.ReadOption{ 127 store.ReadFrom(req.Options.Database, req.Options.Table), 128 } 129 if req.Options.Prefix { 130 opts = append(opts, store.ReadPrefix()) 131 } 132 if req.Options.Suffix { 133 opts = append(opts, store.ReadSuffix()) 134 } 135 if req.Options.Limit > 0 { 136 opts = append(opts, store.ReadLimit(uint(req.Options.Limit))) 137 } 138 if req.Options.Offset > 0 { 139 opts = append(opts, store.ReadOffset(uint(req.Options.Offset))) 140 } 141 if len(req.Options.Order) > 0 { 142 order := store.OrderAsc 143 if req.Options.Order == string(store.OrderDesc) { 144 order = store.OrderDesc 145 } 146 opts = append(opts, store.ReadOrder(order)) 147 } 148 149 // read from the database 150 vals, err := store.DefaultStore.Read(req.Key, opts...) 151 if err != nil && err == store.ErrNotFound { 152 return errors.NotFound("store.Store.Read", err.Error()) 153 } else if err != nil { 154 return errors.InternalServerError("store.Store.Read", err.Error()) 155 } 156 157 // serialize the result 158 for _, val := range vals { 159 metadata := make(map[string]*pb.Field) 160 for k, v := range val.Metadata { 161 metadata[k] = &pb.Field{ 162 Type: reflect.TypeOf(v).String(), 163 Value: fmt.Sprintf("%v", v), 164 } 165 } 166 rsp.Records = append(rsp.Records, &pb.Record{ 167 Key: val.Key, 168 Value: val.Value, 169 Expiry: int64(val.Expiry.Seconds()), 170 Metadata: metadata, 171 }) 172 } 173 return nil 174 } 175 176 // Write to the store 177 func (h *Store) Write(ctx context.Context, req *pb.WriteRequest, rsp *pb.WriteResponse) error { 178 // validate the request 179 if req.Record == nil { 180 return errors.BadRequest("store.Store.Write", "no record specified") 181 } 182 183 // set defaults 184 if req.Options == nil { 185 req.Options = &pb.WriteOptions{} 186 } 187 if len(req.Options.Database) == 0 { 188 req.Options.Database = defaultDatabase 189 } 190 if len(req.Options.Table) == 0 { 191 req.Options.Table = defaultTable 192 } 193 194 // authorize the request 195 if err := namespace.AuthorizeAdmin(ctx, req.Options.Database, "store.Store.Write"); err != nil { 196 return err 197 } 198 199 // setup the store 200 if err := h.setupTable(req.Options.Database, req.Options.Table); err != nil { 201 return errors.InternalServerError("store.Store.Write", err.Error()) 202 } 203 204 // setup the options 205 opts := []store.WriteOption{ 206 store.WriteTo(req.Options.Database, req.Options.Table), 207 } 208 209 // construct the record 210 metadata := make(map[string]interface{}) 211 for k, v := range req.Record.Metadata { 212 metadata[k] = v.Value 213 } 214 record := &store.Record{ 215 Key: req.Record.Key, 216 Value: req.Record.Value, 217 Expiry: time.Duration(req.Record.Expiry) * time.Second, 218 Metadata: metadata, 219 } 220 221 // write to the store 222 err := store.DefaultStore.Write(record, opts...) 223 if err != nil && err == store.ErrNotFound { 224 return errors.NotFound("store.Store.Write", err.Error()) 225 } else if err != nil { 226 return errors.InternalServerError("store.Store.Write", err.Error()) 227 } 228 229 return nil 230 } 231 232 func (h *Store) Delete(ctx context.Context, req *pb.DeleteRequest, rsp *pb.DeleteResponse) error { 233 // set defaults 234 if req.Options == nil { 235 req.Options = &pb.DeleteOptions{} 236 } 237 if len(req.Options.Database) == 0 { 238 req.Options.Database = defaultDatabase 239 } 240 if len(req.Options.Table) == 0 { 241 req.Options.Table = defaultTable 242 } 243 244 // authorize the request 245 if err := namespace.AuthorizeAdmin(ctx, req.Options.Database, "store.Store.Delete"); err != nil { 246 return err 247 } 248 249 // setup the store 250 if err := h.setupTable(req.Options.Database, req.Options.Table); err != nil { 251 return errors.InternalServerError("store.Store.Delete", err.Error()) 252 } 253 254 // setup the options 255 opts := []store.DeleteOption{ 256 store.DeleteFrom(req.Options.Database, req.Options.Table), 257 } 258 259 // delete from the store 260 if err := store.DefaultStore.Delete(req.Key, opts...); err == store.ErrNotFound { 261 return errors.NotFound("store.Store.Delete", err.Error()) 262 } else if err != nil { 263 return errors.InternalServerError("store.Store.Delete", err.Error()) 264 } 265 266 return nil 267 } 268 269 // Databases lists all the databases 270 func (h *Store) Databases(ctx context.Context, req *pb.DatabasesRequest, rsp *pb.DatabasesResponse) error { 271 // authorize the request 272 if err := namespace.AuthorizeAdmin(ctx, defaultDatabase, "store.Store.Database"); err != nil { 273 return err 274 } 275 276 // read the databases from the store 277 opts := []store.ReadOption{ 278 store.ReadPrefix(), 279 store.ReadFrom(defaultDatabase, internalTable), 280 } 281 recs, err := store.DefaultStore.Read("databases/", opts...) 282 if err != nil { 283 return errors.InternalServerError("store.Store.Databases", err.Error()) 284 } 285 286 // serialize the response 287 rsp.Databases = make([]string, len(recs)) 288 for i, r := range recs { 289 rsp.Databases[i] = strings.TrimPrefix(r.Key, "databases/") 290 } 291 return nil 292 } 293 294 // Tables returns all the tables in a database 295 func (h *Store) Tables(ctx context.Context, req *pb.TablesRequest, rsp *pb.TablesResponse) error { 296 // set defaults 297 if len(req.Database) == 0 { 298 req.Database = defaultDatabase 299 } 300 301 // authorize the request 302 if err := namespace.AuthorizeAdmin(ctx, req.Database, "store.Store.Tables"); err != nil { 303 return err 304 } 305 306 // construct the options 307 opts := []store.ReadOption{ 308 store.ReadPrefix(), 309 store.ReadFrom(defaultDatabase, internalTable), 310 } 311 312 // perform the query 313 query := fmt.Sprintf("tables/%v/", req.Database) 314 recs, err := store.DefaultStore.Read(query, opts...) 315 if err != nil { 316 return errors.InternalServerError("store.Store.Tables", err.Error()) 317 } 318 319 // serialize the response 320 rsp.Tables = make([]string, len(recs)) 321 for i, r := range recs { 322 rsp.Tables[i] = strings.TrimPrefix(r.Key, "tables/"+req.Database+"/") 323 } 324 return nil 325 } 326 327 func (h *Store) setupTable(database, table string) error { 328 // lock (might be a race) 329 h.Lock() 330 defer h.Unlock() 331 332 // attempt to get the database 333 if _, ok := h.Stores[database+":"+table]; ok { 334 return nil 335 } 336 337 // record the new database in the internal store 338 opt := store.WriteTo(defaultDatabase, internalTable) 339 dbRecord := &store.Record{Key: "databases/" + database, Value: []byte{}} 340 if err := store.DefaultStore.Write(dbRecord, opt); err != nil { 341 return fmt.Errorf("Error writing new database to internal table: %v", err) 342 } 343 344 // record the new table in the internal store 345 tableRecord := &store.Record{Key: "tables/" + database + "/" + table, Value: []byte{}} 346 if err := store.DefaultStore.Write(tableRecord, opt); err != nil { 347 return fmt.Errorf("Error writing new table to internal table: %v", err) 348 } 349 350 // write to the cache 351 h.Stores[database+":"+table] = true 352 return nil 353 }