// ServeHTTP handles all incoming HTTP requests for this data. func (d *Data) ServeHTTP(uuid dvid.UUID, ctx *datastore.VersionedCtx, w http.ResponseWriter, r *http.Request) { timedLog := dvid.NewTimeLog() // versionID := ctx.VersionID() // Get the action (GET, POST) action := strings.ToLower(r.Method) // Break URL request into arguments url := r.URL.Path[len(server.WebAPIPath):] parts := strings.Split(url, "/") if len(parts[len(parts)-1]) == 0 { parts = parts[:len(parts)-1] } // Handle POST on data -> setting of configuration if len(parts) == 3 && action == "put" { config, err := server.DecodeJSON(r) if err != nil { server.BadRequest(w, r, err) return } if err := d.ModifyConfig(config); err != nil { server.BadRequest(w, r, err) return } if err := datastore.SaveDataByUUID(uuid, d); err != nil { server.BadRequest(w, r, err) return } fmt.Fprintf(w, "Changed '%s' based on received configuration:\n%s\n", d.DataName(), config) return } if len(parts) < 4 { server.BadRequest(w, r, "Incomplete API request") return } // Process help and info. switch parts[3] { case "help": w.Header().Set("Content-Type", "text/plain") fmt.Fprintln(w, dtype.Help()) case "info": jsonBytes, err := d.MarshalJSON() if err != nil { server.BadRequest(w, r, err) return } w.Header().Set("Content-Type", "application/json") fmt.Fprintf(w, string(jsonBytes)) case "sync": if action != "post" { server.BadRequest(w, r, "Only POST allowed to sync endpoint") return } replace := r.URL.Query().Get("replace") == "true" if err := datastore.SetSyncByJSON(d, uuid, replace, r.Body); err != nil { server.BadRequest(w, r, err) return } case "label": if action != "get" { server.BadRequest(w, r, "Only GET action is available on 'label' endpoint.") return } if len(parts) < 5 { server.BadRequest(w, r, "Must include label after 'label' endpoint.") return } label, err := strconv.ParseUint(parts[4], 10, 64) if err != nil { server.BadRequest(w, r, err) return } if label == 0 { server.BadRequest(w, r, "Label 0 is protected background value and cannot be used for query.") return } queryStrings := r.URL.Query() jsonBytes, err := d.GetLabelJSON(ctx, label, queryStrings.Get("relationships") == "true") if err != nil { server.BadRequest(w, r, err) return } w.Header().Set("Content-type", "application/json") if _, err := w.Write(jsonBytes); err != nil { server.BadRequest(w, r, err) return } timedLog.Infof("HTTP %s: get synaptic elements for label %d (%s)", r.Method, label, r.URL) case "tag": if action != "get" { server.BadRequest(w, r, "Only GET action is available on 'tag' endpoint.") return } if len(parts) < 5 { server.BadRequest(w, r, "Must include tag string after 'tag' endpoint.") return } tag := Tag(parts[4]) queryStrings := r.URL.Query() jsonBytes, err := d.GetTagJSON(ctx, tag, queryStrings.Get("relationships") == "true") if err != nil { server.BadRequest(w, r, err) return } w.Header().Set("Content-type", "application/json") if _, err := w.Write(jsonBytes); err != nil { server.BadRequest(w, r, err) return } timedLog.Infof("HTTP %s: get synaptic elements for tag %s (%s)", r.Method, tag, r.URL) case "elements": switch action { case "get": // GET <api URL>/node/<UUID>/<data name>/elements/<size>/<offset> if len(parts) < 6 { server.BadRequest(w, r, "Expect size and offset to follow 'elements' in GET request") return } sizeStr, offsetStr := parts[4], parts[5] ext3d, err := dvid.NewExtents3dFromStrings(offsetStr, sizeStr, "_") if err != nil { server.BadRequest(w, r, err) return } elements, err := d.GetRegionSynapses(ctx, ext3d) if err != nil { server.BadRequest(w, r, err) return } w.Header().Set("Content-type", "application/json") jsonBytes, err := json.Marshal(elements) if err != nil { server.BadRequest(w, r, err) return } if _, err := w.Write(jsonBytes); err != nil { server.BadRequest(w, r, err) return } timedLog.Infof("HTTP %s: synapse elements in subvolume (size %s, offset %s) (%s)", r.Method, sizeStr, offsetStr, r.URL) case "post": if err := d.StoreSynapses(ctx, r.Body); err != nil { server.BadRequest(w, r, err) return } default: server.BadRequest(w, r, "Only GET or POST action is available on 'elements' endpoint.") return } case "element": // DELETE <api URL>/node/<UUID>/<data name>/element/<coord> if action != "delete" { server.BadRequest(w, r, "Only DELETE action is available on 'element' endpoint.") return } if len(parts) < 5 { server.BadRequest(w, r, "Must include coordinate after DELETE on 'element' endpoint.") return } pt, err := dvid.StringToPoint3d(parts[4], "_") if err != nil { server.BadRequest(w, r, err) return } if err := d.DeleteElement(ctx, pt); err != nil { server.BadRequest(w, r, err) return } timedLog.Infof("HTTP %s: delete synaptic element at %s (%s)", r.Method, pt, r.URL) case "move": // POST <api URL>/node/<UUID>/<data name>/move/<from_coord>/<to_coord> if action != "post" { server.BadRequest(w, r, "Only POST action is available on 'move' endpoint.") return } if len(parts) < 6 { server.BadRequest(w, r, "Must include 'from' and 'to' coordinate after 'move' endpoint.") return } fromPt, err := dvid.StringToPoint3d(parts[4], "_") if err != nil { server.BadRequest(w, r, err) return } toPt, err := dvid.StringToPoint3d(parts[5], "_") if err != nil { server.BadRequest(w, r, err) return } if err := d.MoveElement(ctx, fromPt, toPt); err != nil { server.BadRequest(w, r, err) return } timedLog.Infof("HTTP %s: move synaptic element from %s to %s (%s)", r.Method, fromPt, toPt, r.URL) default: server.BadAPIRequest(w, r, d) } }
// ServeHTTP handles all incoming HTTP requests for this data. func (d *Data) ServeHTTP(uuid dvid.UUID, ctx *datastore.VersionedCtx, w http.ResponseWriter, r *http.Request) { timedLog := dvid.NewTimeLog() // Get the action (GET, POST) action := strings.ToLower(r.Method) // Break URL request into arguments url := r.URL.Path[len(server.WebAPIPath):] parts := strings.Split(url, "/") if len(parts[len(parts)-1]) == 0 { parts = parts[:len(parts)-1] } // Handle POST on data -> setting of configuration if len(parts) == 3 && action == "put" { config, err := server.DecodeJSON(r) if err != nil { server.BadRequest(w, r, err) return } if err := d.ModifyConfig(config); err != nil { server.BadRequest(w, r, err) return } if err := datastore.SaveDataByUUID(uuid, d); err != nil { server.BadRequest(w, r, err) return } fmt.Fprintf(w, "Changed '%s' based on received configuration:\n%s\n", d.DataName(), config) return } if len(parts) < 4 { server.BadRequest(w, r, "Incomplete API request") return } // Process help and info. switch parts[3] { case "help": w.Header().Set("Content-Type", "text/plain") fmt.Fprintln(w, dtype.Help()) case "info": jsonBytes, err := d.MarshalJSON() if err != nil { server.BadRequest(w, r, err) return } w.Header().Set("Content-Type", "application/json") fmt.Fprintf(w, string(jsonBytes)) case "sync": if action != "post" { server.BadRequest(w, r, "Only POST allowed to sync endpoint") return } replace := r.URL.Query().Get("replace") == "true" if err := datastore.SetSyncByJSON(d, uuid, replace, r.Body); err != nil { server.BadRequest(w, r, err) return } case "count": if action != "get" { server.BadRequest(w, r, "Only GET action is available on 'count' endpoint.") return } if len(parts) < 6 { server.BadRequest(w, r, "Must include label and element type after 'count' endpoint.") return } label, err := strconv.ParseUint(parts[4], 10, 64) if err != nil { server.BadRequest(w, r, err) return } i := StringToIndexType(parts[5]) if i == UnknownIndex { server.BadRequest(w, r, fmt.Errorf("unknown index type specified (%q)", parts[5])) return } count, err := d.GetCountElementType(ctx, label, i) if err != nil { server.BadRequest(w, r, err) return } w.Header().Set("Content-type", "application/json") jsonStr := fmt.Sprintf(`{"Label":%d,%q:%d}`, label, i, count) if _, err := io.WriteString(w, jsonStr); err != nil { server.BadRequest(w, r, err) return } timedLog.Infof("HTTP %s: get count for label %d, index type %s: %s", r.Method, label, i, r.URL) case "top": if action != "get" { server.BadRequest(w, r, "Only GET action is available on 'top' endpoint.") return } if len(parts) < 6 { server.BadRequest(w, r, "Must include N and element type after 'top' endpoint.") return } n, err := strconv.ParseUint(parts[4], 10, 32) if err != nil { server.BadRequest(w, r, err) return } i := StringToIndexType(parts[5]) if i == UnknownIndex { server.BadRequest(w, r, fmt.Errorf("unknown index type specified (%q)", parts[5])) return } labelSizes, err := d.GetTopElementType(ctx, int(n), i) if err != nil { server.BadRequest(w, r, err) return } w.Header().Set("Content-type", "application/json") jsonBytes, err := json.Marshal(labelSizes) if err != nil { server.BadRequest(w, r, err) return } if _, err := w.Write(jsonBytes); err != nil { server.BadRequest(w, r, err) return } timedLog.Infof("HTTP %s: get top %d labels for index type %s: %s", r.Method, n, i, r.URL) case "threshold": if action != "get" { server.BadRequest(w, r, "Only GET action is available on 'threshold' endpoint.") return } if len(parts) < 6 { server.BadRequest(w, r, "Must include threshold # and element type after 'threshold' endpoint.") return } t, err := strconv.ParseUint(parts[4], 10, 32) if err != nil { server.BadRequest(w, r, err) return } minSize := uint32(t) i := StringToIndexType(parts[5]) if i == UnknownIndex { server.BadRequest(w, r, fmt.Errorf("unknown index type specified (%q)", parts[5])) return } queryStrings := r.URL.Query() var num, offset int offsetStr := queryStrings.Get("offset") if offsetStr != "" { offset, err = strconv.Atoi(offsetStr) if err != nil { server.BadRequest(w, r, fmt.Errorf("bad offset specified in query string (%q)", offsetStr)) return } } numStr := queryStrings.Get("n") if numStr != "" { num, err = strconv.Atoi(numStr) if err != nil { server.BadRequest(w, r, fmt.Errorf("bad num specified in query string (%q)", numStr)) return } } labels, err := d.GetLabelsByThreshold(ctx, i, minSize, offset, num) if err != nil { server.BadRequest(w, r, err) return } w.Header().Set("Content-type", "application/json") jsonBytes, err := json.Marshal(labels) if err != nil { server.BadRequest(w, r, err) return } if _, err := w.Write(jsonBytes); err != nil { server.BadRequest(w, r, err) return } timedLog.Infof("HTTP %s: get %d labels for index type %s with threshold %d: %s", r.Method, num, i, t, r.URL) default: server.BadAPIRequest(w, r, d) } }