// GenerateJavaModel generates the model code for the types defined in the RDL schema. func GenerateJavaModel(banner string, schema *rdl.Schema, outdir string, ns string) error { packageDir, err := javaGenerationDir(outdir, schema, ns) if err != nil { return err } registry := rdl.NewTypeRegistry(schema) for _, t := range schema.Types { tName, _, _ := rdl.TypeInfo(t) if strings.HasPrefix(string(tName), "rdl.") { continue } err := generateJavaType(banner, schema, registry, packageDir, t, ns) if err != nil { return err } } cName := capitalize(string(schema.Name)) + "Schema" out, file, _, err := outputWriter(packageDir, cName, ".java") if err != nil { return err } err = javaGenerateSchema(schema, cName, out, ns, banner) out.Flush() file.Close() if err != nil { return err } return nil }
// GenerateGoModel generates the model code for the types defined in the RDL schema. func GenerateGoModel(banner string, schema *rdl.Schema, outdir string, ns string, librdl string, prefixEnums bool, precise bool, untaggedUnions []string) error { name := strings.ToLower(string(schema.Name)) if strings.HasSuffix(outdir, ".go") { name = filepath.Base(outdir) outdir = filepath.Dir(outdir) } else { name = name + "_model.go" } out, file, _, err := outputWriter(outdir, name, ".go") if err != nil { return err } if file != nil { defer file.Close() } gen := &modelGenerator{rdl.NewTypeRegistry(schema), schema, out, librdl, prefixEnums, precise, nil, untaggedUnions, ns, schema.Name == "rdl"} gen.emitHeader(banner) if gen.err == nil { for _, t := range schema.Types { gen.emitType(t) } } out.Flush() if gen.err == nil { gen.err = GenerateGoSchema(banner, schema, outdir, ns, librdl) } return gen.err }
//ExportToMarkdown exports a markdown rendering of the schema func ExportToMarkdown(schema *rdl.Schema, outdir string) error { out, file, _, err := outputWriter(outdir, string(schema.Name), ".md") if err != nil { return err } if file != nil { defer file.Close() } registry := rdl.NewTypeRegistry(schema) category := "schema" if schema.Resources != nil { category = "API" } title := category if schema.Name != "" { title = "The " + capitalize(string(schema.Name)) + " " + category } else { title = capitalize(category) } fmt.Fprintf(out, "# %s\n\n", title) if schema.Comment != "" { fmt.Fprintf(out, "%s", formatBlock(schema.Comment, 0, 80, "")) } var rows [][]string if schema.Namespace != "" { rows = append(rows, []string{"namespace", string(schema.Namespace)}) } if schema.Version != nil { rows = append(rows, []string{"version", fmt.Sprintf("%d", *schema.Version)}) } if len(rows) > 0 { fmt.Fprintf(out, "This %s has the following attributes:\n\n", category) formatTable(out, []string{"Attribute", "Value"}, rows) } if schema.Resources != nil { fmt.Fprintf(out, "\n## Resources\n") groups := groupResources(schema.Resources) for group, lstRez := range groups { fmt.Fprintf(out, "\n### [%s](#%s)\n", group, group) //too much? formatType(out, schema, schema.FindType(group)) for _, rez := range lstRez { //ideally, sort by method here to be consistent formatResource(out, registry, rez) } } } if len(schema.Types) > 0 { fmt.Fprintf(out, "\n## Types\n") for _, typeDef := range schema.Types { formatType(out, registry, typeDef) } } out.Flush() return nil }
// GenerateGoSchema generates the code to regenerate the Schema func GenerateGoSchema(banner string, schema *rdl.Schema, outdir string, ns string, librdl string) error { name := strings.ToLower(string(schema.Name)) if strings.HasSuffix(outdir, ".go") { name = filepath.Base(outdir) outdir = filepath.Dir(outdir) } else { name = name + "_schema.go" } out, file, _, err := outputWriter(outdir, name, ".go") if err != nil { return err } if file != nil { defer file.Close() } rdlprefix := "rdl." if schema.Name == "rdl" { rdlprefix = "" } gen := &schemaGenerator{rdl.NewTypeRegistry(schema), schema, capitalize(string(schema.Name)), out, nil, banner, ns, librdl, rdlprefix} gen.emit(generationHeader(banner)) gen.emit("\n\npackage " + generationPackage(gen.schema, gen.ns) + "\n\n") if gen.schema.Name != "rdl" { gen.emit("import (\n") gen.emit("\trdl \"" + librdl + "\"\n") gen.emit(")\n\n") } gen.emit("var schema *" + rdlprefix + "Schema\n\n") gen.emit(fmt.Sprintf("func init() {\n\tsb := %sNewSchemaBuilder(%q)\n", rdlprefix, schema.Name)) if schema.Version != nil { gen.emit(fmt.Sprintf("\tsb.Version(%d)\n", *schema.Version)) } if schema.Namespace != "" { gen.emit(fmt.Sprintf("\tsb.Namespace(%q)\n", schema.Namespace)) } if schema.Comment != "" { gen.emit(fmt.Sprintf("\tsb.Comment(%q)\n", schema.Comment)) } gen.emit("\n") if gen.err == nil { for _, t := range schema.Types { gen.emitType(t) } } if gen.err == nil { for _, r := range schema.Resources { gen.emitResource(r) } } gen.emit("\tschema = sb.Build()\n") gen.emit("}\n\n") gen.emit(fmt.Sprintf("func %sSchema() *%sSchema {\n", capitalize(string(schema.Name)), rdlprefix)) gen.emit("\treturn schema\n") gen.emit("}\n") out.Flush() return gen.err }
func javaGenerateSchema(schema *rdl.Schema, cName string, writer io.Writer, ns string, banner string) error { reg := rdl.NewTypeRegistry(schema) funcMap := template.FuncMap{ "header": func() string { s := fmt.Sprintf("//\n// This file generated by %s\n//\n\n", banner) s2 := javaGenerationPackage(schema, ns) if s2 != "" { s = s + "package " + s2 + ";\n" } if ns != "com.yahoo.rdl" { s = s + "import com.yahoo.rdl.*;\n" } return s }, "version": func() string { if schema.Version != nil { return fmt.Sprintf(" sb.version(%d);", *schema.Version) } else { return "" } }, "name": func() string { return string(schema.Name) }, "namespace": func() string { if schema.Namespace != "" { return fmt.Sprintf("\n sb.namespace(%q);", schema.Namespace) } else { return "" } }, "comment": func() string { if schema.Comment != "" { return fmt.Sprintf("\n sb.comment(%q);", schema.Comment) } else { return "" } }, "cname": func() string { return cName }, "typeDef": func(t *rdl.Type) string { return javaGenerateTypeConstructor(reg, t) }, "resourceDef": func(r *rdl.Resource) string { return javaGenerateResourceConstructor(reg, r) }, } t := template.Must(template.New("util").Funcs(funcMap).Parse(javaSchemaTemplate)) return t.Execute(writer, schema) }
// GenerateJavaClient generates the client code to talk to the server func GenerateJavaClient(banner string, schema *rdl.Schema, outdir string, ns string, base string) error { reg := rdl.NewTypeRegistry(schema) packageDir, err := javaGenerationDir(outdir, schema, ns) if err != nil { return err } cName := capitalize(string(schema.Name)) out, file, _, err := outputWriter(packageDir, cName, "Client.java") if err != nil { return err } gen := &javaClientGenerator{reg, schema, cName, out, nil, banner, ns, base} gen.processTemplate(javaClientTemplate) out.Flush() file.Close() if gen.err != nil { return gen.err } //ResourceException - the throawable wrapper for alternate return types out, file, _, err = outputWriter(packageDir, "ResourceException", ".java") if err != nil { return err } err = javaGenerateResourceException(schema, out, ns) out.Flush() file.Close() if err != nil { return err } //ResourceError - the default data object for an error out, file, _, err = outputWriter(packageDir, "ResourceError", ".java") if err != nil { return err } err = javaGenerateResourceError(schema, out, ns) out.Flush() file.Close() return err }
// GenerateGoClient generates the client code to talk to the server. func GenerateGoClient(banner string, schema *rdl.Schema, outdir string, ns string, librdl string, prefixEnums bool, precise bool) error { name := strings.ToLower(string(schema.Name)) if strings.HasSuffix(outdir, ".go") { name = filepath.Base(outdir) outdir = filepath.Dir(outdir) } else { name = name + "_client.go" } out, file, _, err := outputWriter(outdir, name, ".go") if err != nil { return err } if file != nil { defer file.Close() } gen := &clientGenerator{rdl.NewTypeRegistry(schema), schema, capitalize(string(schema.Name)), out, nil, banner, prefixEnums, precise, ns, librdl} gen.emitClient() out.Flush() return gen.err }
// GenerateJavaServer generates the server code for the RDL-defined service func GenerateJavaServer(banner string, schema *rdl.Schema, outdir string, ns string, base string) error { reg := rdl.NewTypeRegistry(schema) packageDir, err := javaGenerationDir(outdir, schema, ns) if err != nil { return err } cName := capitalize(string(schema.Name)) async := false for _, r := range schema.Resources { if r.Async != nil && *r.Async { async = true break } } //FooHandler interface out, file, _, err := outputWriter(packageDir, cName, "Handler.java") if err != nil { return err } gen := &javaServerGenerator{reg, schema, cName, out, nil, banner, ns, async, base} gen.processTemplate(javaServerHandlerTemplate) out.Flush() file.Close() for _, r := range schema.Resources { if r.Async != nil && *r.Async { javaServerMakeAsyncResultModel(banner, schema, reg, outdir, r, ns, base) } else if len(r.Outputs) > 0 { javaServerMakeResultModel(banner, schema, reg, outdir, r, ns, base) } } //ResourceContext interface s := "ResourceContext" out, file, _, err = outputWriter(packageDir, s, ".java") if err != nil { return err } gen = &javaServerGenerator{reg, schema, cName, out, nil, banner, ns, async, base} gen.processTemplate(javaServerContextTemplate) out.Flush() file.Close() if gen.err != nil { return gen.err } //FooResources Jax-RS glue out, file, _, err = outputWriter(packageDir, cName, "Resources.java") if err != nil { return err } gen = &javaServerGenerator{reg, schema, cName, out, nil, banner, ns, async, base} gen.processTemplate(javaServerTemplate) out.Flush() file.Close() if gen.err != nil { return gen.err } //Note: to enable jackson's pretty printer: //import com.fasterxml.jackson.jaxrs.annotation.JacksonFeatures; //import com.fasterxml.jackson.databind.SerializationFeature; //for each resource, add this annotation: // @JacksonFeatures(serializationEnable = { SerializationFeature.INDENT_OUTPUT }) //FooServer - an optional server wrapper that sets up Jetty9/Jersey2 to run Foo out, file, _, err = outputWriter(packageDir, cName, "Server.java") if err != nil { return err } gen = &javaServerGenerator{reg, schema, cName, out, nil, banner, ns, async, base} gen.processTemplate(javaServerInitTemplate) out.Flush() file.Close() if gen.err != nil { return gen.err } //ResourceException - the throawable wrapper for alternate return types s = "ResourceException" out, file, _, err = outputWriter(packageDir, s, ".java") if err != nil { return err } err = javaGenerateResourceException(schema, out, ns) out.Flush() file.Close() if err != nil { return err } //ResourceError - the default data object for an error s = "ResourceError" out, file, _, err = outputWriter(packageDir, s, ".java") if err != nil { return err } err = javaGenerateResourceError(schema, out, ns) out.Flush() file.Close() return err }
func swagger(schema *rdl.Schema) (*SwaggerDoc, error) { reg := rdl.NewTypeRegistry(schema) sname := string(schema.Name) swag := new(SwaggerDoc) swag.Swagger = "2.0" swag.Schemes = []string{"http"} //swag.Host = "localhost" swag.BasePath = "/api" title := "API" if sname != "" { title = "The " + sname + " API" swag.BasePath = "/api/" + sname } swag.Info = new(SwaggerInfo) swag.Info.Title = title if schema.Version != nil { swag.Info.Version = fmt.Sprintf("%d", *schema.Version) swag.BasePath += "/v" + fmt.Sprintf("%d", *schema.Version) } if schema.Comment != "" { swag.Info.Description = schema.Comment } if len(schema.Resources) > 0 { paths := make(map[string]map[string]*SwaggerAction) for _, r := range schema.Resources { path := r.Path actions, ok := paths[path] if !ok { actions = make(map[string]*SwaggerAction) paths[path] = actions } meth := strings.ToLower(r.Method) action, ok := actions[meth] if !ok { action = new(SwaggerAction) } action.Summary = r.Comment tag := string(r.Type) //fixme: RDL has no tags, the type is actually too fine grain for this action.Tags = []string{tag} //multiple tags include the resource in multiple sections action.Produces = []string{"application/json"} var ins []*SwaggerParameter if len(r.Inputs) > 0 { if r.Method == "POST" || r.Method == "PUT" { action.Consumes = []string{"application/json"} } for _, in := range r.Inputs { param := new(SwaggerParameter) param.Name = string(in.Name) param.Description = in.Comment required := true if in.Optional { required = false } param.Required = required if in.PathParam { param.In = "path" } else if in.QueryParam != "" { param.In = "query" param.Name = in.QueryParam //swagger has no formal arg concept } else if in.Header != "" { //swagger has no header params continue } else { param.In = "body" } ptype, pformat, ref := makeSwaggerTypeRef(reg, in.Type) param.Type = ptype param.Format = pformat param.Schema = ref ins = append(ins, param) } action.Parameters = ins } responses := make(map[string]*SwaggerResponse) expected := r.Expected addSwaggerResponse(responses, string(r.Type), expected, "") if len(r.Alternatives) > 0 { for _, alt := range r.Alternatives { addSwaggerResponse(responses, string(r.Type), alt, "") } } if len(r.Exceptions) > 0 { for sym, errdef := range r.Exceptions { errType := errdef.Type //xxx addSwaggerResponse(responses, errType, sym, errdef.Comment) } } action.Responses = responses //responses -> r.expected and r.exceptions //security -> r.auth //r.outputs? //action.description? //action.operationId IGNORE actions[meth] = action paths[path] = actions } swag.Paths = paths } if len(schema.Types) > 0 { defs := make(map[string]*SwaggerType) for _, t := range schema.Types { ref := makeSwaggerTypeDef(reg, t) if ref != nil { tName, _, _ := rdl.TypeInfo(t) defs[string(tName)] = ref } } if true { props := make(map[string]*SwaggerType) codeType := new(SwaggerType) t := "integer" codeType.Type = t f := "int32" codeType.Format = f props["code"] = codeType msgType := new(SwaggerType) t2 := "string" msgType.Type = t2 props["message"] = msgType prop := new(SwaggerType) prop.Required = []string{"code", "message"} prop.Properties = props defs["ResourceError"] = prop } swag.Definitions = defs } return swag, nil }