func (g *jsonschema) generateEnumSchema(file *generator.FileDescriptor, enum *generator.EnumDescriptor, index int) { typeName := file.GetPackage() + "." + strings.Join(enum.TypeName(), ".") typeName = strings.TrimPrefix(typeName, ".") def := g.definitions[typeName] if def == nil { def = g.enumToSchema(file, enum) g.definitions[typeName] = def } }
func (g *jsonschema) generateMessageSchema(file *generator.FileDescriptor, msg *generator.Descriptor, index int) { typeName := file.GetPackage() + "." + strings.Join(msg.TypeName(), ".") typeName = strings.TrimPrefix(typeName, ".") if g.definitions[typeName] == nil { def, deps := g.messageToSchema(file, msg) g.definitions[typeName] = def g.dependencies[typeName] = deps } }
// Generate generates code for the services in the given file. func (g *gensql) Generate(file *generator.FileDescriptor) { imp := generator.NewPluginImports(g.gen) g.imports = imp g.sqlPkg = imp.NewImport("database/sql") g.jsonPkg = imp.NewImport("encoding/json") g.contextPkg = imp.NewImport("golang.org/x/net/context") g.tracePkg = imp.NewImport("limbo.services/trace") g.runtimePkg = imp.NewImport("limbo.services/core/runtime/limbo") g.timePkg = imp.NewImport("time") g.mysqlPkg = imp.NewImport("github.com/go-sql-driver/mysql") var models []*generator.Descriptor for _, msg := range file.Messages() { model := limbo.GetModel(msg) if model == nil { continue } g.models["."+file.GetPackage()+"."+msg.GetName()] = msg models = append(models, msg) } if len(models) == 0 { return } // phase 1 for _, msg := range models { g.populateMessage(file, msg) } // phase 2 for _, msg := range models { g.populateMessageDeep(msg, nil) } for _, msg := range models { g.generateStmt(file, msg) g.generateScanners(file, msg) } // phase 3 for _, msg := range models { model := limbo.GetModel(msg) model.DeepColumn = nil model.DeepJoin = nil model.DeepScanner = nil } }
func (g *jsonschema) enumToSchema(file *generator.FileDescriptor, desc *generator.EnumDescriptor) interface{} { typeName := file.GetPackage() + "." + strings.Join(desc.TypeName(), ".") typeName = strings.TrimPrefix(typeName, ".") title := desc.TypeName()[len(desc.TypeName())-1] values := make([]interface{}, 0, 2*len(desc.Value)) for _, x := range desc.Value { values = append(values, x.GetNumber()) values = append(values, x.GetName()) } return map[string]interface{}{ // "id": typeName, "enum": values, "title": title, } }
// Generate generates code for the services in the given file. func (g *svcauth) Generate(file *generator.FileDescriptor) { for _, msg := range file.Messages() { name := file.GetPackage() + "." + msg.GetName() g.messages[name] = msg } if len(file.FileDescriptorProto.Service) == 0 { return } imp := generator.NewPluginImports(g.gen) g.imports = imp g.contextPkg = imp.NewImport("golang.org/x/net/context") g.runtimePkg = imp.NewImport("limbo.services/core/runtime/limbo") for i, service := range file.FileDescriptorProto.Service { g.generateService(file, service, i) } }
// generateService generates all the code for the named service. func (g *grpc) generateService(file *generator.FileDescriptor, service *pb.ServiceDescriptorProto, index int) { path := fmt.Sprintf("6,%d", index) // 6 means service. origServName := service.GetName() fullServName := origServName if pkg := file.GetPackage(); pkg != "" { fullServName = pkg + "." + fullServName } servName := generator.CamelCase(origServName) g.P() g.P("// Client API for ", servName, " service") g.P() // Client interface. g.P("type ", servName, "Client interface {") for i, method := range service.Method { g.gen.PrintComments(fmt.Sprintf("%s,2,%d", path, i)) // 2 means method in a service. g.P(g.generateClientSignature(servName, method)) } g.P("}") g.P() // Client structure. g.P("type ", unexport(servName), "Client struct {") g.P("cc *", grpcPkg, ".ClientConn") g.P("}") g.P() // NewClient factory. g.P("func New", servName, "Client (cc *", grpcPkg, ".ClientConn) ", servName, "Client {") g.P("return &", unexport(servName), "Client{cc}") g.P("}") g.P() var methodIndex, streamIndex int serviceDescVar := "_" + servName + "_serviceDesc" // Client method implementations. for _, method := range service.Method { var descExpr string if !method.GetServerStreaming() && !method.GetClientStreaming() { // Unary RPC method descExpr = fmt.Sprintf("&%s.Methods[%d]", serviceDescVar, methodIndex) methodIndex++ } else { // Streaming RPC method descExpr = fmt.Sprintf("&%s.Streams[%d]", serviceDescVar, streamIndex) streamIndex++ } g.generateClientMethod(servName, fullServName, serviceDescVar, method, descExpr) } g.P("// Server API for ", servName, " service") g.P() // Server interface. serverType := servName + "Server" g.P("type ", serverType, " interface {") for i, method := range service.Method { g.gen.PrintComments(fmt.Sprintf("%s,2,%d", path, i)) // 2 means method in a service. g.P(g.generateServerSignature(servName, method)) } g.P("}") g.P() // Server registration. g.P("func Register", servName, "Server(s *", grpcPkg, ".Server, srv ", serverType, ", options ...", gogogrpcPkg, ".ServerOption) {") g.P("s.RegisterService(", gogogrpcPkg, ".ApplyServerOptions(&", serviceDescVar, ", srv, options))") g.P("}") g.P() // Server handler implementations. var handlerNames []string for _, method := range service.Method { hname := g.generateServerMethod(servName, fullServName, method) handlerNames = append(handlerNames, hname) } // Service descriptor. g.P("var ", serviceDescVar, " = ", grpcPkg, ".ServiceDesc {") g.P("ServiceName: ", strconv.Quote(fullServName), ",") g.P("HandlerType: (*", serverType, ")(nil),") g.P("Methods: []", grpcPkg, ".MethodDesc{") for i, method := range service.Method { if method.GetServerStreaming() || method.GetClientStreaming() { continue } g.P("{") g.P("MethodName: ", strconv.Quote(method.GetName()), ",") g.P("Handler: ", handlerNames[i], ",") g.P("},") } g.P("},") g.P("Streams: []", grpcPkg, ".StreamDesc{") for i, method := range service.Method { if !method.GetServerStreaming() && !method.GetClientStreaming() { continue } g.P("{") g.P("StreamName: ", strconv.Quote(method.GetName()), ",") g.P("Handler: ", handlerNames[i], ",") if method.GetServerStreaming() { g.P("ServerStreams: true,") } if method.GetClientStreaming() { g.P("ClientStreams: true,") } g.P("},") } g.P("},") g.P("Metadata: ", file.VarName(), ",") g.P("}") g.P() }
func (g *gensql) populateMessage(file *generator.FileDescriptor, msg *generator.Descriptor) { model := limbo.GetModel(msg) model.MessageType = "." + file.GetPackage() + "." + msg.GetName() { // default scanner var found bool for _, scanner := range model.Scanner { if scanner.Name == "" { found = true break } } if !found { model.Scanner = append(model.Scanner, &limbo.ScannerDescriptor{Fields: "*"}) } } for _, scanner := range model.Scanner { scanner.MessageType = "." + file.GetPackage() + "." + msg.GetName() } for _, field := range msg.GetField() { if column := limbo.GetColumn(field); column != nil { column.MessageType = "." + file.GetPackage() + "." + msg.GetName() column.FieldName = field.GetName() if column.Name == "" { column.Name = field.GetName() } model.Column = append(model.Column, column) } if join := limbo.GetJoin(field); join != nil { if field.GetType() != pb.FieldDescriptorProto_TYPE_MESSAGE { g.gen.Fail(field.GetName(), "in", msg.GetName(), "must be a message") } join.MessageType = "." + file.GetPackage() + "." + msg.GetName() join.FieldName = field.GetName() join.ForeignMessageType = field.GetTypeName() if join.Name == "" { join.Name = field.GetName() } if join.Key == "" { join.Key = field.GetName() + "_id" } if join.ForeignKey == "" { join.ForeignKey = "id" } model.Join = append(model.Join, join) } } sort.Sort(limbo.SortedColumnDescriptors(model.Column)) sort.Sort(limbo.SortedJoinDescriptors(model.Join)) sort.Sort(limbo.SortedScannerDescriptors(model.Scanner)) }
func (g *jsonschema) messageToSchema(file *generator.FileDescriptor, desc *generator.Descriptor) (interface{}, []string) { typeName := file.GetPackage() + "." + strings.Join(desc.TypeName(), ".") typeName = strings.TrimPrefix(typeName, ".") title := desc.TypeName()[len(desc.TypeName())-1] var ( dependencies []string properties = make(map[string]interface{}) requiredProperties []string ) for i, field := range desc.GetField() { if field.OneofIndex != nil { continue } f, dep := g.fieldToSchema(field) if f == nil { continue } if limbo.IsRequiredProperty(field) { requiredProperties = append(requiredProperties, getJSONName(field)) } { comment := g.gen.Comments(fmt.Sprintf("%s,%d,%d", desc.Path(), 2, i)) comment = strings.TrimSpace(comment) if comment != "" { f["description"] = comment } } properties[getJSONName(field)] = f if dep != "" { dependencies = append(dependencies, dep) } } schema := map[string]interface{}{ "type": "object", "properties": properties, } if len(requiredProperties) > 0 { schema["required"] = requiredProperties } if len(desc.OneofDecl) > 0 { allOffDefs := make([]interface{}, 0, 1+len(desc.OneofDecl)) oneOfDefs := make([][]interface{}, len(desc.OneofDecl)) for i, field := range desc.GetField() { if field.OneofIndex == nil { continue } oneofIndex := *field.OneofIndex f, dep := g.fieldToSchema(field) if f == nil { continue } if field.IsRepeated() { f = map[string]interface{}{ "type": "array", "items": f, } } { comment := g.gen.Comments(fmt.Sprintf("%s,%d,%d", desc.Path(), 2, i)) comment = strings.TrimSpace(comment) if comment != "" { f["description"] = comment } } def := map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ getJSONName(field): f, }, "required": []string{getJSONName(field)}, } oneOfDefs[oneofIndex] = append(oneOfDefs[oneofIndex], def) if dep != "" { dependencies = append(dependencies, dep) } } allOffDefs = append(allOffDefs, schema) for i, defs := range oneOfDefs { def := map[string]interface{}{ "oneOf": defs, } { comment := g.gen.Comments(fmt.Sprintf("%s,%d,%d", desc.Path(), 8, i)) comment = strings.TrimSpace(comment) if comment != "" { def["description"] = comment } } allOffDefs = append(allOffDefs, def) } schema = map[string]interface{}{ "type": "object", "allOf": allOffDefs, } } { comment := g.gen.Comments(desc.Path()) comment = strings.TrimSpace(comment) if comment != "" { schema["description"] = comment } } { schema["title"] = title // schema["id"] = typeName } { dependencies = uniqStrings(dependencies) } return schema, dependencies }
// generateService generates all the code for the named service. func (g *svcauth) generateService(file *generator.FileDescriptor, service *pb.ServiceDescriptorProto, index int) { methods := g.findMethods(file, service) if len(methods) == 0 { return } origServName := service.GetName() fullServName := file.GetPackage() + "." + origServName servName := generator.CamelCase(origServName) authDescVarName := "_" + servName + "_authDesc" methodsByName := make(map[*pb.MethodDescriptorProto]*authMethod) for _, m := range methods { methodsByName[m.method] = m } g.gen.AddInitf("%s.RegisterServiceAuthDesc(&%s)", g.runtimePkg.Use(), authDescVarName) var interfaceMethods []string g.P(`var `, authDescVarName, ` = `, g.runtimePkg.Use(), `.ServiceAuthDesc{`) g.P(`ServiceName: `, strconv.Quote(fullServName), `,`) g.P(`HandlerType: ((*`, servName, `Server)(nil)),`) g.P(`AuthHandlerType: ((*`, servName, `ServerAuth)(nil)),`) g.P(`Methods: []`, g.runtimePkg.Use(), `.MethodAuthDesc{`) for _, method := range service.Method { if method.GetServerStreaming() || method.GetClientStreaming() { continue } g.P(`{`) g.P(`MethodName: `, strconv.Quote(method.GetName()), `,`) g.generateDesc(servName, method, methodsByName[method], &interfaceMethods) g.P("},") } g.P("},") g.P(`Streams: []`, g.runtimePkg.Use(), `.StreamAuthDesc{`) for _, method := range service.Method { if !method.GetServerStreaming() && !method.GetClientStreaming() { continue } g.P(`{`) g.P(`StreamName: `, strconv.Quote(method.GetName()), `,`) g.generateDesc(servName, method, methodsByName[method], &interfaceMethods) g.P("},") } g.P("},") g.P("}") g.P() if len(interfaceMethods) > 0 { sort.Strings(interfaceMethods) last := "" g.P(`type `, servName, `ServerAuth interface {`) for _, sig := range interfaceMethods { if sig != last { last = sig g.P(sig) } } g.P(`}`) } }
func (g *svcauth) findMethods(file *generator.FileDescriptor, service *pb.ServiceDescriptorProto) []*authMethod { methods := make([]*authMethod, 0, len(service.Method)) var ( defaultAuthnInfo *AuthnRule defaultAuthzInfo *AuthzRule ) if service.Options != nil { v, _ := proto.GetExtension(service.Options, E_DefaultAuthn) defaultAuthnInfo, _ = v.(*AuthnRule) } if service.Options != nil { v, _ := proto.GetExtension(service.Options, E_DefaultAuthz) defaultAuthzInfo, _ = v.(*AuthzRule) } for _, method := range service.Method { var ( authnInfo *AuthnRule authzInfo *AuthzRule ) { // authn v, _ := proto.GetExtension(method.Options, E_Authn) authnInfo, _ = v.(*AuthnRule) if authnInfo == nil && defaultAuthnInfo != nil { authnInfo = &AuthnRule{} } if authnInfo != nil { authnInfo = defaultAuthnInfo.Inherit(authnInfo) authnInfo.SetDefaults() } } { // authz v, _ := proto.GetExtension(method.Options, E_Authz) authzInfo, _ = v.(*AuthzRule) if authzInfo == nil && defaultAuthzInfo != nil { authzInfo = &AuthzRule{} } if authzInfo != nil { authzInfo = defaultAuthzInfo.Inherit(authzInfo) authzInfo.SetDefaults() } } if authnInfo == nil && authzInfo == nil { continue } methods = append(methods, &authMethod{ file: file, service: service, method: method, Authn: authnInfo, Authz: authzInfo, Name: fmt.Sprintf("/%s.%s/%s", file.GetPackage(), service.GetName(), method.GetName()), }) } return methods }
// generateService generates all the code for the named service. func (g *svchttp) generateService(file *generator.FileDescriptor, service *pb.ServiceDescriptorProto, index int) { apis := filterAPIs(service, service.Method, index) if len(apis) == 0 { return } origServName := service.GetName() fullServName := file.GetPackage() + "." + origServName servName := generator.CamelCase(origServName) gatewayVarName := "_" + servName + "_gatewayDesc" g.gen.AddInitf("%s.RegisterGatewayDesc(&%s)", g.runtimePkg.Use(), gatewayVarName) g.P(`var `, gatewayVarName, ` = `, g.runtimePkg.Use(), `.GatewayDesc{`) g.P(`ServiceName: `, strconv.Quote(fullServName), `,`) g.P(`HandlerType: ((*`, servName, `Server)(nil)),`) g.P(`Routes: []`, g.runtimePkg.Use(), `.RouteDesc{`) for _, api := range apis { _, method := api.desc, api.method httpMethod, pattern, ok := api.GetMethodAndPattern() if !ok { g.gen.Fail("xyz.featherhead.http requires a method: pattern") } if idx := strings.IndexRune(pattern, '?'); idx >= 0 { pattern = pattern[:idx] } g.P(`{`) g.P(`Method: `, strconv.Quote(httpMethod), `,`) g.P(`Pattern: `, strconv.Quote(pattern), `,`) g.P(`Handler: `, g.generateServerCallName(servName, method), `,`) g.P("},") } g.P("},") g.P("}") g.P() // Server handler implementations. for _, api := range apis { info, method := api.desc, api.method inputTypeName := method.GetInputType() inputType, _ := g.gen.ObjectNamed(inputTypeName).(*generator.Descriptor) httpMethod, pattern, ok := api.GetMethodAndPattern() queryParams := map[string]string{} if !ok { g.gen.Fail("xyz.featherhead.http requires a method: pattern") } if idx := strings.IndexRune(pattern, '?'); idx >= 0 { queryString := pattern[idx+1:] pattern = pattern[:idx] for _, pair := range strings.SplitN(queryString, "&", -1) { idx := strings.Index(pair, "={") if pair[len(pair)-1] != '}' || idx < 0 { g.gen.Fail("invalid query paramter") } queryParams[pair[:idx]] = pair[idx+2 : len(pair)-1] } } vars, err := router.ExtractVariables(pattern) if err != nil { g.gen.Error(err) return } var ( httpResponseWriter = g.httpPkg.Use() + ".ResponseWriter" httpRequest = g.httpPkg.Use() + ".Request" contextContext = g.contextPkg.Use() + ".Context" ) handlerMethod := g.generateServerCallName(servName, method) jujuErrors := g.jujuErrorsPkg.Use() g.P("func ", handlerMethod, "(srvDesc *", g.grpcPkg.Use(), ".ServiceDesc, srv interface{}, ctx ", contextContext, ", rw ", httpResponseWriter, ", req *", httpRequest, ") error {") g.P("if req.Method != ", strconv.Quote(httpMethod), "{") g.P(` return `, jujuErrors, `.MethodNotAllowedf("expected `, httpMethod, ` request")`) g.P("}") g.P() if len(vars) > 0 { routerP := g.routerPkg.Use() + ".P" g.P(`params := `, routerP, `(ctx)`) } g.P(`stream, err := `, g.runtimePkg.Use(), `.NewServerStream(ctx, rw, req, `, method.GetServerStreaming(), `, `, method.GetClientStreaming(), `, `, int(info.PageSize), `, func(x interface{}) error {`) g.P(`input := x.(*`, g.typeName(inputTypeName), `)`) g.P(`_ = input`) g.P() for param, value := range queryParams { g.P("// populate ?", param, "=", value) g.generateHttpMapping(inputType, value, "req.URL.Query().Get("+strconv.Quote(param)+")") } for _, v := range vars { g.P("// populate {", v.Name, "}") g.generateHttpMapping(inputType, v.Name, "params.Get("+strconv.Quote(v.Name)+")") } g.P(`return nil`) g.P(`})`) g.P() if !api.stream { g.P(`desc := &srvDesc.Methods[`, api.index, `]`) g.P(`output, err := desc.Handler(srv, stream.Context(), stream.RecvMsg, nil)`) g.P(`if err == nil && output == nil {`) g.P(`err = `, g.grpcPkg.Use(), `.Errorf(`, g.grpcCodesPkg.Use(), `.Internal, "internal server error")`) g.P(`}`) g.P(`if err == nil {`) g.P(`err = stream.SendMsg(output)`) g.P(`}`) } else { g.P(`desc := &srvDesc.Streams[`, api.index, `]`) g.P(`err = desc.Handler(srv, stream)`) } g.P(`if err != nil {`) g.P(`stream.SetError(err)`) g.P(`}`) g.P() g.P(`return stream.CloseSend()`) g.P("}") g.P() } }