// HTTP handler for Flow Update func (h *handler) handlePutFlowDef() error { // derive flow def key from URL path flowDefKey := h.PathVar("flowDefKey") base.Logf("Flow Definition Key: ", flowDefKey) if flowDefKey == "" { return base.HTTPErrorf(http.StatusBadRequest, "Invalid Flow Def Key") } // wf := db.NewWorkflowDef(flowDefKey) // read JSON HTTP input into Object _, err := h.readObject(&wf) //base.Logf("Encoding Workflow Def ..%s",wf) data, _ := json.Marshal(wf) //base.Logf("Saving Workflow Def ...%s",wf) _, err = h.db.PutDocRaw(flowDefKey, data) //base.Logf("Done save...") if err != nil { return base.HTTPErrorf(http.StatusBadRequest, "Flow Definition Could not be updated") //TO DO: Need to relook at error } _, err = h.db.PutDocRaw(wf.Name, []byte(flowDefKey)) // this should be inside db trx TO DO //_, err =writeLastVersion(h.db , wf.Name , ) //db.Body{"ok": true, "flowDefKey": flowDefKey} h.writeJSONStatus(http.StatusCreated, &wf) return nil }
// HTTP handler for Flow query func (h *handler) handleGetFlowDef() error { // derive flow def key from URL path flowDefKey := h.PathVar("flowDefKey") base.Logf("Flow Definition Key: ", flowDefKey) if flowDefKey == "" { return base.HTTPErrorf(http.StatusBadRequest, "Invalid Flow Def Key") } data, err := h.db.GetDocRaw(flowDefKey) if err != nil { return base.HTTPErrorf(http.StatusBadRequest, "WF Definition Query Failed") } if data == nil { return base.HTTPErrorf(http.StatusBadRequest, "WF Definition Not Found. Check Flow Def Key again.") } wf := db.NewWorkflowDef(flowDefKey) err = json.Unmarshal([]byte(data), &wf) if err != nil { return err } h.writeJSONStatus(http.StatusCreated, wf) return nil }
// HTTP handler for Flow creation //TO DO: Need to handle validations. Last Version write for deriving correct Def Keys on update. // need to remove flow def from URL func (h *handler) handlePostFlowDef() error { // derive flow name from URL path flowDefName := h.PathVar("flowName") base.Logf("...Flow Definition Name...", flowDefName) //generate flow def key flowDefKey, err := NextFlowDefKey(h.db, flowDefName) base.Logf("Flow Def Key:%s", flowDefKey) if flowDefKey == "" { return base.HTTPErrorf(http.StatusBadRequest, "Could not generate Flow Def Key") } wf := db.NewWorkflowDef(flowDefKey) _, err = h.readObject(wf) if err != nil { return base.HTTPErrorf(http.StatusBadRequest, "Could not map JSON to WfDef Object") //TO DO: Need to relook at error } //base.Logf("Encoding Workflow Def ..%s",wf) data, _ := json.Marshal(wf) //base.Logf("Saving Workflow Def ...%s",wf) _, _ = h.db.PutDocRaw(flowDefKey, data) //base.Logf("Done save...") // this should be inside db trx TO DO //_, err =writeLastVersion(h.db , wf.Name , ) h.writeJSONStatus(http.StatusCreated, db.Body{"ok": true, "flowDefKey": flowDefKey}) return nil }
func (h *handler) handlePostFlowTxn() error { base.Logf("handlePostFlowTxn...") flowInstanceReq := db.NewFlowTxnRequest() _, err := h.readObject(flowInstanceReq) //read JSON request into ftr object base.Logf("Read Object...") if err != nil { return base.HTTPErrorf(http.StatusBadRequest, "Could not map JSON to FlowTxnRequest Object") } base.Logf("Flow Def Key is", flowInstanceReq.FlowDefKey) //use flow def key to retrive workflow raw data and convert it to wf object flowDefData, _ := h.db.GetDocRaw(flowInstanceReq.FlowDefKey) flowDef := &db.WorkflowDef{} _ = json.Unmarshal(flowDefData, flowDef) flowInstance := db.NewFlowInstance(flowInstanceReq, flowDef) base.Logf("Flow Instance Key:%s", flowInstance.InstanceKey) // Save Flow Instance to database data, _ := json.Marshal(flowInstance) _, _ = h.db.PutDocRaw(flowInstance.InstanceKey, data) //JSON output created flow instance h.writeJSONStatus(http.StatusCreated, &flowInstance) return nil }
//general validation of DB name. // can be skipped / changed according to database func ValidateDatabaseName(dbName string) error { if match, _ := regexp.MatchString(`^[a-z][-a-z0-9_$()+/]*$`, dbName); !match { return base.HTTPErrorf(http.StatusBadRequest, "Illegal database name: %s", dbName) } return nil }
func (h *handler) writeMultipart(subtype string, callback func(*multipart.Writer) error) error { if !h.requestAccepts("multipart/") { return base.HTTPErrorf(http.StatusNotAcceptable, "Response is multipart") } // Get the output stream. Due to a CouchDB bug, if we're sending to it we need to buffer the // output in memory so we can trim the final bytes. var output io.Writer var buffer bytes.Buffer if h.userAgentIs("CouchDB") { output = &buffer } else { output = h.response } writer := multipart.NewWriter(output) h.setHeader("Content-Type", fmt.Sprintf("multipart/%s; boundary=%q", subtype, writer.Boundary())) err := callback(writer) writer.Close() if err == nil && output == &buffer { // Trim trailing newline; CouchDB is allergic to it: _, err = h.response.Write(bytes.TrimRight(buffer.Bytes(), "\r\n")) } return err }
// Reads & parses the request body, handling either JSON or multipart. func (h *handler) readDocument() (db.Body, error) { //!contentType, attrs, _ := mime.ParseMediaType(h.rq.Header.Get("Content-Type")) contentType, _, _ := mime.ParseMediaType(h.rq.Header.Get("Content-Type")) switch contentType { case "", "application/json": return h.readJSON() //case "multipart/related": // if DebugMultipart { // raw, err := h.readBody() // if err != nil { // return nil, err // } // reader := multipart.NewReader(bytes.NewReader(raw), attrs["boundary"]) // body, err := db.ReadMultipartDocument(reader) // if err != nil { // ioutil.WriteFile("GatewayPUT.mime", raw, 0600) // base.Warn("Error reading MIME data: copied to file GatewayPUT.mime") // } // return body, err // } else { // reader := multipart.NewReader(h.requestBody, attrs["boundary"]) // return db.ReadMultipartDocument(reader) // } default: return nil, base.HTTPErrorf(http.StatusUnsupportedMediaType, "Invalid content type %s", contentType) } }
// Parses a JSON request body into a custom structure. func (h *handler) readJSONInto(into interface{}) error { contentType := h.rq.Header.Get("Content-Type") if contentType != "" && !strings.HasPrefix(contentType, "application/json") { return base.HTTPErrorf(http.StatusUnsupportedMediaType, "Invalid content type %s", contentType) } //TO DO: zip version to be added decoder := json.NewDecoder(h.requestBody) if err := decoder.Decode(into); err != nil { base.Warn("Couldn't parse JSON in HTTP request: %v", err) return base.HTTPErrorf(http.StatusBadRequest, "Bad JSON") } return nil }
// open new database connection handle func OpenDatabase(spec base.DatabaseSpec) (dbhandle base.DbHandle, err error) { dbhandle, err = base.GetDbHandle(spec) if err != nil { err = base.HTTPErrorf(http.StatusBadGateway, "Unable to connect to server: %s", err) } return }
// HTTP handler for Flow query func (h *handler) handleGetByNameFlowDef() error { // derive flow def key from URL path flowDef := h.PathVar("flowDef") base.Logf("Flow Definition: ", flowDef) if flowDef == "" { return base.HTTPErrorf(http.StatusBadRequest, "Invalid Flow Def Name") } // get all flow def versions data, err := h.db.GetDocRaw(flowDef) if err != nil { return base.HTTPErrorf(http.StatusBadRequest, "WF Definition Query Failed") } base.Logf("Flow Def Key:%s", string(data)) h.writeJSONStatus(http.StatusCreated, nil) return nil }
//TO DO: Need to add multi part reads //This function handles marshaling of input JSON into Struct func (h *handler) readObject(obj interface{}) (interface{}, error) { contentType, _, _ := mime.ParseMediaType(h.rq.Header.Get("Content-Type")) //process JSON Documents only switch contentType { case "", "application/json": return obj, h.readJSONInto(obj) default: return nil, base.HTTPErrorf(http.StatusUnsupportedMediaType, "Invalid content type %s", contentType) } }
func (sc *ServerContext) GetDatabase() (*db.DatabaseContext, error) { sc.lock.RLock() dbc := sc.database_ sc.lock.RUnlock() if dbc != nil { return dbc, nil } else { return nil, base.HTTPErrorf(http.StatusBadRequest, "database name is invalid or it is not open ") } return dbc, nil }
// Top-level handler call. It's passed a pointer to the specific method to run. func (h *handler) invoke(method handlerMethod) error { restExpvars.Add("requests_total", 1) restExpvars.Add("requests_active", 1) defer restExpvars.Add("requests_active", -1) switch h.rq.Header.Get("Content-Encoding") { case "": h.requestBody = h.rq.Body default: return base.HTTPErrorf(http.StatusUnsupportedMediaType, "Unsupported Content-Encoding;") } var err error // If there is a "db" path variable, look up the database context: var dbContext *db.DatabaseContext if dbContext, err = h.server.GetDatabase(); err != nil { h.logRequestLine() return err } // Authenticate, if not on admin port: //TO DO: Add authorization for DB Context //if h.privs != adminPrivs { // if err = h.checkAuth(dbContext); err != nil { // h.logRequestLine() // return err // } //} h.logRequestLine() // Now set the request's Database (i.e. context + user) if dbContext != nil { h.db, err = db.GetDatabase(dbContext, nil) if err != nil { return err } } return method(h) // Call the actual handler code }
// Get Document TO DO: Need to see if this is needed // need to check if DBC or Database func (db *DatabaseContext) GetDoc(docid string) (*document, error) { key := realDocID(docid) if key == "" { return nil, base.HTTPErrorf(400, "Invalid doc ID") } dbExpvars.Add("document_gets", 1) // need to check this doc := newDocument(docid) //get data from store data, err := db.DbHandle.GetRaw(key) if err != nil { return nil, err } //unmarshal JSON into doc.body doc.UnmarshalJSON(data) if err != nil { return nil, err } return doc, nil }
"net/http" "net/url" "os" "strconv" "strings" "sync/atomic" "time" "github.com/gorilla/mux" "github.com/mindhash/goFlow/auth" "github.com/mindhash/goFlow/base" "github.com/mindhash/goFlow/db" ) var kNotFoundError = base.HTTPErrorf(http.StatusNotFound, "missing") var kBadMethodError = base.HTTPErrorf(http.StatusMethodNotAllowed, "Method Not Allowed") var kBadRequestError = base.HTTPErrorf(http.StatusMethodNotAllowed, "Bad Request") var restExpvars = expvar.NewMap("goflow_rest") // If set to true, JSON output will be pretty-printed. var PrettyPrint bool = false // If set to true, diagnostic data will be dumped if there's a problem with MIME multipart data var DebugMultipart bool = false var lastSerialNum uint64 = 0 func init() { DebugMultipart = (os.Getenv("GatewayDebugMultipart") != "")