// ReadOne loads a resource from Stretchr with the given path. func (r *Request) ReadOne() (*Resource, error) { response, err := r.UnderlyingRequest.Read() if err != nil { return nil, err } responseObject := response.BodyObject() // return the error if there was one errs := GetErrorsFromResponseObject(responseObject) if len(errs) > 0 { return nil, errs[0] } switch responseObject.Data().(type) { case map[string]interface{}: resource := MakeResourceAt(r.UnderlyingRequest.Path()) resource.data = objx.Map(responseObject.Data().(map[string]interface{})[common.ResponseObjectFieldData].([]interface{})[0].(map[string]interface{})).Copy() return resource, nil case []interface{}: return nil, ErrSingleObjectExpectedButGotArray case nil: return nil, ErrSingleObjectExpectedButGotNil } return nil, ErrSingleObjectExpectedButGotSomethingElse }
// createMapResponse is a helper for generating a response value from // a value of type map. func createMapResponse(value reflect.Value, options objx.Map, constructor func(interface{}, interface{}) interface{}, domain string) interface{} { response := reflect.MakeMap(value.Type()) for _, key := range value.MapKeys() { var elementOptions objx.Map keyStr := key.Interface().(string) if options != nil { var elementOptionsValue *objx.Value if options.Has(keyStr) { elementOptionsValue = options.Get(keyStr) } else if options.Has("*") { elementOptionsValue = options.Get("*") } if elementOptionsValue.IsMSI() { elementOptions = objx.Map(elementOptionsValue.MSI()) } else if elementOptionsValue.IsObjxMap() { elementOptions = elementOptionsValue.ObjxMap() } else { panic("Don't know what to do with option") } } itemResponse := createResponseValue(value.MapIndex(key), elementOptions, constructor, domain) response.SetMapIndex(key, reflect.ValueOf(itemResponse)) } return response.Interface() }
func (t *Template) replaceTokens(record intf.Record) string { output := t.template mapper := objx.Map(record) for _, token := range t.tokens { value := t.castValueToString(mapper.Get(token)) output = strings.Replace(output, `%{`+token+`}`, value, -1) } return output }
// createStructResponse is a helper for generating a response value // from a value of type struct. func createStructResponse(value reflect.Value, options objx.Map, constructor func(interface{}, interface{}) interface{}, domain string) interface{} { structType := value.Type() // Support "database/sql".Null* types, and any other types // matching that structure if v, err := createNullableDbResponse(value, structType); err == nil { return v } response := make(objx.Map) for i := 0; i < value.NumField(); i++ { fieldType := structType.Field(i) fieldValue := value.Field(i) if fieldType.Anonymous { embeddedResponse := CreateResponse(fieldValue.Interface(), options, constructor, domain).(objx.Map) for key, value := range embeddedResponse { // Don't overwrite values from the base struct if _, ok := response[key]; !ok { response[key] = value } } } else if unicode.IsUpper(rune(fieldType.Name[0])) { name := ResponseTag(fieldType) switch name { case "-": continue default: var subOptions objx.Map if options != nil && (options.Has(name) || options.Has("*")) { var subOptionsValue *objx.Value if options.Has(name) { subOptionsValue = options.Get(name) } else { subOptionsValue = options.Get("*") } if subOptionsValue.IsMSI() { subOptions = objx.Map(subOptionsValue.MSI()) } else if subOptionsValue.IsObjxMap() { subOptions = subOptionsValue.ObjxMap() } else { panic("Don't know what to do with option") } } response[name] = createResponseValue(fieldValue, subOptions, constructor, domain) } } } return response }
// ConvertMSIToObjxMap recursively converts map[string]interface{} // values to objx.Map. This is designed around the return types of // json.Unmarshal, so it may not work for non-json data. func ConvertMSIToObjxMap(value interface{}) interface{} { switch src := value.(type) { case map[string]interface{}: for key, val := range src { src[key] = ConvertMSIToObjxMap(val) } return objx.Map(src) case []interface{}: for index, val := range src { src[index] = ConvertMSIToObjxMap(val) } return src } return value }
// ReadMany loads many resources from Stretchr with the given path. func (r *Request) ReadMany() (*ResourceCollection, error) { response, err := r.UnderlyingRequest.Read() if err != nil { return nil, err } responseObject := response.BodyObject() // return the error if there was one errs := GetErrorsFromResponseObject(responseObject) if len(errs) > 0 { return nil, errs[0] } if resourceArray, exists := responseObject.Data().(map[string]interface{})[common.ResponseObjectFieldData].([]interface{}); exists { resources := make([]*Resource, len(resourceArray)) // populate the resources for resIndex, responseData := range resourceArray { resource := MakeResourceAt(r.UnderlyingRequest.Path()) resource.data = objx.Map(responseData.(map[string]interface{})).Copy() resources[resIndex] = resource } resourceCollection := NewResourceCollection(resources) // set the total (if there is one) if total, ok := responseObject.Data().(map[string]interface{})[common.ResponseObjectFieldTotal]; ok { if totalNum, ok := total.(float64); ok { resourceCollection.Total = totalNum } } return resourceCollection, nil } else { return nil, ErrArrayObjectExpectedButGotSomethingElse } }
func (engine *RuleEngine) DefineVirtualDevice(name string, obj objx.Map) error { title := name if obj.Has("title") { title = obj.Get("title").Str(name) } // if the device was for some reason defined in another script, // we must remove it engine.model.RemoveLocalDevice(name) dev := engine.model.EnsureLocalDevice(name, title) engine.cleanup.AddCleanup(func() { // runs when the rule file is reloaded engine.model.RemoveLocalDevice(name) }) if !obj.Has("cells") { return nil } v := obj.Get("cells") var m objx.Map switch { case v.IsObjxMap(): m = v.ObjxMap() case v.IsMSI(): m = objx.Map(v.MSI()) default: return fmt.Errorf("device %s doesn't have proper 'cells' property", name) } // Sorting cells by their names is not important when defining device // while the engine is not active because all the cells will be published // all at once when the engine starts. // On the other hand, when defining the device for the active engine // the newly added cells are published immediately and if their order // changes (map key order is random) the tests may break. cellNames := make([]string, 0, len(m)) for cellName, _ := range m { cellNames = append(cellNames, cellName) } sort.Strings(cellNames) for _, cellName := range cellNames { maybeCellDef := m[cellName] cellDef, ok := maybeCellDef.(objx.Map) if !ok { cd, ok := maybeCellDef.(map[string]interface{}) if !ok { return fmt.Errorf("%s/%s: bad cell definition", name, cellName) } cellDef = objx.Map(cd) } cellType, ok := cellDef["type"] if !ok { return fmt.Errorf("%s/%s: no cell type", name, cellName) } // FIXME: too much spaghetti for my taste if cellType == "pushbutton" { dev.SetButtonCell(cellName) continue } cellValue, ok := cellDef["value"] if !ok { return fmt.Errorf("%s/%s: cell value required for cell type %s", name, cellName, cellType) } cellReadonly := false cellReadonlyRaw, hasReadonly := cellDef["readonly"] if hasReadonly { cellReadonly, ok = cellReadonlyRaw.(bool) if !ok { return fmt.Errorf("%s/%s: non-boolean value of readonly property", name, cellName) } } if cellType == "range" { fmax := DEFAULT_CELL_MAX max, ok := cellDef["max"] if ok { fmax, ok = max.(float64) if !ok { return fmt.Errorf("%s/%s: non-numeric value of max property", name, cellName) } } // FIXME: can be float dev.SetRangeCell(cellName, cellValue, fmax, cellReadonly) } else { dev.SetCell(cellName, cellType.(string), cellValue, cellReadonly) } } return nil }