func (s *Server) v1DataPut(w http.ResponseWriter, r *http.Request) { ctx := r.Context() vars := mux.Vars(r) var value interface{} if err := util.NewJSONDecoder(r.Body).Decode(&value); err != nil { handleError(w, 400, err) return } txn, err := s.store.NewTransaction(ctx) if err != nil { handleErrorAuto(w, err) return } defer s.store.Close(ctx, txn) path, ok := storage.ParsePath("/" + strings.Trim(vars["path"], "/")) if !ok { handleErrorf(w, 400, "bad path format %v", vars["path"]) return } _, err = s.store.Read(ctx, txn, path) if err != nil { if !storage.IsNotFound(err) { handleErrorAuto(w, err) return } if err := s.makeDir(ctx, txn, path[:len(path)-1]); err != nil { handleErrorAuto(w, err) return } } else if r.Header.Get("If-None-Match") == "*" { handleResponse(w, 304, nil) return } if err := s.store.Write(ctx, txn, storage.AddOp, path, value); err != nil { handleErrorAuto(w, err) return } handleResponse(w, 204, nil) }
func handleErrorAuto(w http.ResponseWriter, err error) { var prev error for curr := err; curr != prev; { if storage.IsNotFound(curr) { handleError(w, 404, err) return } if IsWriteConflict(curr) { handleError(w, 404, err) return } if isBadRequest(curr) { handleError(w, http.StatusBadRequest, err) return } if storage.IsInvalidPatch(curr) { handleError(w, 400, err) return } prev = curr curr = errors.Cause(prev) } handleError(w, 500, err) }
func (s *Server) makeDir(ctx context.Context, txn storage.Transaction, path storage.Path) error { node, err := s.store.Read(ctx, txn, path) if err == nil { if _, ok := node.(map[string]interface{}); ok { return nil } return WriteConflictError{path} } if !storage.IsNotFound(err) { return err } if err := s.makeDir(ctx, txn, path[:len(path)-1]); err != nil { return err } if err := s.writeConflict(storage.AddOp, path); err != nil { return err } return s.store.Write(ctx, txn, storage.AddOp, path, map[string]interface{}{}) }
func ExampleStorage_Read() { // Initialize context for the example. Normally the caller would obtain the // context from an input parameter or instantiate their own. ctx := context.Background() // Define some dummy data to initialize the built-in store with. exampleInput := ` { "users": [ { "name": "alice", "color": "red", "likes": ["clouds", "ships"] }, { "name": "burt", "likes": ["cheese", "wine"] } ] } ` var data map[string]interface{} // OPA uses Go's standard JSON library but assumes that numbers have been // decoded as json.Number instead of float64. You MUST decode with UseNumber // enabled. decoder := json.NewDecoder(bytes.NewBufferString(exampleInput)) decoder.UseNumber() if err := decoder.Decode(&data); err != nil { // Handle error. } // Instantiate the storage layer. store := storage.New(storage.InMemoryWithJSONConfig(data)) txn, err := store.NewTransaction(ctx) if err != nil { // Handle error. } defer store.Close(ctx, txn) // Read values out of storage. v1, err1 := store.Read(ctx, txn, storage.MustParsePath("/users/1/likes/1")) v2, err2 := store.Read(ctx, txn, storage.MustParsePath("/users/0/age")) // Inspect the return values. fmt.Println("v1:", v1) fmt.Println("err1:", err1) fmt.Println("v2:", v2) fmt.Println("err2:", err2) fmt.Println("err2 is not found:", storage.IsNotFound(err2)) // Output: // v1: wine // err1: <nil> // v2: <nil> // err2: storage error (code: 1): bad path: /users/0/age, document does not exist // err2 is not found: true }