github.com/mmatczuk/gohan@v0.0.0-20170206152520-30e45d9bdb69/cli/template.go (about) 1 package cli 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "os" 7 "path" 8 "regexp" 9 10 "github.com/cloudwan/gohan/schema" 11 "github.com/cloudwan/gohan/util" 12 "github.com/codegangsta/cli" 13 14 "github.com/flosch/pongo2" 15 "io/ioutil" 16 "strings" 17 ) 18 19 func deleteGohanExtendedProperties(node map[string]interface{}) { 20 extendedProperties := [...]string{"unique", "permission", "relation", 21 "relation_property", "view", "detail_view", "propertiesOrder", 22 "on_delete_cascade", "indexed", "relationColumn" } 23 24 for _, extendedProperty := range extendedProperties { 25 delete(node, extendedProperty) 26 } 27 } 28 29 func fixEnumDefaultValue(node map[string]interface{}) { 30 if defaultValue, ok := node["default"]; ok { 31 if enums, ok := node["enum"]; ok { 32 if defaultValueStr, ok := defaultValue.(string); ok { 33 enumsArr := util.MaybeStringList(enums) 34 if !util.ContainsString(enumsArr, defaultValueStr) { 35 delete(node, "default") 36 } 37 } 38 } 39 } 40 } 41 42 func removeEmptyRequiredList(node map[string]interface{}) { 43 const requiredProperty = "required" 44 45 if required, ok := node[requiredProperty]; ok { 46 switch list := required.(type) { 47 case []string: 48 if len(list) == 0 { 49 delete(node, requiredProperty) 50 } 51 case []interface{}: 52 if len(list) == 0 { 53 delete(node, requiredProperty) 54 } 55 } 56 } 57 } 58 59 func removeNotSupportedFormat(node map[string]interface{}) { 60 const formatProperty string = "format" 61 var allowedFormats = []string{"uri", "uuid", "email", "int32", "int64", "float", "double", 62 "byte", "binary", "date", "date-time", "password"} 63 64 if format, ok := node[formatProperty]; ok { 65 if format, ok := format.(string); ok { 66 if !util.ContainsString(allowedFormats, format) { 67 delete(node, formatProperty) 68 } 69 } 70 } 71 } 72 73 func fixPropertyTree(node map[string]interface{}) { 74 75 deleteGohanExtendedProperties(node) 76 fixEnumDefaultValue(node) 77 removeEmptyRequiredList(node) 78 removeNotSupportedFormat(node) 79 80 for _, value := range node { 81 switch childs := value.(type) { 82 case map[string]interface{}: 83 fixPropertyTree(childs) 84 case map[string]map[string]interface{}: 85 for _, value := range childs { 86 fixPropertyTree(value) 87 } 88 } 89 } 90 91 } 92 93 func toSwagger(in *pongo2.Value, param *pongo2.Value) (*pongo2.Value, *pongo2.Error) { 94 i := in.Interface() 95 m := i.(map[string]interface{}) 96 97 fixPropertyTree(m) 98 99 data, _ := json.MarshalIndent(i, param.String(), " ") 100 return pongo2.AsValue(string(data)), nil 101 } 102 103 func toSwaggerPath(in *pongo2.Value, param *pongo2.Value) (*pongo2.Value, *pongo2.Error) { 104 i := in.String() 105 r := regexp.MustCompile(":([^/]+)") 106 return pongo2.AsValue(r.ReplaceAllString(i, "{$1}")), nil 107 } 108 109 func hasIdParam(in *pongo2.Value, param *pongo2.Value) (*pongo2.Value, *pongo2.Error) { 110 i := in.String() 111 return pongo2.AsValue(strings.Contains(i, ":id")), nil 112 } 113 114 func init() { 115 pongo2.RegisterFilter("swagger", toSwagger) 116 pongo2.RegisterFilter("swagger_path", toSwaggerPath) 117 pongo2.RegisterFilter("swagger_has_id_param", hasIdParam) 118 } 119 120 func doTemplate(c *cli.Context) { 121 template := c.String("template") 122 manager := schema.GetManager() 123 configFile := c.String("config-file") 124 config := util.GetConfig() 125 err := config.ReadConfig(configFile) 126 if err != nil { 127 util.ExitFatal(err) 128 return 129 } 130 templateCode, err := util.GetContent(template) 131 if err != nil { 132 util.ExitFatal(err) 133 return 134 } 135 pwd, _ := os.Getwd() 136 os.Chdir(path.Dir(configFile)) 137 schemaFiles := config.GetStringList("schemas", nil) 138 if schemaFiles == nil { 139 util.ExitFatal("No schema specified in configuraion") 140 } else { 141 err = manager.LoadSchemasFromFiles(schemaFiles...) 142 if err != nil { 143 util.ExitFatal(err) 144 return 145 } 146 } 147 schemas := manager.OrderedSchemas() 148 149 if err != nil { 150 util.ExitFatal(err) 151 return 152 } 153 tpl, err := pongo2.FromString(string(templateCode)) 154 if err != nil { 155 util.ExitFatal(err) 156 return 157 } 158 if c.IsSet("split-by-resource-group") { 159 saveAllResources(schemas, tpl) 160 return 161 } 162 policies := manager.Policies() 163 policy := c.String("policy") 164 schemasPolicy := filterSchemasForPolicy(policy, policies, schemas) 165 output, err := tpl.Execute(pongo2.Context{"schemas": schemasPolicy}) 166 if err != nil { 167 util.ExitFatal(err) 168 return 169 } 170 os.Chdir(pwd) 171 fmt.Println(output) 172 } 173 174 func saveAllResources(schemas []*schema.Schema, tpl *pongo2.Template) { 175 for _, resource := range getAllResourcesFromSchemas(schemas) { 176 resourceSchemas := filerSchemasByResource(resource, schemas) 177 output, _ := tpl.Execute(pongo2.Context{"schemas": resourceSchemas}) 178 ioutil.WriteFile(resource+".json", []byte(output), 0644) 179 } 180 } 181 182 func getAllResourcesFromSchemas(schemas []*schema.Schema) []string { 183 resourcesSet := make(map[string]bool) 184 for _, schema := range schemas { 185 metadata, _ := schema.Metadata["resource_group"].(string) 186 resourcesSet[metadata] = true 187 } 188 resources := make([]string, 0, len(resourcesSet)) 189 for resource := range resourcesSet { 190 resources = append(resources, resource) 191 } 192 return resources 193 } 194 195 func filerSchemasByResource(resource string, schemas []*schema.Schema) []*schema.Schema { 196 var filteredSchemas []*schema.Schema 197 for _, schema := range schemas { 198 if schema.Metadata["resource_group"] == resource { 199 filteredSchemas = append(filteredSchemas, schema) 200 } 201 } 202 return filteredSchemas 203 } 204 205 func filterSchemasForPolicy(principal string, policies []*schema.Policy, schemas []*schema.Schema) []*schema.Schema { 206 var schemasPolicy []*schema.Schema 207 var matchedPolicies []* schema.Policy 208 for _, policy := range policies { 209 if policy.Principal == principal { 210 matchedPolicies = append(matchedPolicies, policy) 211 } 212 } 213 for _, schema := range schemas { 214 for _, policy := range matchedPolicies { 215 if policy.Resource.Path.MatchString(schema.URL) { 216 schemasPolicy = append(schemasPolicy, schema) 217 break 218 } 219 } 220 } 221 return schemasPolicy 222 } 223 224 func getTemplateCommand() cli.Command { 225 return cli.Command{ 226 Name: "template", 227 ShortName: "template", 228 Usage: "Convert gohan schema using pongo2 template", 229 Description: "Convert gohan schema using pongo2 template", 230 Flags: []cli.Flag{ 231 cli.StringFlag{Name: "config-file", Value: "gohan.yaml", Usage: "Server config File"}, 232 cli.StringFlag{Name: "template, t", Value: "", Usage: "Template File"}, 233 cli.StringFlag{Name: "split-by-resource-group", Value: "", Usage: "Group by resource"}, 234 cli.StringFlag{Name: "policy", Value: "admin", Usage: "Policy"}, 235 }, 236 Action: doTemplate, 237 } 238 } 239 240 func getOpenAPICommand() cli.Command { 241 return cli.Command{ 242 Name: "openapi", 243 ShortName: "openapi", 244 Usage: "Convert gohan schema to OpenAPI", 245 Description: "Convert gohan schema to OpenAPI", 246 Flags: []cli.Flag{ 247 cli.StringFlag{Name: "config-file", Value: "gohan.yaml", Usage: "Server config File"}, 248 cli.StringFlag{Name: "template, t", Value: "embed://etc/templates/openapi.tmpl", Usage: "Template File"}, 249 cli.StringFlag{Name: "split-by-resource-group", Value: "", Usage: "Group by resource"}, 250 cli.StringFlag{Name: "policy", Value: "admin", Usage: "Policy"}, 251 }, 252 Action: doTemplate, 253 } 254 } 255 256 func getMarkdownCommand() cli.Command { 257 return cli.Command{ 258 Name: "markdown", 259 ShortName: "markdown", 260 Usage: "Convert gohan schema to markdown doc", 261 Description: "Convert gohan schema to markdown doc", 262 Flags: []cli.Flag{ 263 cli.StringFlag{Name: "config-file", Value: "gohan.yaml", Usage: "Server config File"}, 264 cli.StringFlag{Name: "template, t", Value: "embed://etc/templates/markdown.tmpl", Usage: "Template File"}, 265 cli.StringFlag{Name: "split-by-resource-group", Value: "", Usage: "Group by resource"}, 266 cli.StringFlag{Name: "policy", Value: "admin", Usage: "Policy"}, 267 }, 268 Action: doTemplate, 269 } 270 } 271 272 func getDotCommand() cli.Command { 273 return cli.Command{ 274 Name: "dot", 275 ShortName: "dot", 276 Usage: "Convert gohan schema to dot file for graphviz", 277 Description: "Convert gohan schema to dot file for graphviz", 278 Flags: []cli.Flag{ 279 cli.StringFlag{Name: "config-file", Value: "gohan.yaml", Usage: "Server config File"}, 280 cli.StringFlag{Name: "template, t", Value: "embed://etc/templates/dot.tmpl", Usage: "Template File"}, 281 cli.StringFlag{Name: "split-by-resource-group", Value: "", Usage: "Group by resource"}, 282 cli.StringFlag{Name: "policy", Value: "admin", Usage: "Policy"}, 283 }, 284 Action: doTemplate, 285 } 286 }