package main import ( "encoding/json" "fmt" "io/ioutil" "log" "net/http" "strings" ) //abstract CRUD operation for espoCRM Entity func crmCreateEntity(entityType string, jsonB []byte) (entity interface{}, err error) { url := CRMConfig.apiURL(entityType) jsonStr, err := postRAW(jsonB, url, crmBuildCommonAPIHeader()) if err != nil { entity, _ = crmRescueDuplicateCreate(err, entityType) return } return crmJSON2Entity(entityType, jsonStr) } func crmUpdateEntity(entityType string, id string, jsonB []byte) (entity interface{}, err error) { url := CRMConfig.apiEntityURL(entityType, id) jsonStr, err := patchRAW(jsonB, url, crmBuildCommonAPIHeader()) if err != nil { log.Println(err) return } return crmJSON2Entity(entityType, jsonStr) } func crmReplaceEntity(entityType string, id string, jsonB []byte) (entity interface{}, err error) { url := CRMConfig.apiEntityURL(entityType, id) jsonStr, err := putRAW(jsonB, url, crmBuildCommonAPIHeader()) if err != nil { log.Println(err) return } return crmJSON2Entity(entityType, jsonStr) } func crmDeleteEntity(entityType string, id string) (deleted bool, err error) { url := CRMConfig.apiEntityURL(entityType, id) resp, err := deleteRAW(url, crmBuildCommonAPIHeader()) if err != nil { log.Println(err) return } deleted = strings.ToLower(resp) == "true" return } //give an id, return json func crmGetEntity(entityType string, id string) (entity interface{}, err error) { url := CRMConfig.apiEntityURL(entityType, id) jsonStr, err := getRAW(url, crmBuildCommonAPIHeader()) if err != nil { log.Println(err) return } return crmJSON2Entity(entityType, jsonStr) } func crmBuildCommonAPIHeader() (headers map[string]string) { headers = map[string]string{} headers["Authorization"] = crmAuthHeader() headers["Accept"] = "application/json" headers["Content-Type"] = "application/json" return headers } //given a json string, convert it to Typed structure func crmJSON2Entity(entityType string, data string) (r interface{}, err error) { switch entityType { case "Lead": e := crmdLead{} err = json.Unmarshal([]byte(data), &e) r = e case "Account": //r = crmdAccount{} default: log.Fatalf("crmJSON2Entity: Unknown EntityType %s", entityType) } if err != nil { log.Println(err) } return } func crmRescueDuplicateCreate(e error, entityType string) (entity interface{}, success bool) { if !isErrIndicateDuplicate(e) { return } entity, err := e.(errorHTTPResponse).XStatusReason().Data2Entity(entityType) success = err == nil return } func isErrIndicateDuplicate(e error) (yes bool) { err, isOurError := e.(errorHTTPResponse) if !isOurError { return } reason := err.XStatusReason() yes = strings.ToLower(reason.Reason) == "duplicate" return } type crmdSearchFilter struct { Attribute string CompareMethod string //startWith, equals, contains, endsWith, like, isNull, isNotNull , notEquals ExpectValue string } type crmdSearchResult struct { Total int `json:"total"` List *json.RawMessage `json:"list"` } func crmSearchEntity(entityType string, filters []crmdSearchFilter) (result crmdSearchResult, err error) { url := CRMConfig.apiURL(entityType) req, err := http.NewRequest("GET", url, nil) if err != nil { log.Println("crmGetRecord: cannot form good http Request") log.Print(err) return } //auth header req.Header.Set("Authorization", crmAuthHeader()) req.Header.Set("Accept", "application/json, text/javascript, */*; q=0.01") req.Header.Set("Content-Type", "application/json") //query string q := req.URL.Query() q.Add("maxSize", "20") q.Add("maxSize", "0") q.Add("sortBy", "createdAt") q.Add("asc", "false") for k, v := range filters { switch v.CompareMethod { case "startWith", "equals", "contains", "endsWith", "like", "notEquals": q.Add(fmt.Sprintf("where[%d][type]", k), v.CompareMethod) q.Add(fmt.Sprintf("where[%d][attribute]", k), v.Attribute) q.Add(fmt.Sprintf("where[%d][value]", k), v.ExpectValue) case "isNull", "isNotNull": q.Add(fmt.Sprintf("where[%d][type]", k), v.CompareMethod) q.Add(fmt.Sprintf("where[%d][attribute]", k), v.Attribute) default: log.Printf("Unknown Compare Method %s", v.CompareMethod) } } req.URL.RawQuery = q.Encode() //debugDumpHTTPRequest(req) client := &http.Client{} r, err := client.Do(req) //debugDumpHTTPResponse(r) if err != nil { return } defer r.Body.Close() jsonB, err := ioutil.ReadAll(r.Body) if err != nil { return } err = json.Unmarshal(jsonB, &result) return } func crmFindEntityByID(entityType string, id string) (entity interface{}, err error) { filters := []crmdSearchFilter{ {"id", "equals", id}, } cs, err := crmSearchEntity(entityType, filters) if err != nil || cs.Total < 1 { return } if cs.Total > 1 { log.Printf("ERROR: more than one(%d) %s associated with %s", cs.Total, entityType, id) } switch entityType { case "Lead": e := []crmdLead{} err = json.Unmarshal(*cs.List, &e) if (err != nil) || (len(e) != cs.Total) { return } entity = e[0] case "Account": return } return } func crmFindEntityByAttr(entityType, attribute, value string) (total int, list interface{}, err error) { filters := []crmdSearchFilter{ {attribute, "equals", value}, } cs, err := crmSearchEntity(entityType, filters) if err != nil || cs.Total < 1 { return } total = cs.Total switch entityType { case "Lead": e := []crmdLead{} err = json.Unmarshal(*cs.List, &e) list = e case "Account": return } return }