// Extend extends target schema func (schema *Schema) Extend(fromSchema *Schema) error { if schema.Parent == "" { schema.Parent = fromSchema.Parent } if schema.Prefix == "" { schema.Prefix = fromSchema.Prefix } if schema.URL == "" { schema.URL = fromSchema.URL } if schema.NamespaceID == "" { schema.NamespaceID = fromSchema.NamespaceID } schema.JSONSchema["properties"] = util.ExtendMap( util.MaybeMap(schema.JSONSchema["properties"]), util.MaybeMap(fromSchema.JSONSchema["properties"])) schema.JSONSchema["propertiesOrder"] = util.ExtendStringList( util.MaybeStringList(fromSchema.JSONSchema["propertiesOrder"]), util.MaybeStringList(schema.JSONSchema["propertiesOrder"])) MergeAction: for _, action := range fromSchema.Actions { for _, existingAction := range schema.Actions { if action.ID == existingAction.ID { continue MergeAction } } schema.Actions = append(schema.Actions, action) } schema.Metadata = util.ExtendMap(schema.Metadata, fromSchema.Metadata) return schema.Init() }
func filterSchemaByPermission(schema map[string]interface{}, permission string) map[string]interface{} { filteredSchema := map[string]interface{}{"type": "object"} filteredProperties := map[string]map[string]interface{}{} filteredRequirements := []string{} for id, property := range util.MaybeMap(schema["properties"]) { propertyMap := util.MaybeMap(property) allowedList := util.MaybeStringList(propertyMap["permission"]) for _, allowed := range allowedList { if allowed == permission { filteredProperties[id] = propertyMap } } } filteredSchema["properties"] = filteredProperties requirements := util.MaybeStringList(schema["required"]) if permission != "create" { // required property is used on only create event requirements = []string{} } for _, requirement := range requirements { if _, ok := filteredProperties[requirement]; ok { filteredRequirements = append(filteredRequirements, requirement) } } filteredSchema["required"] = filteredRequirements filteredSchema["additionalProperties"] = false return filteredSchema }
//GetSchema returns the schema filtered and trimmed for a specific user or nil when the user shouldn't see it at all func GetSchema(s *schema.Schema, authorization schema.Authorization) (result *schema.Resource, err error) { manager := schema.GetManager() metaschema, _ := manager.Schema("schema") policy, _ := manager.PolicyValidate("read", s.GetPluralURL(), authorization) if policy == nil { return } if s.IsAbstract() { return } rawSchema := s.JSON() filteredSchema := util.ExtendMap(nil, s.JSONSchema) rawSchema["schema"] = filteredSchema schemaProperties, schemaPropertiesOrder, schemaRequired := policy.FilterSchema( util.MaybeMap(s.JSONSchema["properties"]), util.MaybeStringList(s.JSONSchema["propertiesOrder"]), util.MaybeStringList(s.JSONSchema["required"])) filteredSchema["properties"] = schemaProperties filteredSchema["propertiesOrder"] = schemaPropertiesOrder filteredSchema["required"] = schemaRequired result, err = schema.NewResource(metaschema, rawSchema) if err != nil { log.Warning("%s %s", result, err) return } return }
func include(stmt *Stmt) (func(*Context) (interface{}, error), error) { source := util.MaybeString(stmt.RawData["include"]) if !filepath.IsAbs(source) { currentSource := stmt.File source = filepath.Clean(filepath.Join(filepath.Dir(currentSource), source)) } importedCode, err := LoadYAMLFile(source) if err != nil { return nil, stmt.Errorf("yaml parse error: %s", err) } stmt.Args = map[string]Value{} for key, value := range stmt.RawData { stmt.Args[key], err = NewValue(value) if err != nil { return nil, stmt.Errorf("yaml parse error: %s", err) } } stmt.File = source stmts, err := MakeStmts(stmt.File, importedCode) if err != nil { return nil, stmt.Errorf("import code parse error: %s", err) } stmt.Vars, err = MapToValue(util.MaybeMap(stmt.RawData["vars"])) if err != nil { return nil, stmt.Errorf("yaml parse error: %s", err) } return StmtsToFunc(stmt.funcName, stmts) }
func serveResponse(w http.ResponseWriter, context map[string]interface{}) { response := context["response"] responseCode, ok := context["code"].(int) if !ok { responseCode = 200 } if 200 <= responseCode && responseCode < 300 { w.WriteHeader(responseCode) routes.ServeJson(w, response) } else { message := util.MaybeMap(context["exception"]) middleware.HTTPJSONError(w, message["message"].(string), responseCode) } }
func parseCode(key string, code interface{}) map[string]interface{} { switch c := code.(type) { case string: l := newLexer(c) result := l.parseDict() if l.err != nil { args := map[string]interface{}{} args[key] = c return args } return result case map[string]interface{}: return c case map[interface{}]interface{}: return util.MaybeMap(c) } return map[string]interface{}{key: code} }
// Init initializes schema func (schema *Schema) Init() error { if schema.IsAbstract() { return nil } jsonSchema := schema.JSONSchema parent := schema.Parent required := util.MaybeStringList(jsonSchema["required"]) properties := util.MaybeMap(jsonSchema["properties"]) propertiesOrder := util.MaybeStringList(jsonSchema["propertiesOrder"]) if parent != "" && properties[FormatParentID(parent)] == nil { properties[FormatParentID(parent)] = getParentPropertyObj(parent, parent) propertiesOrder = append(propertiesOrder, FormatParentID(parent)) required = append(required, FormatParentID(parent)) } jsonSchema["required"] = required schema.JSONSchemaOnCreate = filterSchemaByPermission(jsonSchema, "create") schema.JSONSchemaOnUpdate = filterSchemaByPermission(jsonSchema, "update") schema.Properties = []Property{} for key := range properties { if !util.ContainsString(propertiesOrder, key) { propertiesOrder = append(propertiesOrder, key) } } jsonSchema["propertiesOrder"] = propertiesOrder for _, id := range propertiesOrder { property, ok := properties[id] if !ok { continue } propertyRequired := util.ContainsString(required, id) propertyObj, err := NewPropertyFromObj(id, property, propertyRequired) if err != nil { return fmt.Errorf("Invalid schema: Properties is missing %v", err) } schema.Properties = append(schema.Properties, *propertyObj) } return nil }
func define(stmt *Stmt) (func(*Context) (interface{}, error), error) { var err error funcName := util.MaybeString(stmt.Args["name"].Value(nil)) defineNode := MappingNodeToMap(stmt.RawNode["define"]) funcArgs := util.MaybeMap(stmt.Args["args"].Value(nil)) stmts, err := MakeStmts(stmt.File, defineNode["body"]) if err != nil { return nil, stmt.Errorf("error in define body: %s", err) } var body func(*Context) (interface{}, error) RegisterStmtParser( funcName, func(aStmt *Stmt) (func(*Context) (interface{}, error), error) { for key := range funcArgs { _, ok := aStmt.Args[key] if !ok { return nil, fmt.Errorf("missing augument %s", key) } } return func(context *Context) (value interface{}, err error) { vm := context.VM newContext := NewContext(vm) for key := range funcArgs { newContext.Set(key, aStmt.Arg(key, context)) } value, err = body(newContext) if vm.debugReturn { vm.debugReturn = false vm.debug = true } return }, nil }) body, err = StmtsToFunc("funcName", stmts) if err != nil { return nil, err } return func(context *Context) (interface{}, error) { return nil, nil }, nil }
func command(stmt *gohanscript.Stmt) (func(*gohanscript.Context) (interface{}, error), error) { var err error stmt.Args, err = gohanscript.MapToValue(util.MaybeMap(stmt.RawData["args"])) if err != nil { return nil, err } stmt.Args["command"], err = gohanscript.NewValue(stmt.RawData["command"]) if err != nil { return nil, err } return func(context *gohanscript.Context) (interface{}, error) { chdir := stmt.Arg("chdir", context) if chdir != nil { currentDir, _ := filepath.Abs(".") os.Chdir(util.MaybeString(chdir)) defer os.Chdir(currentDir) } command := util.MaybeString(stmt.Arg("command", context)) parts := strings.Fields(command) result, err := exec.Command(parts[0], parts[1:]...).CombinedOutput() return string(result), err }, nil }
//NewSchemaFromObj is a constructor for a schema by obj func NewSchemaFromObj(rawTypeData interface{}) (*Schema, error) { typeData := rawTypeData.(map[string]interface{}) metaschema, ok := GetManager().Schema("schema") if ok { err := metaschema.Validate(metaschema.JSONSchema, typeData) if err != nil { return nil, err } } id := util.MaybeString(typeData["id"]) if id == "" { return nil, &typeAssertionError{"id"} } plural := util.MaybeString(typeData["plural"]) if plural == "" { return nil, &typeAssertionError{"plural"} } title := util.MaybeString(typeData["title"]) if title == "" { return nil, &typeAssertionError{"title"} } description := util.MaybeString(typeData["description"]) if description == "" { return nil, &typeAssertionError{"description"} } singular := util.MaybeString(typeData["singular"]) if singular == "" { return nil, &typeAssertionError{"singular"} } schema := NewSchema(id, plural, title, description, singular) schema.Prefix = util.MaybeString(typeData["prefix"]) schema.URL = util.MaybeString(typeData["url"]) schema.Type = util.MaybeString(typeData["type"]) schema.Parent = util.MaybeString(typeData["parent"]) schema.OnParentDeleteCascade, _ = typeData["on_parent_delete_cascade"].(bool) schema.NamespaceID = util.MaybeString(typeData["namespace"]) schema.IsolationLevel = util.MaybeMap(typeData["isolation_level"]) jsonSchema, ok := typeData["schema"].(map[string]interface{}) if !ok { return nil, &typeAssertionError{"schema"} } schema.JSONSchema = jsonSchema schema.Metadata = util.MaybeMap(typeData["metadata"]) schema.Extends = util.MaybeStringList(typeData["extends"]) actions := util.MaybeMap(typeData["actions"]) schema.Actions = []Action{} for actionID, actionBody := range actions { action, err := NewActionFromObject(actionID, actionBody) if err != nil { return nil, err } schema.Actions = append(schema.Actions, action) } if err := schema.Init(); err != nil { return nil, err } return schema, nil }
func httpServer(stmt *gohanscript.Stmt) (func(*gohanscript.Context) (interface{}, error), error) { return func(globalContext *gohanscript.Context) (interface{}, error) { m := martini.Classic() var mutex = &sync.Mutex{} history := []interface{}{} server := map[string]interface{}{ "history": history, } m.Handlers() m.Use(middleware.Logging()) m.Use(martini.Recovery()) rawBody := util.MaybeMap(stmt.RawData["http_server"]) paths := util.MaybeMap(rawBody["paths"]) middlewareCode := util.MaybeString(rawBody["middleware"]) if middlewareCode != "" { vm := gohanscript.NewVM() err := vm.LoadString(stmt.File, middlewareCode) if err != nil { return nil, err } m.Use(func(w http.ResponseWriter, r *http.Request) { context := globalContext.Extend(nil) fillInContext(context.Data(), r, w, nil) reqData, _ := ioutil.ReadAll(r.Body) buff := ioutil.NopCloser(bytes.NewBuffer(reqData)) r.Body = buff var data interface{} if reqData != nil { json.Unmarshal(reqData, &data) } context.Set("request", data) vm.Run(context.Data()) }) } m.Use(func(w http.ResponseWriter, r *http.Request) { reqData, _ := ioutil.ReadAll(r.Body) buff := ioutil.NopCloser(bytes.NewBuffer(reqData)) r.Body = buff var data interface{} if reqData != nil { json.Unmarshal(reqData, &data) } mutex.Lock() server["history"] = append(server["history"].([]interface{}), map[string]interface{}{ "method": r.Method, "path": r.URL.String(), "data": data, }) mutex.Unlock() }) for path, body := range paths { methods, ok := body.(map[string]interface{}) if !ok { continue } for method, rawRouteBody := range methods { routeBody, ok := rawRouteBody.(map[string]interface{}) if !ok { continue } code := util.MaybeString(routeBody["code"]) vm := gohanscript.NewVM() err := vm.LoadString(stmt.File, code) if err != nil { return nil, err } switch method { case "get": m.Get(path, func(w http.ResponseWriter, r *http.Request, p martini.Params) { context := globalContext.Extend(nil) fillInContext(context.Data(), r, w, p) vm.Run(context.Data()) serveResponse(w, context.Data()) }) case "post": m.Post(path, func(w http.ResponseWriter, r *http.Request, p martini.Params) { context := globalContext.Extend(nil) fillInContext(context.Data(), r, w, p) requestData, _ := middleware.ReadJSON(r) context.Set("request", requestData) vm.Run(context.Data()) serveResponse(w, context.Data()) }) case "put": m.Put(path, func(w http.ResponseWriter, r *http.Request, p martini.Params) { context := globalContext.Extend(nil) fillInContext(context.Data(), r, w, p) requestData, _ := middleware.ReadJSON(r) context.Set("request", requestData) vm.Run(context.Data()) serveResponse(w, context.Data()) }) case "delete": m.Delete(path, func(w http.ResponseWriter, r *http.Request, p martini.Params) { context := globalContext.Extend(nil) fillInContext(context.Data(), r, w, p) vm.Run(context.Data()) serveResponse(w, context.Data()) }) } } } testMode := stmt.Args["test"].Value(globalContext).(bool) if testMode { ts := httptest.NewServer(m) server["server"] = ts return server, nil } m.RunOnAddr(stmt.Args["address"].Value(globalContext).(string)) return nil, nil }, nil }
//NewStmt makes gohan statement from yaml node func NewStmt(FileName string, node *yaml.Node) (stmt *Stmt, err error) { if node == nil { return nil, nil } stmt = &Stmt{} stmt.RawNode = MappingNodeToMap(node) stmt.Line = node.Line + 1 stmt.Column = node.Column var rawData interface{} yaml.UnmarshalNode(node, &rawData) stmt.RawData = util.MaybeMap(convertMapformat(rawData)) stmt.Name = util.MaybeString(stmt.RawData["name"]) stmt.File, _ = filepath.Abs(FileName) stmt.Dir = filepath.Dir(stmt.File) stmt.Always, err = MakeStmts(FileName, stmt.RawNode["always"]) if err != nil { return nil, stmt.Error(err) } stmt.Rescue, err = MakeStmts(FileName, stmt.RawNode["rescue"]) if err != nil { return nil, stmt.Error(err) } stmt.ElseStmt, err = MakeStmts(FileName, stmt.RawNode["else"]) if err != nil { return nil, stmt.Error(err) } stmt.Retry = util.MaybeInt(stmt.RawData["retries"]) stmt.Worker = util.MaybeInt(stmt.RawData["worker"]) stmt.LoopVar = util.MaybeString(stmt.RawData["loop_var"]) if stmt.LoopVar == "" { stmt.LoopVar = "item" } if stmt.Retry == 0 { stmt.Retry = 1 } stmt.Delay = util.MaybeInt(stmt.RawData["delay"]) stmt.WithItems, err = NewValue(stmt.RawData["with_items"]) if err != nil { return nil, stmt.Error(err) } stmt.WithDict, err = NewValue(stmt.RawData["with_dict"]) if err != nil { return nil, stmt.Error(err) } if stmt.RawData["when"] != nil { stmt.When, err = CompileExpr(util.MaybeString(stmt.RawData["when"])) if err != nil { return nil, stmt.Error(err) } } if stmt.RawData["until"] != nil { stmt.Until, err = CompileExpr(util.MaybeString(stmt.RawData["until"])) if err != nil { return nil, stmt.Error(err) } } stmt.Register = util.MaybeString(stmt.RawData["register"]) stmt.Vars, err = MapToValue(util.MaybeMap(stmt.RawData["vars"])) if err != nil { return nil, stmt.Error(err) } return stmt, nil }
// MaybeMap resturns a Map or nil func (context *Context) MaybeMap(key string) map[string]interface{} { return util.MaybeMap(context.MayGet(key)) }