package main import ( "encoding/json" "errors" "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 "Livecast": e := crmdLiveCast{} err = json.Unmarshal([]byte(data), &e) r = e case "Meeting": e := crmdMeeting{} err = json.Unmarshal([]byte(data), &e) r = e case "Location": e := crmdLocation{} err = json.Unmarshal([]byte(data), &e) r = e case "Contact": e := crmdContact{} err = json.Unmarshal([]byte(data), &e) r = e 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) { id = strings.TrimSpace(id) if id == "" { err = errors.New("empty ID not accepted") return } total, list, err := crmFindEntityByAttr(entityType, "id", id) if total > 1 { log.Printf("ERROR: more than one(%d) %s associated with %s", total, entityType, id) } if err != nil { return } switch entityType { case "Lead": e, ok := list.([]crmdLead) if (!ok) || (len(e) != total) { return } entity = e[0] case "Livecast": e, ok := list.([]crmdLiveCast) if (!ok) || (len(e) != total) { return } entity = e[0] case "Meeting": e, ok := list.([]crmdMeeting) if (!ok) || (len(e) != total) { return } entity = e[0] default: err = errors.New("unknown entity type " + entityType) } 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 } return cs.toEntityList(entityType) } func (m crmdSearchResult) toEntityList(entityType string) (total int, list interface{}, err error) { total = m.Total switch entityType { case "Lead": e := []crmdLead{} err = json.Unmarshal(*m.List, &e) list = e case "Livecast": e := []crmdLiveCast{} err = json.Unmarshal(*m.List, &e) list = e case "Meeting": e := []crmdMeeting{} err = json.Unmarshal(*m.List, &e) list = e default: err = errors.New("unknown entity type " + entityType) } return }