├── .gitignore ├── LICENSE ├── go.mod ├── go.sum ├── main.go ├── methods.go ├── module.go ├── readme.md ├── register.go ├── schema ├── schema.pb.go └── schema.proto └── templates.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | protoc-gen-graphql 8 | 9 | # Test binary, build with `go test -c` 10 | *.test 11 | 12 | # Output of the go coverage tool, specifically when used with LiteIDE 13 | *.out 14 | 15 | # IDE prefereces 16 | .vscode/ 17 | .idea/ 18 | .DS_Store 19 | protoc-gen-jaal 20 | protoc-gen-jaal.zip 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Appointy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module go.appointy.com/protoc-gen-jaal 2 | 3 | require ( 4 | github.com/davecgh/go-spew v1.1.1 // indirect 5 | github.com/golang/protobuf v1.3.1 6 | github.com/lyft/protoc-gen-star v0.4.10 7 | github.com/spf13/afero v1.2.2 // indirect 8 | github.com/stretchr/testify v1.3.0 // indirect 9 | ) 10 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= 5 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 6 | github.com/lyft/protoc-gen-star v0.4.10 h1:yekjh68Yp+AF+IXd/0vLd+DQv+eb6joCTA6qxRmtE2A= 7 | github.com/lyft/protoc-gen-star v0.4.10/go.mod h1:mE8fbna26u7aEA2QCVvvfBU/ZrPgocG1206xAFPcs94= 8 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 9 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 10 | github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= 11 | github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= 12 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 13 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= 14 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 15 | golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= 16 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 17 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | pgs "github.com/lyft/protoc-gen-star" 5 | pgsgo "github.com/lyft/protoc-gen-star/lang/go" 6 | ) 7 | 8 | func main() { 9 | pgs.Init(pgs.DebugEnv("DEBUG")). 10 | RegisterModule(&jaalModule{ModuleBase: &pgs.ModuleBase{}}). 11 | RegisterPostProcessor(pgsgo.GoFmt()). 12 | Render() 13 | } 14 | -------------------------------------------------------------------------------- /methods.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "os" 7 | "strings" 8 | 9 | "github.com/golang/protobuf/proto" 10 | pgd "github.com/golang/protobuf/protoc-gen-go/descriptor" 11 | pgs "github.com/lyft/protoc-gen-star" 12 | pbt "go.appointy.com/protoc-gen-jaal/schema" 13 | ) 14 | 15 | type Value struct { 16 | Value string 17 | Index int32 18 | } 19 | 20 | type enum struct { 21 | Name string 22 | Values []Value 23 | } 24 | 25 | type MsgFields struct { 26 | TargetName string 27 | FieldName string 28 | FuncPara string 29 | TargetVal string 30 | } 31 | 32 | type UnionObject struct { 33 | UnionName string 34 | UnionFields []string 35 | } 36 | 37 | type InputMap struct { 38 | FieldName string 39 | TargetName string 40 | TargetVal string 41 | Key string 42 | Value string 43 | } 44 | 45 | type Duration struct { 46 | FieldName string 47 | Name string 48 | } 49 | 50 | type Id struct { 51 | FieldName string 52 | Name string 53 | } 54 | type InputClass struct { 55 | Name string 56 | Type string 57 | InputObjName string 58 | Maps []InputMap 59 | Durations []Duration 60 | Fields []MsgFields 61 | Ids []Id 62 | } 63 | 64 | func (m *jaalModule) scalarMap(scalar string) string { 65 | // Maps protoc scalars to go scalars 66 | 67 | switch scalar { 68 | case "BOOL": 69 | return "bool" 70 | case "INT32": 71 | return "int32" 72 | case "INT64": 73 | return "int64" 74 | case "UINT32": 75 | return "uint32" 76 | case "UINT64": 77 | return "uint64" 78 | case "SINT32": 79 | return "int32" 80 | case "SINT64": 81 | return "int64" 82 | case "FIXED32": 83 | return "uint32" 84 | case "FIXED64": 85 | return "uint64" 86 | case "SFIXED32": 87 | return "int32" 88 | case "SFIXED64": 89 | return "int64" 90 | case "FLOAT": 91 | return "float32" 92 | case "DOUBLE": 93 | return "float64" 94 | case "STRING": 95 | return "string" 96 | case "BYTES": 97 | return "[]byte" 98 | 99 | } 100 | return "" 101 | 102 | } 103 | func (m *jaalModule) fieldElementType(valKey pgs.FieldTypeElem) string { 104 | // returns Type for a pgs.FieldTypeElem 105 | 106 | switch valKey.ProtoType().Proto().String() { 107 | case "TYPE_MESSAGE": 108 | 109 | obj := valKey.Embed() 110 | return obj.Name().String() 111 | 112 | case "TYPE_ENUM": 113 | 114 | enum := valKey.Enum() 115 | return enum.Name().String() 116 | 117 | default: 118 | 119 | tType := strings.Split(valKey.ProtoType().Proto().String(), "_") 120 | return m.scalarMap(tType[len(tType)-1]) 121 | 122 | } 123 | } 124 | 125 | type PayloadFields struct { 126 | FieldName string 127 | FuncPara string 128 | TargetVal string 129 | } 130 | 131 | type OneOfFields struct { 132 | CaseName string 133 | ReturnType string 134 | } 135 | 136 | type UnionObjectPayload struct { 137 | FieldName string 138 | FuncReturn string 139 | SwitchName string 140 | Fields []OneOfFields 141 | } 142 | type PayloadMap struct { 143 | FieldName string 144 | TargetVal string 145 | } 146 | type Payload struct { 147 | Name string 148 | Type string 149 | PayloadObjName string 150 | UnionObjects []UnionObjectPayload 151 | Maps []PayloadMap 152 | Durations []Duration 153 | Fields []PayloadFields 154 | Ids []Id 155 | } 156 | 157 | func (m *jaalModule) EnumType(enumData pgs.Enum, imports map[string]string, initFunctionsName map[string]bool) (string, error) { 158 | // returns generated template in for a enum type 159 | 160 | enumval := enum{Name: enumData.Name().UpperCamelCase().String()} 161 | 162 | initFunctionsName["Register"+enumval.Name] = true 163 | 164 | for _, val := range enumData.Values() { 165 | enumval.Values = append(enumval.Values, Value{Value: val.Name().String(), Index: val.Value()}) 166 | } 167 | 168 | tmp := getEnumTemplate() 169 | buf := &bytes.Buffer{} 170 | 171 | if err := tmp.Execute(buf, enumval); err != nil { 172 | return "", err 173 | } 174 | 175 | return buf.String(), nil 176 | } 177 | 178 | type Oneof struct { 179 | Name string 180 | SchemaObjectPara string 181 | FieldFuncPara string 182 | FieldFuncSecondParaFuncPara string 183 | TargetName string 184 | TargetVal string 185 | } 186 | 187 | func (m *jaalModule) GetSkipOption(message pgs.Message) (bool, error) { 188 | //returns true if a message have skip flag as true or no skip option 189 | //returns false if message have skip flag as false 190 | 191 | opt := message.Descriptor().GetOptions() 192 | x, err := proto.GetExtension(opt, pbt.E_Skip) 193 | 194 | if opt == nil { 195 | 196 | return false, nil 197 | 198 | } 199 | 200 | if err != nil { 201 | 202 | if err == proto.ErrMissingExtension { 203 | 204 | return false, nil 205 | 206 | } 207 | 208 | return false, err 209 | 210 | } 211 | 212 | return *x.((*bool)), nil 213 | } 214 | 215 | func (m *jaalModule) GetMessageTypeOption(message pgs.Message) (bool, string, error) { 216 | //returns message type if present 217 | 218 | opt := message.Descriptor().GetOptions() 219 | x, err := proto.GetExtension(opt, pbt.E_Type) 220 | 221 | if opt == nil { 222 | 223 | return false, "", nil 224 | 225 | } 226 | 227 | if err != nil { 228 | 229 | if err == proto.ErrMissingExtension { 230 | 231 | return false, "", nil 232 | 233 | } 234 | 235 | return false, "", err 236 | 237 | } 238 | 239 | return true, *x.((*string)), nil 240 | } 241 | 242 | func (m *jaalModule) OneofInputType(inputData pgs.Message, imports map[string]string, initFunctionsName map[string]bool) (string, error) { 243 | /* 244 | returns generated template(Input) in for a Oneof type 245 | Primitive,Enum and object type are handled inside a oneof 246 | */ 247 | var oneOfArr []Oneof 248 | 249 | for _, oneof := range inputData.OneOfs() { 250 | 251 | for _, fields := range oneof.Fields() { 252 | //checks skip_input field option 253 | if fieldSkip, err := m.GetFieldOptionInput(fields); err != nil { 254 | return "", err 255 | } else if fieldSkip { 256 | continue 257 | } 258 | name := fields.Message().Name().UpperCamelCase().String() + "_" + fields.Name().UpperCamelCase().String() 259 | initFunctionsName["RegisterInput"+name] = true 260 | schemaObjectPara := fields.Message().Name().LowerCamelCase().String() + fields.Name().UpperCamelCase().String() 261 | fieldFuncPara := fields.Name().LowerCamelCase().String() 262 | targetName := fields.Name().UpperCamelCase().String() 263 | fieldFuncSecondParaFuncPara := m.RPCFieldType(fields) 264 | if fieldFuncSecondParaFuncPara[0] == '*' { 265 | fieldFuncSecondParaFuncPara = fieldFuncSecondParaFuncPara[1:len(fieldFuncSecondParaFuncPara)] 266 | } 267 | goPkg := "" 268 | targetVal := "" 269 | if fields.Type().IsEnum() { 270 | goPkg = m.GetGoPackageOfFiles(inputData.File(), fields.Type().Enum().File()) 271 | if goPkg != "" { 272 | goPkg += "." 273 | } 274 | targetVal = "*source" 275 | } else if fields.Type().IsEmbed() { 276 | goPkg = m.GetGoPackageOfFiles(inputData.File(), fields.Type().Embed().File()) 277 | if goPkg != "" { 278 | goPkg += "." 279 | } 280 | targetVal = "source" 281 | } else { 282 | targetVal = "*source" 283 | } 284 | fieldFuncSecondParaFuncPara = goPkg + fieldFuncSecondParaFuncPara 285 | if fieldFuncSecondParaFuncPara == "field_mask.FieldMask" { 286 | targetVal = "gtypes.ModifyFieldMask(source)" 287 | } else if strings.HasSuffix(fieldFuncSecondParaFuncPara, "duration.Duration") { 288 | fieldFuncSecondParaFuncPara = fieldFuncSecondParaFuncPara[:len(fieldFuncSecondParaFuncPara)-17] 289 | fieldFuncSecondParaFuncPara += "schemabuilder.Duration" 290 | targetVal = "(*duration.Duration)(" + targetVal + ")" 291 | } else if strings.HasSuffix(fieldFuncSecondParaFuncPara, "timestamp.Timestamp") { 292 | fieldFuncSecondParaFuncPara = fieldFuncSecondParaFuncPara[:len(fieldFuncSecondParaFuncPara)-19] 293 | fieldFuncSecondParaFuncPara += "schemabuilder.Timestamp" 294 | targetVal = "(*timestamp.Timestamp)(" + targetVal + ")" 295 | } else if strings.HasSuffix(fieldFuncSecondParaFuncPara, "byte") { 296 | fieldFuncSecondParaFuncPara = "schemabuilder.Bytes" 297 | targetVal = "source.Value" 298 | } 299 | oneOfArr = append(oneOfArr, Oneof{TargetVal: targetVal, Name: name, SchemaObjectPara: schemaObjectPara, FieldFuncPara: fieldFuncPara, TargetName: targetName, FieldFuncSecondParaFuncPara: fieldFuncSecondParaFuncPara}) 300 | } 301 | } 302 | 303 | tmp := getOneofInputTemplate() 304 | buf := &bytes.Buffer{} 305 | 306 | if err := tmp.Execute(buf, oneOfArr); err != nil { 307 | 308 | return "", err 309 | 310 | } 311 | 312 | return buf.String(), nil 313 | } 314 | 315 | type OneofPayload struct { 316 | Name string 317 | SchemaObjectPara string 318 | FieldFuncPara string 319 | FieldFuncSecondFuncReturn string 320 | FieldFuncReturn string 321 | } 322 | 323 | func (m *jaalModule) OneofPayloadType(inputData pgs.Message, imports map[string]string, initFunctionsName map[string]bool) (string, error) { 324 | /* 325 | returns generated template(Payload) in for all oneOf type 326 | Primitive,Enum and object type are handled inside a oneof 327 | */ 328 | var oneOfArr []OneofPayload 329 | 330 | for _, oneof := range inputData.OneOfs() { 331 | 332 | for _, fields := range oneof.Fields() { 333 | //checks skip_payload field option 334 | if fieldSkip, err := m.GetFieldOptionPayload(fields); err != nil { 335 | return "", err 336 | } else if fieldSkip { 337 | continue 338 | } 339 | name := fields.Message().Name().UpperCamelCase().String() + "_" + fields.Name().UpperCamelCase().String() 340 | initFunctionsName["RegisterPayload"+name] = true 341 | schemaObjectPara := fields.Message().Name().LowerCamelCase().String() + fields.Name().UpperCamelCase().String() 342 | fieldFuncPara := fields.Name().LowerCamelCase().String() 343 | fieldFuncSecondFuncReturn := m.RPCFieldType(fields) 344 | if fieldFuncSecondFuncReturn[0] == '*' { 345 | fieldFuncSecondFuncReturn = fieldFuncSecondFuncReturn[1:len(fieldFuncSecondFuncReturn)] 346 | } 347 | goPkg := "" 348 | if fields.Type().IsEnum() { 349 | goPkg = m.GetGoPackageOfFiles(inputData.File(), fields.Type().Enum().File()) 350 | if goPkg != "" { 351 | goPkg += "." 352 | } 353 | } else if fields.Type().IsEmbed() { 354 | goPkg = m.GetGoPackageOfFiles(inputData.File(), fields.Type().Embed().File()) 355 | if goPkg != "" { 356 | goPkg = "*" + goPkg 357 | goPkg += "." 358 | } else { 359 | goPkg = "*" 360 | } 361 | } 362 | fieldFuncSecondFuncReturn = goPkg + fieldFuncSecondFuncReturn 363 | fieldFuncReturn := fields.Name().UpperCamelCase().String() 364 | if fieldFuncSecondFuncReturn == "*field_mask.FieldMask" { 365 | fieldFuncReturn = "gtypes.ModifyFieldMask(in." + fieldFuncReturn + ")" 366 | } else if strings.HasSuffix(fieldFuncSecondFuncReturn, "byte") { 367 | fieldFuncSecondFuncReturn = "*schemabuilder.Bytes" 368 | fieldFuncReturn = "&schemabuilder.Bytes{Value:in." + fieldFuncReturn + "}" 369 | 370 | } else { 371 | fieldFuncReturn = "in." + fieldFuncReturn 372 | } 373 | oneOfArr = append(oneOfArr, OneofPayload{Name: name, SchemaObjectPara: schemaObjectPara, FieldFuncPara: fieldFuncPara, FieldFuncReturn: fieldFuncReturn, FieldFuncSecondFuncReturn: fieldFuncSecondFuncReturn}) 374 | } 375 | } 376 | 377 | tmp := getOneofPayloadTemplate() 378 | buf := &bytes.Buffer{} 379 | 380 | if err := tmp.Execute(buf, oneOfArr); err != nil { 381 | 382 | return "", err 383 | 384 | } 385 | 386 | return buf.String(), nil 387 | } 388 | 389 | func (m *jaalModule) UnionStruct(inputData pgs.Message, imports map[string]string, PossibleReqObjects map[string]bool, initFunctionsName map[string]bool) (string, error) { 390 | //returns generated template(Union Struct) in for all one of type 391 | 392 | var unionObjects []UnionObject 393 | 394 | for _, oneof := range inputData.OneOfs() { 395 | 396 | unionName := "Union" 397 | msgName := oneof.Message().Name().UpperCamelCase().String() 398 | unionName += msgName 399 | unionName += oneof.Name().UpperCamelCase().String() 400 | 401 | var unionField []string 402 | 403 | for _, fields := range oneof.Fields() { 404 | 405 | unionField = append(unionField, "*"+fields.Message().Name().UpperCamelCase().String()+"_"+fields.Name().UpperCamelCase().String()) 406 | 407 | } 408 | 409 | unionObjects = append(unionObjects, UnionObject{UnionName: unionName, UnionFields: unionField}) 410 | 411 | } 412 | 413 | tmp := getUnionStructTemplate() 414 | buf := &bytes.Buffer{} 415 | 416 | if err := tmp.Execute(buf, unionObjects); err != nil { 417 | 418 | return "", err 419 | 420 | } 421 | 422 | return buf.String(), nil 423 | 424 | } 425 | 426 | func (m *jaalModule) GetMessageName(message pgs.Message) (string, error) { 427 | opt := message.Descriptor().GetOptions() // checks Name option 428 | if opt != nil { 429 | x, err := proto.GetExtension(opt, pbt.E_Name) 430 | if err != nil && err != proto.ErrMissingExtension { 431 | return "", err 432 | } 433 | if x != nil { 434 | return *x.(*string), nil 435 | } 436 | } 437 | return "", nil 438 | } 439 | 440 | func (m *jaalModule) InputType(inputData pgs.Message, imports map[string]string, PossibleReqObjects map[string]bool, initFunctionsName map[string]bool, typeCastMap map[string]string) (string, error) { 441 | // returns generated template(Input) in for a message type 442 | 443 | if skip, err := m.GetSkipOption(inputData); err != nil { 444 | // checks skip flag 445 | return "", err 446 | 447 | } else if skip { 448 | 449 | return "", nil 450 | } 451 | 452 | // handles embedded messages 453 | fullyQualifiedName := inputData.FullyQualifiedName() 454 | embeddedMessageParent := "" 455 | 456 | names := strings.Split(fullyQualifiedName, ".") 457 | 458 | if inputData.Parent().Name().String() == names[len(names)-2] { 459 | embeddedMessageParent = strings.Join(names[1:len(names)-1], "_") + "_" 460 | } 461 | 462 | msg := InputClass{Name: embeddedMessageParent + inputData.Name().UpperCamelCase().String()} 463 | 464 | newName, err := m.GetMessageName(inputData) 465 | if err != nil { 466 | return "", err 467 | 468 | } else if newName != "" { 469 | 470 | msg.InputObjName = newName + "Input" 471 | 472 | } else if PossibleReqObjects[inputData.Name().String()] { 473 | 474 | msg.InputObjName = m.InputAppend(inputData.Name().UpperCamelCase().String()) 475 | 476 | } else { 477 | 478 | msg.InputObjName = inputData.Name().UpperCamelCase().String() + "Input" 479 | 480 | } 481 | 482 | initFunctionsName["RegisterInput"+msg.Name] = true 483 | 484 | var maps []InputMap 485 | 486 | for _, oneof := range inputData.OneOfs() { 487 | 488 | unionName := "Union" 489 | msgName := oneof.Message().Name().UpperCamelCase().String() 490 | unionName += msgName 491 | unionName += oneof.Name().UpperCamelCase().String() 492 | 493 | for _, fields := range oneof.Fields() { 494 | //checks skip_input field option 495 | if fieldSkip, err := m.GetFieldOptionInput(fields); err != nil { 496 | return "", err 497 | } else if fieldSkip { 498 | continue 499 | } 500 | 501 | msg.Fields = append(msg.Fields, MsgFields{TargetName: oneof.Name().UpperCamelCase().String(), FieldName: oneof.Message().Name().LowerCamelCase().String() + fields.Name().UpperCamelCase().String(), FuncPara: "*" + fields.Message().Name().UpperCamelCase().String() + "_" + fields.Name().UpperCamelCase().String(), TargetVal: "source"}) 502 | 503 | } 504 | 505 | } 506 | 507 | for _, fields := range inputData.NonOneOfFields() { 508 | //m.Log(fields.Name(),fields.Type().IsEmbed()) 509 | 510 | //checks skip_input field option 511 | if fieldSkip, err := m.GetFieldOptionInput(fields); err != nil { 512 | return "", err 513 | } else if fieldSkip { 514 | continue 515 | } 516 | 517 | overrideFieldName, nameToBeOverridden, err := m.getFieldNameOption(fields) 518 | if err != nil { 519 | m.Log("In Input") 520 | return "", err 521 | } 522 | 523 | msgArg := "" 524 | tVal := "" 525 | flag := true 526 | flag2 := true 527 | flag3 := true 528 | fieldName := fields.Name().LowerCamelCase().String() 529 | targetName := fields.Name().UpperCamelCase().String() 530 | 531 | if overrideFieldName { 532 | fieldName = nameToBeOverridden 533 | } 534 | 535 | if fields.Type().IsRepeated() { 536 | 537 | msgArg += "[]" 538 | flag = false 539 | 540 | if !fields.Type().Element().IsEmbed() { 541 | 542 | flag3 = false 543 | 544 | } 545 | } 546 | 547 | if flag3 { 548 | 549 | msgArg += "*" 550 | 551 | } 552 | 553 | idOption, err := m.IdOption(fields) 554 | if err != nil { 555 | return "", err 556 | } 557 | 558 | if strings.ToLower(fields.Name().String()) == "id" || idOption { 559 | 560 | msgArg += "schemabuilder.ID" 561 | tVal += "source.Value" 562 | flag = false 563 | flag2 = false 564 | } else if fields.Type().IsRepeated() { 565 | 566 | msgArg = "[]" 567 | tObj := fields.Type().Element() 568 | 569 | if tObj.IsEmbed() { 570 | 571 | msgArg += "*" 572 | 573 | } 574 | 575 | if tObj.IsEmbed() { 576 | if tObj.Embed().File().Descriptor().Options != nil && tObj.Embed().File().Descriptor().Options.GoPackage != nil && inputData.Package().ProtoName().String() != tObj.Embed().Package().ProtoName().String() { 577 | 578 | msgArg += m.GetGoPackage(tObj.Embed().File()) 579 | msgArg += "." 580 | 581 | } else { 582 | if strings.Split(tObj.Embed().FullyQualifiedName(), ".")[len(strings.Split(tObj.Embed().FullyQualifiedName(), "."))-2] == tObj.Embed().Parent().Name().String() { 583 | names := strings.Split(fields.FullyQualifiedName(), ".") 584 | tembeddedMessageParent := strings.Join(names[1:len(names)-1], "_") + "_" 585 | msgArg += tembeddedMessageParent 586 | } 587 | } 588 | } 589 | 590 | ttype := m.fieldElementType(tObj) 591 | msgArg += ttype 592 | 593 | } else if fields.Type().IsMap() { 594 | // TODO : Repeated case not handled 595 | 596 | goPkg := "" 597 | if fields.Type().Element().IsEmbed() { 598 | goPkg = m.GetGoPackageOfFiles(inputData.File(), fields.Type().Element().Embed().File()) 599 | if goPkg != "" { 600 | goPkg += "." 601 | } 602 | } 603 | 604 | asterik := "" 605 | if fields.Type().Element().IsEmbed() { 606 | asterik = "*" 607 | } 608 | 609 | value := asterik + goPkg + m.fieldElementType(fields.Type().Element()) 610 | maps = append(maps, InputMap{FieldName: fieldName, TargetVal: "*source", TargetName: targetName, Key: m.fieldElementType(fields.Type().Key()), Value: value}) 611 | continue 612 | } else if fields.Descriptor().GetType().String() == "TYPE_MESSAGE" { 613 | 614 | if fields.Type().IsEmbed() { 615 | goPkg := m.GetGoPackageOfFiles(inputData.File(), fields.Type().Embed().File()) 616 | if goPkg != "" { 617 | msgArg += goPkg 618 | msgArg += "." 619 | } else { 620 | if strings.Split(fields.FullyQualifiedName(), ".")[len(strings.Split(fields.FullyQualifiedName(), "."))-2] == fields.Type().Embed().Parent().Name().String() { 621 | names := strings.Split(fields.FullyQualifiedName(), ".") 622 | tembeddedMessageParent := strings.Join(names[1:len(names)-1], "_") + "_" 623 | msgArg += tembeddedMessageParent 624 | } 625 | } 626 | } 627 | 628 | tmsg := strings.Split(fields.Descriptor().GetTypeName(), ".") 629 | msgArg += tmsg[len(tmsg)-1] 630 | 631 | flag = false 632 | 633 | } else if fields.Descriptor().GetType().String() == "TYPE_ENUM" { 634 | goPkg := m.GetGoPackageOfFiles(inputData.File(), fields.Type().Enum().File()) 635 | if goPkg != "" { 636 | goPkg += "." 637 | } 638 | flag = false 639 | tmsg := strings.Split(fields.Descriptor().GetTypeName(), ".") 640 | msgArg = goPkg + tmsg[len(tmsg)-1] 641 | 642 | } else { 643 | 644 | tmsg := strings.Split(fields.Descriptor().GetType().String(), "_") 645 | msgArg = m.scalarMap(tmsg[len(tmsg)-1]) 646 | flag = false 647 | } 648 | 649 | if flag { 650 | 651 | tVal += "*" 652 | 653 | } 654 | 655 | if flag2 { 656 | 657 | tVal += "source" 658 | 659 | } 660 | if strings.HasSuffix(msgArg, "timestamp.Timestamp") { 661 | // handles special case of timestamp 662 | //todo repeated case is not handled 663 | msgArg = msgArg[:len(msgArg)-19] 664 | msgArg += "schemabuilder.Timestamp" 665 | 666 | tVal = "(*timestamp.Timestamp)(" + tVal + ")" 667 | } else if strings.HasSuffix(msgArg, "duration.Duration") { 668 | if fields.Type().IsRepeated() { 669 | msgArg = msgArg[:len(msgArg)-17] 670 | msgArg += "schemabuilder.Duration" 671 | msg.Durations = append(msg.Durations, Duration{FieldName: fields.Name().LowerCamelCase().String(), Name: fields.Name().UpperCamelCase().String()}) 672 | continue 673 | } else { 674 | msgArg = msgArg[:len(msgArg)-17] 675 | msgArg += "schemabuilder.Duration" 676 | tVal = "(*duration.Duration)(" + tVal + ")" 677 | } 678 | 679 | } else if strings.HasSuffix(msgArg, "field_mask.FieldMask") { 680 | tVal = "gtypes.ModifyFieldMask(" + tVal + ")" 681 | } else if strings.HasSuffix(msgArg, "byte") { 682 | msgArg = "*schemabuilder.Bytes" 683 | tVal = "source.Value" 684 | } else if msgArg == "[]schemabuilder.ID" { 685 | //handles repeated ids 686 | msg.Ids = append(msg.Ids, Id{FieldName: fields.Name().LowerCamelCase().String(), Name: fields.Name().UpperCamelCase().String()}) 687 | continue 688 | } 689 | msg.Fields = append(msg.Fields, MsgFields{TargetName: targetName, FieldName: fieldName, FuncPara: msgArg, TargetVal: tVal}) 690 | 691 | } 692 | // adds all maps 693 | msg.Maps = maps 694 | 695 | buf := &bytes.Buffer{} 696 | tmp := getInputTemplate() 697 | 698 | msg.Type = msg.Name 699 | tbuf := &bytes.Buffer{} 700 | if err := tmp.Execute(tbuf, msg); err != nil { 701 | return "", err 702 | } else { 703 | buf.WriteString(tbuf.String()) 704 | } 705 | //if message type is set 706 | if ok, val, err := m.GetMessageTypeOption(inputData); err != nil { 707 | return "", err 708 | } else if ok { 709 | typeCastMap[val] = msg.Name 710 | msg.Type = val 711 | msg.Name = msg.Type 712 | msg.InputObjName = val + "Input" 713 | tbuf := &bytes.Buffer{} 714 | if err := tmp.Execute(tbuf, msg); err != nil { 715 | return "", err 716 | } else { 717 | initFunctionsName["RegisterInput"+msg.Name] = true 718 | buf.WriteString(tbuf.String()) 719 | } 720 | } 721 | return buf.String(), nil 722 | } 723 | 724 | func (m *jaalModule) PayloadType(payloadData pgs.Message, imports map[string]string, initFunctionsName map[string]bool, typeCastMap map[string]string) (string, error) { 725 | // returns generated template(Payload) in for a message type 726 | 727 | if skip, err := m.GetSkipOption(payloadData); err != nil { 728 | // checks skip flag 729 | return "", err 730 | 731 | } else if skip { 732 | 733 | return "", nil 734 | } 735 | 736 | fullyQualifiedName := payloadData.FullyQualifiedName() 737 | names := strings.Split(fullyQualifiedName, ".") 738 | embeddedMessageParent := "" 739 | if payloadData.Parent().Name().String() == names[len(names)-2] { 740 | 741 | embeddedMessageParent = strings.Join(names[1:len(names)-1], "_") + "_" 742 | } 743 | msg := Payload{Name: embeddedMessageParent + payloadData.Name().UpperCamelCase().String()} 744 | newName, err := m.GetMessageName(payloadData) 745 | if err != nil { 746 | return "", err 747 | } else if newName != "" { 748 | msg.PayloadObjName = newName 749 | } else { 750 | msg.PayloadObjName = payloadData.Name().UpperCamelCase().String() 751 | } 752 | initFunctionsName["RegisterPayload"+msg.Name] = true 753 | var maps []PayloadMap 754 | for _, oneof := range payloadData.OneOfs() { 755 | 756 | var oneofFields []OneOfFields 757 | 758 | for _, fields := range oneof.Fields() { 759 | //checks skip_payload field option 760 | if fieldSkip, err := m.GetFieldOptionPayload(fields); err != nil { 761 | return "", err 762 | } else if fieldSkip { 763 | continue 764 | } 765 | caseName := fields.Message().Name().UpperCamelCase().String() + "_" + fields.Name().UpperCamelCase().String() 766 | returnType := "&Union" + oneof.Message().Name().UpperCamelCase().String() + oneof.Name().UpperCamelCase().String() 767 | oneofFields = append(oneofFields, OneOfFields{CaseName: caseName, ReturnType: returnType}) 768 | 769 | } 770 | 771 | funcpara := "*Union" + oneof.Message().Name().UpperCamelCase().String() + oneof.Name().UpperCamelCase().String() 772 | fieldName := oneof.Name().LowerCamelCase().String() 773 | switchName := oneof.Name().UpperCamelCase().String() 774 | msg.UnionObjects = append(msg.UnionObjects, UnionObjectPayload{FieldName: fieldName, SwitchName: switchName, FuncReturn: funcpara, Fields: oneofFields}) 775 | 776 | } 777 | for _, fields := range payloadData.NonOneOfFields() { 778 | //checks skip_payload field option 779 | if fieldSkip, err := m.GetFieldOptionPayload(fields); err != nil { 780 | return "", err 781 | } else if fieldSkip { 782 | continue 783 | } 784 | 785 | overrideFieldName, nameToBeOverridden, err := m.getFieldNameOption(fields) 786 | if err != nil { 787 | return "", err 788 | } 789 | 790 | msgArg := "" 791 | tVal := "" 792 | fieldName := fields.Name().LowerCamelCase().String() 793 | if overrideFieldName { 794 | fieldName = nameToBeOverridden 795 | } 796 | 797 | if fields.Type().IsRepeated() { 798 | 799 | msgArg += "[]" 800 | 801 | } 802 | 803 | idOption, err := m.IdOption(fields) 804 | if err != nil { 805 | return "", err 806 | } 807 | 808 | if strings.ToLower(fields.Name().String()) == "id" || idOption { 809 | 810 | msgArg += "schemabuilder.ID" 811 | tVal += "schemabuilder." 812 | tVal += strings.ToUpper(fields.Name().String()) 813 | tVal += "{Value: in." 814 | tVal += fields.Name().UpperCamelCase().String() 815 | tVal += "}" 816 | 817 | } else if fields.Type().IsRepeated() { 818 | 819 | msgArg = "[]" 820 | tObj := fields.Type().Element() 821 | 822 | if tObj.IsEmbed() { 823 | 824 | msgArg += "*" 825 | 826 | } 827 | 828 | if tObj.IsEmbed() { 829 | 830 | if tObj.Embed().File().Descriptor().Options != nil && tObj.Embed().File().Descriptor().Options.GoPackage != nil && payloadData.Package().ProtoName().String() != tObj.Embed().Package().ProtoName().String() { 831 | 832 | msgArg += m.GetGoPackage(tObj.Embed().File()) 833 | msgArg += "." 834 | } else { 835 | if strings.Split(tObj.Embed().FullyQualifiedName(), ".")[len(strings.Split(tObj.Embed().FullyQualifiedName(), "."))-2] == tObj.Embed().Parent().Name().String() { 836 | names := strings.Split(fields.FullyQualifiedName(), ".") 837 | tembeddedMessageParent := strings.Join(names[1:len(names)-1], "_") + "_" 838 | msgArg += tembeddedMessageParent 839 | } 840 | } 841 | 842 | } 843 | 844 | ttype := m.fieldElementType(tObj) 845 | msgArg += ttype 846 | tVal += "in." + fields.Name().UpperCamelCase().String() 847 | 848 | } else if fields.Type().IsMap() { 849 | //TODO : Repeated case not handled 850 | 851 | tVal += "in." 852 | tVal += fields.Name().UpperCamelCase().String() 853 | maps = append(maps, PayloadMap{FieldName: fieldName, TargetVal: tVal}) 854 | continue 855 | 856 | } else if fields.Descriptor().GetType().String() == "TYPE_MESSAGE" { 857 | 858 | msgArg += "*" 859 | 860 | if fields.Type().IsEmbed() { 861 | goPkg := m.GetGoPackageOfFiles(payloadData.File(), fields.Type().Embed().File()) 862 | if goPkg != "" { 863 | msgArg += goPkg 864 | msgArg += "." 865 | } else { 866 | if strings.Split(fields.FullyQualifiedName(), ".")[len(strings.Split(fields.FullyQualifiedName(), "."))-2] == fields.Type().Embed().Parent().Name().String() { 867 | names := strings.Split(fields.FullyQualifiedName(), ".") 868 | tembeddedMessageParent := strings.Join(names[1:len(names)-1], "_") + "_" 869 | msgArg += tembeddedMessageParent 870 | } 871 | } 872 | } 873 | 874 | tTypeArr := strings.Split(fields.Descriptor().GetTypeName(), ".") 875 | msgArg += tTypeArr[len(tTypeArr)-1] 876 | 877 | tVal += "in." 878 | tVal += fields.Name().UpperCamelCase().String() 879 | 880 | } else if fields.Descriptor().GetType().String() == "TYPE_ENUM" { 881 | goPkg := m.GetGoPackageOfFiles(payloadData.File(), fields.Type().Enum().File()) 882 | if goPkg != "" { 883 | goPkg += "." 884 | } 885 | 886 | tTypeArr := strings.Split(fields.Descriptor().GetTypeName(), ".") 887 | msgArg = goPkg + tTypeArr[len(tTypeArr)-1] 888 | tVal += "in." 889 | tVal += fields.Name().UpperCamelCase().String() 890 | 891 | } else { 892 | goPkg := m.GetGoPackageOfFiles(payloadData.File(), fields.File()) 893 | if goPkg != "" { 894 | goPkg += "." 895 | } 896 | tTypeArr := strings.Split(fields.Descriptor().GetType().String(), "_") 897 | msgArg = goPkg + m.scalarMap(tTypeArr[len(tTypeArr)-1]) 898 | tVal += "in." 899 | tVal += fields.Name().UpperCamelCase().String() 900 | 901 | } 902 | if strings.HasSuffix(msgArg, "timestamp.Timestamp") { 903 | // handles special case of timestamp 904 | //todo repeated case is not handled 905 | msgArg = msgArg[:len(msgArg)-19] 906 | msgArg += "schemabuilder.Timestamp" 907 | 908 | tVal = "(*schemabuilder.Timestamp)(" + tVal + ")" 909 | } else if strings.HasSuffix(msgArg, "duration.Duration") { 910 | if fields.Type().IsRepeated() { 911 | msgArg = msgArg[:len(msgArg)-17] 912 | msgArg += "schemabuilder.Duration" 913 | msg.Durations = append(msg.Durations, Duration{FieldName: fields.Name().LowerCamelCase().String(), Name: fields.Name().UpperCamelCase().String()}) 914 | continue 915 | } else { 916 | msgArg = msgArg[:len(msgArg)-17] 917 | msgArg += "schemabuilder.Duration" 918 | tVal = "(*schemabuilder.Duration)(" + tVal + ")" 919 | } 920 | } else if strings.HasSuffix(msgArg, "field_mask.FieldMask") { 921 | tVal = "gtypes.ModifyFieldMask(" + tVal + ")" 922 | } else if strings.HasSuffix(msgArg, "byte") { 923 | msgArg = "*" + "schemabuilder.Bytes" 924 | tVal = "&schemabuilder.Bytes{Value:in." + fields.Name().UpperCamelCase().String() + "}" 925 | } else if msgArg == "[]schemabuilder.ID" { 926 | msg.Ids = append(msg.Ids, Id{FieldName: fields.Name().LowerCamelCase().String(), Name: fields.Name().UpperCamelCase().String()}) 927 | continue 928 | } 929 | 930 | msg.Fields = append(msg.Fields, PayloadFields{FieldName: fieldName, FuncPara: msgArg, TargetVal: tVal}) 931 | 932 | } 933 | 934 | // adds all maps 935 | msg.Maps = maps 936 | 937 | buf := &bytes.Buffer{} 938 | tmp := getPayloadTemplate() 939 | 940 | msg.Type = msg.Name 941 | tbuf := &bytes.Buffer{} 942 | if err := tmp.Execute(tbuf, msg); err != nil { 943 | return "", err 944 | } else { 945 | buf.WriteString(tbuf.String()) 946 | } 947 | //if message type is set 948 | if ok, val, err := m.GetMessageTypeOption(payloadData); err != nil { 949 | return "", err 950 | } else if ok { 951 | typeCastMap[val] = msg.Name 952 | msg.Type = val 953 | msg.Name = msg.Type 954 | msg.PayloadObjName = val 955 | tbuf := &bytes.Buffer{} 956 | if err := tmp.Execute(tbuf, msg); err != nil { 957 | return "", err 958 | } else { 959 | initFunctionsName["RegisterPayload"+msg.Name] = true 960 | buf.WriteString(tbuf.String()) 961 | } 962 | } 963 | return buf.String(), nil 964 | } 965 | 966 | type MapData struct { 967 | Name string 968 | Key string 969 | Value string 970 | NewVarName string 971 | } 972 | 973 | type Fields struct { 974 | Name string 975 | Type string 976 | } 977 | 978 | type Query struct { 979 | FieldName string 980 | InType []Fields 981 | InputName string 982 | ReturnType []Fields 983 | FirstReturnArgType string 984 | ReturnFunc string 985 | MapsData []MapData 986 | Oneofs []OneOfMutation 987 | Durations []Duration 988 | Ids []Id 989 | } 990 | 991 | type Mutation struct { 992 | FieldName string 993 | InputType string 994 | FirstReturnArgType string 995 | RequestType string 996 | RequestFields []string 997 | ResponseType string 998 | ReturnType string 999 | OneOfs []OneOfMutation 1000 | } 1001 | 1002 | type OneOfMutation struct { 1003 | Name string 1004 | Fields []Fields 1005 | } 1006 | type Service struct { 1007 | Name string 1008 | Queries []Query 1009 | Mutations []Mutation 1010 | } 1011 | 1012 | func (m *jaalModule) InputAppend(str string) string { 1013 | // returns input object name for input type 1014 | 1015 | if strings.HasSuffix(strings.ToLower(str), "req") { 1016 | 1017 | str = str[:len(str)-3] 1018 | str += "Input" 1019 | return str 1020 | 1021 | } else if strings.HasSuffix(strings.ToLower(str), "request") { 1022 | 1023 | str = str[:len(str)-7] 1024 | str += "Input" 1025 | return str 1026 | 1027 | } else { 1028 | 1029 | return str + "Input" 1030 | 1031 | } 1032 | } 1033 | 1034 | func (m *jaalModule) getFieldNameOption(field pgs.Field) (bool, string, error) { 1035 | opt := field.Descriptor().GetOptions() 1036 | if opt == nil { 1037 | return false, "", nil 1038 | } 1039 | 1040 | x, err := proto.GetExtension(opt, pbt.E_FieldName) 1041 | if err != nil { 1042 | if err == proto.ErrMissingExtension { 1043 | return false, "", nil 1044 | } 1045 | return false, "", err 1046 | } 1047 | 1048 | option := *x.(*string) 1049 | return true, option, nil 1050 | } 1051 | 1052 | func (m *jaalModule) GetFieldOptionInput(field pgs.Field) (bool, error) { 1053 | //returns input_skip option for a message field 1054 | 1055 | opt := field.Descriptor().GetOptions() 1056 | if opt == nil { 1057 | return false, nil 1058 | } 1059 | 1060 | x, err := proto.GetExtension(opt, pbt.E_InputSkip) 1061 | if err != nil { 1062 | if err == proto.ErrMissingExtension { 1063 | return false, nil 1064 | } 1065 | 1066 | return false, err 1067 | } 1068 | 1069 | option := *x.(*bool) 1070 | return option, nil 1071 | } 1072 | 1073 | func (m *jaalModule) GetFieldOptionPayload(field pgs.Field) (bool, error) { 1074 | //returns payload_skip option for a message field 1075 | 1076 | opt := field.Descriptor().GetOptions() 1077 | x, err := proto.GetExtension(opt, pbt.E_PayloadSkip) 1078 | 1079 | if opt == nil { 1080 | 1081 | return false, nil 1082 | 1083 | } 1084 | 1085 | if err != nil { 1086 | 1087 | if err == proto.ErrMissingExtension { 1088 | 1089 | return false, nil 1090 | 1091 | } 1092 | 1093 | return false, err 1094 | 1095 | } 1096 | 1097 | option := *x.(*bool) 1098 | 1099 | return option, nil 1100 | } 1101 | 1102 | func (m *jaalModule) GetOption(rpc pgs.Method) (bool, pbt.MethodOptions, error) { 1103 | //returns method option for a rpc method (Used to get query and mutation data) 1104 | 1105 | opt := rpc.Descriptor().GetOptions() 1106 | x, err := proto.GetExtension(opt, pbt.E_Schema) 1107 | 1108 | if opt == nil { 1109 | 1110 | return false, pbt.MethodOptions{}, nil 1111 | 1112 | } 1113 | 1114 | if err != nil { 1115 | 1116 | if err == proto.ErrMissingExtension { 1117 | 1118 | return false, pbt.MethodOptions{}, nil 1119 | 1120 | } 1121 | 1122 | return false, pbt.MethodOptions{}, err 1123 | 1124 | } 1125 | 1126 | option := *x.(*pbt.MethodOptions) 1127 | 1128 | return true, option, nil 1129 | } 1130 | 1131 | func (m *jaalModule) ServiceInput(service pgs.Service) (string, error) { 1132 | // returns generated template(Service) in for a service type 1133 | 1134 | var varQuery []Query 1135 | var varMutation []Mutation 1136 | 1137 | for _, rpc := range service.Methods() { 1138 | 1139 | flag, option, err := m.GetOption(rpc) 1140 | 1141 | if err != nil { 1142 | 1143 | m.Log("Error", err) 1144 | os.Exit(0) 1145 | 1146 | } 1147 | 1148 | if flag == false { 1149 | continue 1150 | 1151 | } 1152 | //todo case for no query and mutation 1153 | if option.GetMutation() == "" { 1154 | 1155 | fieldName := option.GetQuery() 1156 | firstReturnArgType := "" 1157 | if rpc.Output().Package().ProtoName().String() != service.Package().ProtoName().String() { 1158 | firstReturnArgType += m.GetGoPackage(rpc.Output().File()) 1159 | firstReturnArgType += "." 1160 | } 1161 | firstReturnArgType += rpc.Output().Name().UpperCamelCase().String() 1162 | returnFunc := rpc.Name().UpperCamelCase().String() 1163 | var inType []Fields 1164 | var returnType []Fields 1165 | var mapsData []MapData 1166 | var oneOfs []OneOfMutation 1167 | var duration []Duration 1168 | var rIds []Id 1169 | for _, oneOf := range rpc.Input().OneOfs() { 1170 | var fields []Fields 1171 | for _, field := range oneOf.Fields() { 1172 | //checks skip_input field option 1173 | if fieldSkip, err := m.GetFieldOptionInput(field); err != nil { 1174 | return "", err 1175 | } else if fieldSkip { 1176 | continue 1177 | } 1178 | goPkg := m.GetGoPackageOfFiles(service.File(), field.File()) 1179 | if goPkg != "" { 1180 | goPkg += "." 1181 | } 1182 | name := field.Name().UpperCamelCase().String() 1183 | ttype := goPkg + rpc.Input().Name().UpperCamelCase().String() + "_" + name 1184 | ttype = "*" + ttype 1185 | fields = append(fields, Fields{Name: name, Type: ttype}) 1186 | } 1187 | oneOfs = append(oneOfs, OneOfMutation{Name: oneOf.Name().UpperCamelCase().String(), Fields: fields}) 1188 | } 1189 | for _, field := range rpc.Input().Fields() { 1190 | //checks skip_input field option 1191 | if fieldSkip, err := m.GetFieldOptionInput(field); err != nil { 1192 | return "", err 1193 | } else if fieldSkip { 1194 | continue 1195 | } 1196 | name := field.Name().UpperCamelCase().String() 1197 | tType := "" 1198 | 1199 | idOption, err := m.IdOption(field) 1200 | if err != nil { 1201 | return "", err 1202 | } 1203 | 1204 | if strings.ToLower(name) == "id" || idOption { 1205 | 1206 | tType = "schemabuilder.ID" 1207 | if field.Type().IsRepeated() { 1208 | tType = "[]" + tType 1209 | rIds = append(rIds, Id{Name: field.Name().UpperCamelCase().String()}) 1210 | } 1211 | } else if field.Type().IsRepeated() { 1212 | 1213 | tType = "[]" 1214 | tObj := field.Type().Element() 1215 | 1216 | if tObj.IsEmbed() { 1217 | 1218 | tType += "*" 1219 | 1220 | } 1221 | 1222 | if tObj.IsEmbed() && tObj.Embed().File().Descriptor().Options != nil && tObj.Embed().File().Descriptor().Options.GoPackage != nil { 1223 | 1224 | if service.Package().ProtoName().String() != tObj.Embed().Package().ProtoName().String() { 1225 | 1226 | tType += m.GetGoPackage(tObj.Embed().File()) 1227 | tType += "." 1228 | 1229 | } else { 1230 | if strings.Split(tObj.Embed().FullyQualifiedName(), ".")[len(strings.Split(tObj.Embed().FullyQualifiedName(), "."))-2] == tObj.Embed().Parent().Name().String() { 1231 | tType += (tObj.Embed().Parent().Name().String() + "_") 1232 | } 1233 | } 1234 | 1235 | } 1236 | 1237 | tType += m.fieldElementType(tObj) 1238 | if strings.HasSuffix(tType, "duration.Duration") { 1239 | tType = tType[:len(tType)-17] 1240 | tType += "schemabuilder.Duration" 1241 | duration = append(duration, Duration{Name: field.Name().UpperCamelCase().String()}) 1242 | } 1243 | 1244 | } else if field.Type().IsMap() { 1245 | tType = "*schemabuilder.Map" 1246 | goPkg := "" 1247 | if field.Type().Element().IsEmbed() { 1248 | goPkg = m.GetGoPackageOfFiles(service.File(), field.Type().Element().Embed().File()) 1249 | if goPkg != "" { 1250 | goPkg += "." 1251 | } 1252 | } 1253 | 1254 | asterik := "" 1255 | if field.Type().Element().IsEmbed() { 1256 | asterik = "*" 1257 | } 1258 | 1259 | value := asterik + goPkg + m.fieldElementType(field.Type().Element()) 1260 | mapsData = append(mapsData, MapData{Name: name, NewVarName: field.Name().LowerCamelCase().String(), Key: m.fieldElementType(field.Type().Key()), Value: value}) 1261 | //returnType = "map returns" 1262 | } else if field.Type().IsEmbed() && field.Type().Embed().File().Descriptor().Options != nil && field.Type().Embed().File().Descriptor().Options.GoPackage != nil { 1263 | goPkg := m.GetGoPackageOfFiles(service.File(), field.Type().Embed().File()) 1264 | if goPkg != "" { 1265 | tType += goPkg 1266 | tType += "." 1267 | } else { 1268 | if strings.Split(field.FullyQualifiedName(), ".")[len(strings.Split(field.FullyQualifiedName(), "."))-2] == field.Type().Embed().Parent().Name().String() { 1269 | 1270 | tType += field.Type().Embed().Parent().Name().String() + "_" 1271 | } 1272 | } 1273 | 1274 | tType = "*" + tType 1275 | 1276 | funcRType := m.RPCFieldType(field) 1277 | 1278 | if funcRType[0] == '*' { 1279 | 1280 | funcRType = funcRType[1:len(funcRType)] 1281 | 1282 | } 1283 | 1284 | tType += funcRType 1285 | 1286 | } else { 1287 | if field.Type().IsEmbed() { 1288 | 1289 | tType += "*" 1290 | 1291 | } else if field.Type().IsEnum() { 1292 | goPkg := m.GetGoPackageOfFiles(service.File(), field.Type().Enum().File()) 1293 | if goPkg != "" { 1294 | tType += goPkg 1295 | tType += "." 1296 | } 1297 | 1298 | } 1299 | 1300 | funcRType := m.RPCFieldType(field) 1301 | 1302 | if funcRType[0] == '*' { 1303 | 1304 | funcRType = funcRType[1:len(funcRType)] 1305 | 1306 | } 1307 | 1308 | tType += funcRType 1309 | } 1310 | if field.InOneOf() { 1311 | tType = "*" 1312 | if field.Package().ProtoName().String() != service.Package().ProtoName().String() { 1313 | tType += m.GetGoPackage(field.File()) 1314 | tType += "." 1315 | } 1316 | tType += rpc.Input().Name().UpperCamelCase().String() 1317 | tType += "_" 1318 | tType += field.Name().UpperCamelCase().String() 1319 | } else if strings.ToLower(name) == "id" { 1320 | if tType != "[]schemabuilder.ID" { 1321 | returnType = append(returnType, Fields{Name: name, Type: "args.Id.Value"}) 1322 | } 1323 | } else if tType == "*timestamp.Timestamp" { 1324 | tType = "*schemabuilder.Timestamp" 1325 | returnType = append(returnType, Fields{Name: name, Type: "(*timestamp.Timestamp)" + "(args." + name + ")"}) 1326 | } else if tType == "*duration.Duration" { 1327 | tType = "*schemabuilder.Duration" 1328 | returnType = append(returnType, Fields{Name: name, Type: "(*duration.Duration)" + "(args." + name + ")"}) 1329 | } else if tType == "*field_mask.FieldMask" { 1330 | returnType = append(returnType, Fields{Name: name, Type: "gtypes.ModifyFieldMask" + "(args." + name + ")"}) 1331 | } else if field.Type().IsMap() { 1332 | returnType = append(returnType, Fields{Name: name, Type: field.Name().LowerCamelCase().String() + "Map"}) 1333 | } else if strings.HasSuffix(tType, "byte") { 1334 | tType = "*" + "schemabuilder.Bytes" 1335 | returnType = append(returnType, Fields{Name: name, Type: "args." + name + ".Value"}) 1336 | } else if !strings.HasSuffix(tType, "schemabuilder.Duration") { 1337 | returnType = append(returnType, Fields{Name: name, Type: "args." + name}) 1338 | } 1339 | inType = append(inType, Fields{Name: name, Type: tType}) 1340 | } 1341 | inputName := "" 1342 | if rpc.Input().Package().ProtoName().String() != service.Package().ProtoName().String() { 1343 | inputName += m.GetGoPackage(rpc.Input().File()) 1344 | inputName += "." 1345 | } 1346 | inputName += rpc.Input().Name().UpperCamelCase().String() 1347 | varQuery = append(varQuery, Query{Ids: rIds, Durations: duration, Oneofs: oneOfs, InputName: inputName, MapsData: mapsData, ReturnType: returnType, FieldName: fieldName, InType: inType, FirstReturnArgType: firstReturnArgType, ReturnFunc: returnFunc}) 1348 | 1349 | } else if option.GetQuery() == "" { 1350 | 1351 | fieldName := option.GetMutation() 1352 | inputType := "*" + rpc.Name().UpperCamelCase().String() + "Input" 1353 | firstReturnArgType := rpc.Name().UpperCamelCase().String() + "Payload" 1354 | returnType := rpc.Name().UpperCamelCase().String() + "Payload" 1355 | goPkg := m.GetGoPackageOfFiles(service.File(), rpc.Input().File()) 1356 | if goPkg != "" { 1357 | goPkg += "." 1358 | } 1359 | 1360 | requestType := "&" + goPkg + rpc.Input().Name().UpperCamelCase().String() 1361 | var requestFields []string 1362 | var oneOfMutation []OneOfMutation 1363 | for _, oneOf := range rpc.Input().OneOfs() { 1364 | var fields []Fields 1365 | for _, field := range oneOf.Fields() { 1366 | goPkg := m.GetGoPackageOfFiles(service.File(), field.File()) 1367 | if goPkg != "" { 1368 | goPkg += "." 1369 | } 1370 | name := field.Name().UpperCamelCase().String() 1371 | ttype := goPkg + rpc.Input().Name().UpperCamelCase().String() + "_" + name 1372 | ttype = "*" + ttype 1373 | fields = append(fields, Fields{Name: name, Type: ttype}) 1374 | } 1375 | oneOfMutation = append(oneOfMutation, OneOfMutation{Name: oneOf.Name().UpperCamelCase().String(), Fields: fields}) 1376 | } 1377 | for _, fields := range rpc.Input().NonOneOfFields() { 1378 | 1379 | requestFields = append(requestFields, fields.Name().UpperCamelCase().String()) 1380 | 1381 | } 1382 | 1383 | responseType := rpc.Name().UpperCamelCase().String() 1384 | varMutation = append(varMutation, Mutation{OneOfs: oneOfMutation, FieldName: fieldName, InputType: inputType, FirstReturnArgType: firstReturnArgType, RequestType: requestType, RequestFields: requestFields, ResponseType: responseType, ReturnType: returnType}) 1385 | 1386 | } 1387 | } 1388 | 1389 | name := service.Name().UpperCamelCase().String() 1390 | varService := Service{Name: name, Queries: varQuery, Mutations: varMutation} 1391 | tmp := getServiceTemplate() 1392 | buf := &bytes.Buffer{} 1393 | 1394 | if err := tmp.Execute(buf, varService); err != nil { 1395 | 1396 | return "", err 1397 | 1398 | } 1399 | 1400 | return buf.String(), nil 1401 | 1402 | } 1403 | 1404 | type InputField struct { 1405 | Name string 1406 | Type string 1407 | } 1408 | 1409 | type InputServiceStruct struct { 1410 | RpcName string 1411 | InputFields []InputField 1412 | } 1413 | 1414 | func (m *jaalModule) RPCFieldType(field pgs.Field) string { 1415 | //returns type of a rpc field(pgs.Field type) 1416 | 1417 | switch field.Descriptor().GetType().String() { 1418 | 1419 | case "TYPE_MESSAGE": 1420 | 1421 | if field.Type().IsRepeated() { 1422 | 1423 | tTypeArr := strings.Split(*field.Descriptor().TypeName, ".") 1424 | typeName := tTypeArr[len(tTypeArr)-1] 1425 | 1426 | return "*" + typeName 1427 | 1428 | } else if field.Type().IsMap() { 1429 | 1430 | return "*[" + m.fieldElementType(field.Type().Key()) + "]" + m.fieldElementType(field.Type().Element()) 1431 | 1432 | } 1433 | 1434 | obj := field.Type().Embed() 1435 | 1436 | return "*" + obj.Name().String() 1437 | 1438 | case "TYPE_ENUM": 1439 | 1440 | if field.Type().IsRepeated() { 1441 | 1442 | tTypeArr := strings.Split(*field.Descriptor().TypeName, ".") 1443 | typeName := tTypeArr[len(tTypeArr)-1] 1444 | 1445 | return typeName 1446 | 1447 | } 1448 | 1449 | enum := field.Type().Enum() 1450 | 1451 | return enum.Name().String() 1452 | 1453 | default: 1454 | 1455 | tTypeArr := strings.Split(field.Descriptor().GetType().String(), "_") 1456 | scalarType := tTypeArr[len(tTypeArr)-1] 1457 | 1458 | return m.scalarMap(scalarType) 1459 | 1460 | } 1461 | } 1462 | 1463 | func (m *jaalModule) ServiceStructInput(service pgs.Service) (string, error) { 1464 | //returns template(Service input struct) for a service 1465 | 1466 | var inputServiceStruct []InputServiceStruct 1467 | 1468 | for _, rpc := range service.Methods() { 1469 | 1470 | flag, option, err := m.GetOption(rpc) 1471 | 1472 | if err != nil { 1473 | 1474 | m.Log("Error", err) 1475 | os.Exit(0) 1476 | 1477 | } 1478 | 1479 | if flag == false { 1480 | 1481 | continue 1482 | 1483 | } 1484 | 1485 | if option.GetMutation() == "" { 1486 | 1487 | continue 1488 | 1489 | } 1490 | 1491 | tInputServiceSTruct := InputServiceStruct{RpcName: rpc.Name().UpperCamelCase().String()} 1492 | for _, ipField := range rpc.Input().Fields() { 1493 | 1494 | name := ipField.Name().UpperCamelCase().String() 1495 | ttype := m.RPCFieldType(ipField) 1496 | 1497 | if ipField.Type().IsRepeated() { 1498 | 1499 | ttype = "[]" 1500 | tObj := ipField.Type().Element() 1501 | 1502 | if tObj.IsEmbed() { 1503 | 1504 | ttype += "*" 1505 | 1506 | } 1507 | 1508 | if tObj.IsEmbed() && tObj.Embed().File().Descriptor().Options != nil && tObj.Embed().File().Descriptor().Options.GoPackage != nil { 1509 | 1510 | if service.Package().ProtoName().String() != tObj.Embed().Package().ProtoName().String() { 1511 | 1512 | ttype += m.GetGoPackage(tObj.Embed().File()) 1513 | ttype += "." 1514 | } else { 1515 | if strings.Split(tObj.Embed().FullyQualifiedName(), ".")[len(strings.Split(tObj.Embed().FullyQualifiedName(), "."))-2] == tObj.Embed().Parent().Name().String() { 1516 | ttype += (tObj.Embed().Parent().Name().String() + "_") 1517 | } 1518 | } 1519 | } 1520 | 1521 | ttype += m.fieldElementType(tObj) 1522 | } else if ipField.Type().IsMap() { 1523 | goPkg := "" 1524 | 1525 | if ipField.Type().Element().IsEmbed() { 1526 | goPkg = m.GetGoPackageOfFiles(service.File(), ipField.Type().Element().Embed().File()) 1527 | if goPkg != "" { 1528 | goPkg += "." 1529 | } 1530 | } 1531 | 1532 | asterik := "" 1533 | if ipField.Type().Element().IsEmbed() { 1534 | asterik = "*" 1535 | } 1536 | 1537 | value := asterik + goPkg + m.fieldElementType(ipField.Type().Element()) 1538 | ttype = "map[" + m.fieldElementType(ipField.Type().Key()) + "]" + value 1539 | } else { 1540 | 1541 | goPkg := "" 1542 | 1543 | if ipField.Type().IsEmbed() { 1544 | goPkg = m.GetGoPackageOfFiles(service.File(), ipField.Type().Embed().File()) 1545 | if goPkg != "" { 1546 | goPkg += "." 1547 | } else { 1548 | if strings.Split(ipField.FullyQualifiedName(), ".")[len(strings.Split(ipField.FullyQualifiedName(), "."))-2] == ipField.Type().Embed().Parent().Name().String() { 1549 | goPkg += ipField.Type().Embed().Parent().Name().String() + "_" 1550 | } 1551 | } 1552 | } else if ipField.Type().IsEnum() { 1553 | goPkg = m.GetGoPackageOfFiles(service.File(), ipField.Type().Enum().File()) 1554 | if goPkg != "" { 1555 | goPkg += "." 1556 | } 1557 | } 1558 | 1559 | if ttype[0] == '*' { 1560 | 1561 | ttype = "*" + goPkg + ttype[1:len(ttype)] 1562 | 1563 | } else { 1564 | 1565 | ttype = goPkg + ttype 1566 | 1567 | } 1568 | } 1569 | if ipField.InOneOf() { 1570 | // handles one of fields 1571 | goPkg := m.GetGoPackageOfFiles(service.File(), ipField.OneOf().File()) 1572 | if goPkg != "" { 1573 | goPkg += "." 1574 | } 1575 | ttype = goPkg + rpc.Input().Name().UpperCamelCase().String() + "_" + name 1576 | ttype = "*" + ttype 1577 | } 1578 | tInputServiceSTruct.InputFields = append(tInputServiceSTruct.InputFields, InputField{Name: name, Type: ttype}) 1579 | } 1580 | 1581 | inputServiceStruct = append(inputServiceStruct, tInputServiceSTruct) 1582 | 1583 | } 1584 | 1585 | tmp := getServiceStructInputTemplate() 1586 | buf := &bytes.Buffer{} 1587 | 1588 | if err := tmp.Execute(buf, inputServiceStruct); err != nil { 1589 | 1590 | return "", err 1591 | 1592 | } 1593 | 1594 | return buf.String(), nil 1595 | } 1596 | 1597 | type PayloadServiceStruct struct { 1598 | Name string 1599 | ReturnType string 1600 | } 1601 | 1602 | func (m *jaalModule) GetGoPackageOfFiles(file1 pgs.File, file2 pgs.File) string { 1603 | /* 1604 | If file2 have different package than file1 then package of file2 is returned 1605 | else empty string is returned 1606 | */ 1607 | if file1.Package().ProtoName().String() != file2.Package().ProtoName().String() { 1608 | return m.GetGoPackage(file2) 1609 | } 1610 | return "" 1611 | } 1612 | 1613 | func (m *jaalModule) ServiceStructPayload(service pgs.Service) (string, error) { 1614 | /* 1615 | returns template(Service payload struct) for a service 1616 | */ 1617 | var payloadService []PayloadServiceStruct 1618 | 1619 | for _, rpc := range service.Methods() { 1620 | 1621 | flag, option, err := m.GetOption(rpc) 1622 | 1623 | if err != nil { 1624 | 1625 | m.Log("Error", err) 1626 | os.Exit(0) 1627 | 1628 | } 1629 | 1630 | if flag == false { 1631 | 1632 | continue 1633 | 1634 | } 1635 | 1636 | if option.GetMutation() == "" { 1637 | 1638 | continue 1639 | 1640 | } 1641 | goPkg := m.GetGoPackageOfFiles(service.File(), rpc.Output().File()) 1642 | 1643 | if goPkg != "" { 1644 | goPkg += "." 1645 | } 1646 | 1647 | returnType := "*" + goPkg + rpc.Output().Name().UpperCamelCase().String() 1648 | payloadService = append(payloadService, PayloadServiceStruct{Name: rpc.Name().UpperCamelCase().String(), ReturnType: returnType}) 1649 | } 1650 | 1651 | tmp := getServiceStructPayloadTemplate() 1652 | buf := &bytes.Buffer{} 1653 | 1654 | if err := tmp.Execute(buf, payloadService); err != nil { 1655 | 1656 | return "", err 1657 | 1658 | } 1659 | 1660 | return buf.String(), nil 1661 | } 1662 | 1663 | func (m *jaalModule) getPossibleReqObjects(service pgs.Service, PossibleReqObjects map[string]bool) error { 1664 | //saves all rpc inputs where rpcs are query type (used to append Input and remove Req), in PossibleReqObjects map 1665 | 1666 | for _, rpc := range service.Methods() { 1667 | flag, option, err := m.GetOption(rpc) 1668 | if err != nil { 1669 | m.Log("Error", err) 1670 | os.Exit(0) 1671 | } 1672 | 1673 | if flag == false { 1674 | continue 1675 | } 1676 | 1677 | if option.GetMutation() == "" { 1678 | continue 1679 | } 1680 | 1681 | if option.GetQuery() != "" { 1682 | PossibleReqObjects[rpc.Input().Name().String()] = true 1683 | } 1684 | } 1685 | 1686 | return nil 1687 | } 1688 | 1689 | func (m *jaalModule) InitFunc(initFunctionsName map[string]bool) (string, error) { 1690 | //returns template of init function 1691 | 1692 | tmp := getInitTemplate() 1693 | buf := &bytes.Buffer{} 1694 | 1695 | if err := tmp.Execute(buf, initFunctionsName); err != nil { 1696 | return "", err 1697 | } 1698 | 1699 | return buf.String(), nil 1700 | } 1701 | 1702 | func (m *jaalModule) TypeCastType(typeCastMap map[string]string) (string, error) { 1703 | str := "" 1704 | for k, v := range typeCastMap { 1705 | str += ("type " + k + " " + v) 1706 | str += "\n" 1707 | } 1708 | return str, nil 1709 | } 1710 | 1711 | func (m *jaalModule) GetGoPackage(target pgs.File) string { 1712 | //returns go package for a file 1713 | 1714 | goPackage := "pb" 1715 | 1716 | if target.Descriptor().GetOptions() != nil && target.Descriptor().GetOptions().GoPackage != nil { 1717 | goPackage = *target.Descriptor().GetOptions().GoPackage 1718 | goPackage = strings.Split(goPackage, ";")[0] 1719 | goPackage = strings.Split(goPackage, "/")[len(strings.Split(goPackage, "/"))-1] 1720 | 1721 | } 1722 | 1723 | return goPackage 1724 | } 1725 | func (m *jaalModule) GetImports(target pgs.File) map[string]string { 1726 | // returns a map of all imports 1727 | 1728 | imports := make(map[string]string) 1729 | 1730 | for _, importFile := range target.Imports() { 1731 | 1732 | if importFile.Descriptor().Options != nil && importFile.Descriptor().Options.GoPackage != nil { 1733 | 1734 | key := *importFile.Descriptor().Options.GoPackage 1735 | key = strings.Split(key, ";")[0] 1736 | imports[key] = strings.Split(key, "/")[len(strings.Split(key, "/"))-1] 1737 | } 1738 | } 1739 | 1740 | return imports 1741 | } 1742 | 1743 | func (m *jaalModule) ServiceStructInputFunc(service pgs.Service, initFunctionsName map[string]bool) (string, error) { 1744 | //returns template of service input struct registered methods for a service 1745 | 1746 | var inputServiceStructFunc []InputClass 1747 | 1748 | for _, rpc := range service.Methods() { 1749 | var maps []InputMap 1750 | var durations []Duration 1751 | var rIds []Id 1752 | flag, option, err := m.GetOption(rpc) 1753 | 1754 | if err != nil { 1755 | m.Log("Error", err) 1756 | os.Exit(0) 1757 | } 1758 | 1759 | if flag == false { 1760 | continue 1761 | } 1762 | 1763 | if option.GetMutation() == "" { 1764 | continue 1765 | } 1766 | 1767 | var field []MsgFields 1768 | 1769 | for _, ipField := range rpc.Input().Fields() { 1770 | //checks skip_input field option 1771 | if fieldSkip, err := m.GetFieldOptionInput(ipField); err != nil { 1772 | return "", err 1773 | } else if fieldSkip { 1774 | continue 1775 | } 1776 | tname := ipField.Name().UpperCamelCase().String() 1777 | 1778 | fName := ipField.Name().LowerCamelCase().String() 1779 | tval := "" 1780 | funcPara := "" 1781 | 1782 | idOption, err := m.IdOption(ipField) 1783 | if err != nil { 1784 | return "", err 1785 | } 1786 | 1787 | if strings.ToLower(fName) == "id" || idOption { 1788 | funcPara = "*schemabuilder.ID" 1789 | tval = "source.Value" 1790 | if ipField.Type().IsRepeated() { 1791 | funcPara = "[]*schemabuilder.ID" 1792 | rIds = append(rIds, Id{Name: ipField.Name().UpperCamelCase().String(), FieldName: ipField.Name().LowerCamelCase().String()}) 1793 | continue 1794 | } 1795 | } else if ipField.InOneOf() { 1796 | goPkg := m.GetGoPackageOfFiles(service.File(), ipField.OneOf().File()) 1797 | if goPkg != "" { 1798 | goPkg += "." 1799 | } 1800 | funcPara = goPkg + rpc.Input().Name().UpperCamelCase().String() + "_" + ipField.Name().UpperCamelCase().String() 1801 | funcPara = "*" + funcPara 1802 | field = append(field, MsgFields{TargetName: tname, FieldName: fName, FuncPara: funcPara, TargetVal: "source"}) 1803 | continue 1804 | } else if ipField.Type().IsRepeated() { 1805 | funcPara = "[]" 1806 | tObj := ipField.Type().Element() 1807 | 1808 | if tObj.IsEmbed() { 1809 | 1810 | funcPara += "*" 1811 | 1812 | } 1813 | 1814 | if tObj.IsEmbed() && tObj.Embed().File().Descriptor().Options != nil && tObj.Embed().File().Descriptor().Options.GoPackage != nil { 1815 | if service.Package().ProtoName().String() != tObj.Embed().Package().ProtoName().String() { 1816 | 1817 | funcPara += m.GetGoPackage(tObj.Embed().File()) 1818 | funcPara += "." 1819 | 1820 | } else { 1821 | if strings.Split(tObj.Embed().FullyQualifiedName(), ".")[len(strings.Split(tObj.Embed().FullyQualifiedName(), "."))-2] == tObj.Embed().Parent().Name().String() { 1822 | funcPara += (tObj.Embed().Parent().Name().String() + "_") 1823 | } 1824 | } 1825 | 1826 | } 1827 | tval = "source" 1828 | funcPara += m.fieldElementType(tObj) 1829 | } else if ipField.Type().IsMap() { 1830 | // TODO : Repeated case not handled 1831 | goPkg := "" 1832 | if ipField.Type().Element().IsEmbed() { 1833 | 1834 | goPkg = m.GetGoPackageOfFiles(service.File(), ipField.Type().Element().Embed().File()) 1835 | if goPkg != "" { 1836 | goPkg += "." 1837 | } 1838 | } 1839 | 1840 | asterik := "" 1841 | if ipField.Type().Element().IsEmbed() { 1842 | asterik = "*" 1843 | } 1844 | 1845 | value := asterik + goPkg + m.fieldElementType(ipField.Type().Element()) 1846 | maps = append(maps, InputMap{FieldName: fName, TargetVal: "*source", TargetName: ipField.Name().UpperCamelCase().String(), Key: m.fieldElementType(ipField.Type().Key()), Value: value}) 1847 | continue 1848 | } else { 1849 | var scalar = false 1850 | if ipField.Descriptor().GetType().String() == "TYPE_MESSAGE" { 1851 | tval = "source" 1852 | } else { 1853 | scalar = true 1854 | tval = "source" 1855 | } 1856 | 1857 | funcPara = m.RPCFieldType(ipField) 1858 | 1859 | if funcPara[0] == '*' { 1860 | funcPara = funcPara[1:len(funcPara)] 1861 | } 1862 | 1863 | goPkg := "" 1864 | if ipField.Type().IsEmbed() { 1865 | if ipField.Type().Embed().File().Descriptor().Options != nil && ipField.Type().Embed().File().Descriptor().Options.GoPackage != nil && service.Package().ProtoName().String() != ipField.Type().Embed().Package().ProtoName().String() { 1866 | goPkg = m.GetGoPackage(ipField.Type().Embed().File()) + "." 1867 | } else { 1868 | // message is embedded inside a message then it's gopkg is it's parent message 1869 | if strings.Split(ipField.FullyQualifiedName(), ".")[len(strings.Split(ipField.FullyQualifiedName(), "."))-2] == ipField.Type().Embed().Parent().Name().String() { 1870 | goPkg = ipField.Type().Embed().Parent().Name().String() + "_" 1871 | } 1872 | } 1873 | } else if ipField.Type().IsEnum() { 1874 | goPkg = m.GetGoPackageOfFiles(service.File(), ipField.Type().Enum().File()) 1875 | if goPkg != "" { 1876 | goPkg += "." 1877 | } 1878 | } 1879 | 1880 | if !scalar { 1881 | funcPara = "*" + goPkg + funcPara 1882 | } 1883 | } 1884 | if strings.HasSuffix(funcPara, "*timestamp.Timestamp") { 1885 | funcPara = funcPara[:len(funcPara)-19] + "schemabuilder.Timestamp" 1886 | tval = "(*timestamp.Timestamp)(source)" 1887 | } else if strings.HasSuffix(funcPara, "duration.Duration") { 1888 | if ipField.Type().IsRepeated() { 1889 | durations = append(durations, Duration{Name: ipField.Name().UpperCamelCase().String()}) 1890 | continue 1891 | } else { 1892 | funcPara = funcPara[:len(funcPara)-17] 1893 | funcPara += "schemabuilder.Duration" 1894 | tval = "(*duration.Duration)(" + tval + ")" 1895 | } 1896 | 1897 | } else if strings.HasSuffix(funcPara, "*field_mask.FieldMask") { 1898 | tval = "gtypes.ModifyFieldMask(source)" 1899 | } else if strings.HasSuffix(funcPara, "byte") { 1900 | funcPara = "*schemabuilder.Bytes" 1901 | tval = "source.Value" 1902 | } 1903 | field = append(field, MsgFields{TargetName: tname, FieldName: fName, FuncPara: funcPara, TargetVal: tval}) 1904 | } 1905 | 1906 | initFunctionsName["RegisterInput"+rpc.Name().UpperCamelCase().String()+"Input"] = true 1907 | inputServiceStructFunc = append(inputServiceStructFunc, InputClass{Name: rpc.Name().UpperCamelCase().String(), Fields: field, Maps: maps, Durations: durations, Ids: rIds}) 1908 | } 1909 | 1910 | tmp := getServiceStructInputFuncTemplate() 1911 | buf := &bytes.Buffer{} 1912 | 1913 | if err := tmp.Execute(buf, inputServiceStructFunc); err != nil { 1914 | return "", err 1915 | } 1916 | 1917 | return buf.String(), nil 1918 | } 1919 | 1920 | func (m *jaalModule) ServiceStructPayloadFunc(service pgs.Service, initFunctionsName map[string]bool) (string, error) { 1921 | //returns template of service payload struct registered methods for a service 1922 | var payloadService []PayloadServiceStruct 1923 | 1924 | for _, rpc := range service.Methods() { 1925 | flag, option, err := m.GetOption(rpc) 1926 | if err != nil { 1927 | 1928 | m.Log("Error", err) 1929 | os.Exit(0) 1930 | 1931 | } 1932 | 1933 | if flag == false { 1934 | continue 1935 | } 1936 | 1937 | if option.GetMutation() == "" { 1938 | continue 1939 | } 1940 | 1941 | initFunctionsName["RegisterPayload"+rpc.Name().UpperCamelCase().String()+"Payload"] = true 1942 | goPkg := m.GetGoPackageOfFiles(service.File(), rpc.Output().File()) 1943 | if goPkg != "" { 1944 | goPkg += "." 1945 | } 1946 | returnType := "*" + goPkg + rpc.Output().Name().UpperCamelCase().String() // "*" + rpc.Output().Name().UpperCamelCase().String() 1947 | payloadService = append(payloadService, PayloadServiceStruct{Name: rpc.Name().UpperCamelCase().String(), ReturnType: returnType}) 1948 | } 1949 | 1950 | tmp := getServiceStructPayloadFuncTemplate() 1951 | buf := &bytes.Buffer{} 1952 | 1953 | if err := tmp.Execute(buf, payloadService); err != nil { 1954 | return "", err 1955 | } 1956 | 1957 | return buf.String(), nil 1958 | } 1959 | 1960 | func (m *jaalModule) IdOption(field pgs.Field) (bool, error) { 1961 | opt := field.Descriptor().GetOptions() 1962 | if opt != nil { 1963 | x, err := proto.GetExtension(opt, pbt.E_Id) 1964 | if err == proto.ErrMissingExtension { 1965 | return false, nil 1966 | } 1967 | 1968 | option := *x.((*bool)) 1969 | if option == true && *field.Descriptor().Type != pgd.FieldDescriptorProto_TYPE_STRING { 1970 | return false, fmt.Errorf("id can be used to tag string fields only") 1971 | } 1972 | 1973 | return option, nil 1974 | } 1975 | 1976 | return false, nil 1977 | } 1978 | -------------------------------------------------------------------------------- /module.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/golang/protobuf/proto" 7 | pgs "github.com/lyft/protoc-gen-star" 8 | pgsgo "github.com/lyft/protoc-gen-star/lang/go" 9 | pbt "go.appointy.com/protoc-gen-jaal/schema" 10 | ) 11 | 12 | type jaalModule struct { 13 | *pgs.ModuleBase 14 | pgsgo.Context 15 | } 16 | 17 | func (m *jaalModule) InitContext(c pgs.BuildContext) { 18 | m.ModuleBase.InitContext(c) 19 | m.Context = pgsgo.InitContext(c.Parameters()) 20 | } 21 | 22 | func (m *jaalModule) Name() string { return "jaal" } 23 | func (m *jaalModule) CheckSkipFile(target pgs.File) (bool, error) { 24 | // checks file_skip option 25 | opt := target.Descriptor().GetOptions() 26 | if opt != nil { 27 | x, err := proto.GetExtension(opt, pbt.E_FileSkip) 28 | if err != nil && proto.ErrMissingExtension != err { 29 | return false, err 30 | } 31 | 32 | if x != nil && *x.(*bool) == true { // skips only when file_skip is explicitly true 33 | return true, nil 34 | } 35 | } 36 | return false, nil 37 | } 38 | func (m *jaalModule) Execute(targets map[string]pgs.File, pkgs map[string]pgs.Package) []pgs.Artifact { 39 | for _, target := range targets { // loop over files 40 | 41 | if ok, err := m.CheckSkipFile(target); err != nil { // checks file_skip option 42 | fmt.Println("Error :", err) 43 | continue 44 | } else if ok == true { 45 | continue 46 | } 47 | 48 | fname := target.Name().String() 49 | fname = fname[:len(fname)-6] 50 | 51 | name := m.BuildContext.OutputPath() + "/" + fname + ".pb.gq.go" 52 | 53 | str, err := m.generateFileData(target) 54 | if err != nil { 55 | m.Log("Error : ", err) 56 | } 57 | m.AddGeneratorFile(name, str) 58 | } 59 | return m.Artifacts() 60 | } 61 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # protoc-gen-jaal - Develop Relay compliant GraphQL servers 2 | 3 | protoc-gen-jaal is a protoc plugin used to generate [Jaal](https://github.com/appointy/jaal) APIs. The server built from these APIs is GraphQL spec compliant as well as Relay compliant. protoc-gen-jaal also handles oneOf by registering it as a Union on the schema. 4 | 5 | ## Getting Started 6 | 7 | Let's get started with a quick example. 8 | 9 | ```protobuf 10 | syntax = "proto3"; 11 | 12 | package customer; 13 | 14 | option go_package = "customerpb"; 15 | 16 | import "schema/schema.proto"; 17 | 18 | service Customers { 19 | // CreateCustomer creates new customer. 20 | rpc CreateCustomer (CreateCustomerRequest) returns (Customer) { 21 | option (graphql.schema) = { 22 | mutation : "createCustomer" 23 | }; 24 | }; 25 | 26 | // GetCustomer returns the customer by its unique user id. 27 | rpc GetCustomer (GetCustomerRequest) returns (Customer) { 28 | option (graphql.schema) = { 29 | query : "customer" 30 | }; 31 | }; 32 | } 33 | 34 | message CreateCustomerRequest { 35 | string email = 1; 36 | string first_name = 2; 37 | string last_name = 3; 38 | } 39 | 40 | message GetCustomerRequest { 41 | string id = 1; 42 | } 43 | 44 | message Customer { 45 | string id = 1; 46 | string email = 2; 47 | string first_name = 3; 48 | string last_name = 4; 49 | } 50 | ``` 51 | 52 | protoc-gen-jaal uses the method option *schema* to determine whether to register an rpc as query or as mutation. If an *rpc* is not tagged, then it will not be registered on the GraphQL schema. To generate Relay compliant servers, protoc-gen-jaal generates the input and payload of each mutation with clientMutationId. The graphql schema of above example is as follows: 53 | 54 | ```GraphQl Schema 55 | input CreateCustomerInput { 56 | clientMutationId: String 57 | email: String 58 | firstName: String 59 | lastName: String 60 | } 61 | 62 | type CreateCustomerPayload { 63 | clientMutationId: String! 64 | payload: Customer 65 | } 66 | 67 | type Customer { 68 | email: String! 69 | firstName: String! 70 | id: ID! 71 | lastName: String! 72 | } 73 | 74 | type Mutation { 75 | createCustomer(input: CreateCustomerInput): CreateCustomerPayload 76 | } 77 | 78 | type Query { 79 | customer(id: ID): Customer 80 | } 81 | ``` 82 | 83 | ### Installing 84 | 85 | The installation of protoc-gen-jaal can be done directly by running go get. 86 | 87 | ``` 88 | go get go.appointy.com/protoc-gen-jaal 89 | ``` 90 | 91 | ### Usage 92 | 93 | For a proto file customer.proto, the corresponding code is generated in customer.gq.go. 94 | 95 | ``` 96 | protoc \ 97 | -I . \ 98 | -I ${GOPATH}/src \ 99 | -I ${GOPATH}/src/go.appointy.com/protoc-gen-jaal \ 100 | --go_out=grpc=plugins:. \ 101 | --jaal_out:. \ 102 | customer.proto && goimports -w . 103 | ``` 104 | 105 | protoc-gen-jaal generates the code to register each message as input and payload. The payload is registered with the name of message. The input is registered with the name of message suffixed with "Input". protoc-gen-jaal implicitly registers field named id as GraphQL ID. 106 | 107 | ## Available Options 108 | 109 | The behaviour of protoc-gen-jaal can be modified using the following options: 110 | 111 | ### File Option 112 | 113 | * file_skip : This option is used to skip the generation of gq file. 114 | 115 | ### Method Option 116 | 117 | * schema : This option is used to tag an rpc as query or mutation. 118 | 119 | ### Message Options 120 | 121 | * skip : This option is used to skip the registration of a message on the graphql schema. 122 | 123 | * name : This option is used to change default name of message on graphql schema. 124 | 125 | * type : This option is used to change go type of the message in the gq file. 126 | 127 | ### Field Options 128 | 129 | * input_skip : This option is used to skip the registration of the field on input object. 130 | 131 | * payload_skip : This option is used to skip the registration of the field on payload object. 132 | 133 | * id : This option is used to expose the field as GraphQL ID. Only string field can be tagged with this option. 134 | -------------------------------------------------------------------------------- /register.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | 6 | pgs "github.com/lyft/protoc-gen-star" 7 | ) 8 | 9 | func (m *jaalModule) generateFileData(target pgs.File) (string, error) { 10 | buf := &bytes.Buffer{} 11 | 12 | go_package := m.GetGoPackage(target) 13 | buf.WriteString("// Code generated by protoc-gen-graphql. DO NOT EDIT.\n") 14 | buf.WriteString("package " + go_package + ";\n") 15 | 16 | initFunctionsName := make(map[string]bool) 17 | imports := m.GetImports(target) 18 | 19 | for key, _ := range imports { 20 | buf.WriteString("import \"" + key + "\";\n") 21 | } 22 | buf.WriteString("import \"context\";") 23 | buf.WriteString("import \"encoding/json\";") 24 | buf.WriteString("import \"encoding/base64\";") 25 | buf.WriteString("import \"go.appointy.com/jaal/gtypes\";") 26 | buf.WriteString("import \"go.appointy.com/jaal/schemabuilder\";") 27 | 28 | for _, enums := range target.AllEnums() { //enum type 29 | str, err := m.EnumType(enums, imports, initFunctionsName) 30 | 31 | if err != nil { 32 | return "", err 33 | } 34 | 35 | buf.WriteString(str + "\n") 36 | } 37 | 38 | PossibleReqObjects := make(map[string]bool) 39 | typeCastMap := make(map[string]string) 40 | for _, service := range target.Services() { 41 | if err := m.getPossibleReqObjects(service, PossibleReqObjects); err != nil { 42 | return "", err 43 | } 44 | } 45 | 46 | for _, msgs := range target.AllMessages() { // union struct Type 47 | str, err := m.UnionStruct(msgs, imports, PossibleReqObjects, initFunctionsName) 48 | if err != nil { 49 | return "", err 50 | } 51 | buf.WriteString(str + "\n") 52 | } 53 | 54 | for _, msgs := range target.AllMessages() { // oneof Input Type 55 | str, err := m.OneofInputType(msgs, imports, initFunctionsName) 56 | if err != nil { 57 | return "", err 58 | } 59 | buf.WriteString(str + "\n") 60 | } 61 | 62 | for _, msgs := range target.AllMessages() { // oneof payload Type 63 | str, err := m.OneofPayloadType(msgs, imports, initFunctionsName) 64 | if err != nil { 65 | return "", err 66 | } 67 | buf.WriteString(str + "\n") 68 | } 69 | 70 | for _, msgs := range target.AllMessages() { // Input Type 71 | str, err := m.InputType(msgs, imports, PossibleReqObjects, initFunctionsName, typeCastMap) 72 | if err != nil { 73 | return "", err 74 | } 75 | buf.WriteString(str + "\n") 76 | } 77 | 78 | for _, msgs := range target.AllMessages() { // payload type 79 | str, err := m.PayloadType(msgs, imports, initFunctionsName, typeCastMap) 80 | if err != nil { 81 | return "", err 82 | } 83 | buf.WriteString(str + "\n") 84 | } 85 | 86 | for _, service := range target.Services() { // mutation input struct 87 | str, err := m.ServiceStructInput(service) 88 | if err != nil { 89 | return "", err 90 | } 91 | buf.WriteString(str + "\n") 92 | } 93 | 94 | for _, service := range target.Services() { // mutation output struct 95 | str, err := m.ServiceStructPayload(service) 96 | if err != nil { 97 | return "", err 98 | } 99 | buf.WriteString(str + "\n") 100 | } 101 | 102 | for _, service := range target.Services() { // mutation init input 103 | str, err := m.ServiceStructInputFunc(service, initFunctionsName) 104 | if err != nil { 105 | return "", err 106 | } 107 | buf.WriteString(str + "\n") 108 | } 109 | 110 | for _, service := range target.Services() { // mutation init payload 111 | str, err := m.ServiceStructPayloadFunc(service, initFunctionsName) 112 | if err != nil { 113 | return "", err 114 | } 115 | buf.WriteString(str + "\n") 116 | } 117 | 118 | for _, service := range target.Services() { // services 119 | str, err := m.ServiceInput(service) 120 | if err != nil { 121 | return "", err 122 | } 123 | buf.WriteString(str + "\n") 124 | } 125 | 126 | if str, err := m.InitFunc(initFunctionsName); err != nil { // init 127 | return "", err 128 | } else { 129 | buf.WriteString(str + "\n") 130 | } 131 | 132 | if str, err := m.TypeCastType(typeCastMap); err != nil { //type cast 133 | return "", err 134 | } else { 135 | buf.WriteString(str + "\n") 136 | } 137 | return buf.String(), nil 138 | } 139 | -------------------------------------------------------------------------------- /schema/schema.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // source: schema/schema.proto 3 | 4 | package schema 5 | 6 | import ( 7 | fmt "fmt" 8 | proto "github.com/golang/protobuf/proto" 9 | descriptor "github.com/golang/protobuf/protoc-gen-go/descriptor" 10 | math "math" 11 | ) 12 | 13 | // Reference imports to suppress errors if they are not otherwise used. 14 | var _ = proto.Marshal 15 | var _ = fmt.Errorf 16 | var _ = math.Inf 17 | 18 | // This is a compile-time assertion to ensure that this generated file 19 | // is compatible with the proto package it is being compiled against. 20 | // A compilation error at this line likely means your copy of the 21 | // proto package needs to be updated. 22 | const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package 23 | 24 | type MethodOptions struct { 25 | // Types that are valid to be assigned to Type: 26 | // *MethodOptions_Query 27 | // *MethodOptions_Mutation 28 | Type isMethodOptions_Type `protobuf_oneof:"type"` 29 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 30 | XXX_unrecognized []byte `json:"-"` 31 | XXX_sizecache int32 `json:"-"` 32 | } 33 | 34 | func (m *MethodOptions) Reset() { *m = MethodOptions{} } 35 | func (m *MethodOptions) String() string { return proto.CompactTextString(m) } 36 | func (*MethodOptions) ProtoMessage() {} 37 | func (*MethodOptions) Descriptor() ([]byte, []int) { 38 | return fileDescriptor_98b0d2c3e7e0142d, []int{0} 39 | } 40 | 41 | func (m *MethodOptions) XXX_Unmarshal(b []byte) error { 42 | return xxx_messageInfo_MethodOptions.Unmarshal(m, b) 43 | } 44 | func (m *MethodOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 45 | return xxx_messageInfo_MethodOptions.Marshal(b, m, deterministic) 46 | } 47 | func (m *MethodOptions) XXX_Merge(src proto.Message) { 48 | xxx_messageInfo_MethodOptions.Merge(m, src) 49 | } 50 | func (m *MethodOptions) XXX_Size() int { 51 | return xxx_messageInfo_MethodOptions.Size(m) 52 | } 53 | func (m *MethodOptions) XXX_DiscardUnknown() { 54 | xxx_messageInfo_MethodOptions.DiscardUnknown(m) 55 | } 56 | 57 | var xxx_messageInfo_MethodOptions proto.InternalMessageInfo 58 | 59 | type isMethodOptions_Type interface { 60 | isMethodOptions_Type() 61 | } 62 | 63 | type MethodOptions_Query struct { 64 | Query string `protobuf:"bytes,1,opt,name=query,proto3,oneof"` 65 | } 66 | 67 | type MethodOptions_Mutation struct { 68 | Mutation string `protobuf:"bytes,2,opt,name=mutation,proto3,oneof"` 69 | } 70 | 71 | func (*MethodOptions_Query) isMethodOptions_Type() {} 72 | 73 | func (*MethodOptions_Mutation) isMethodOptions_Type() {} 74 | 75 | func (m *MethodOptions) GetType() isMethodOptions_Type { 76 | if m != nil { 77 | return m.Type 78 | } 79 | return nil 80 | } 81 | 82 | func (m *MethodOptions) GetQuery() string { 83 | if x, ok := m.GetType().(*MethodOptions_Query); ok { 84 | return x.Query 85 | } 86 | return "" 87 | } 88 | 89 | func (m *MethodOptions) GetMutation() string { 90 | if x, ok := m.GetType().(*MethodOptions_Mutation); ok { 91 | return x.Mutation 92 | } 93 | return "" 94 | } 95 | 96 | // XXX_OneofWrappers is for the internal use of the proto package. 97 | func (*MethodOptions) XXX_OneofWrappers() []interface{} { 98 | return []interface{}{ 99 | (*MethodOptions_Query)(nil), 100 | (*MethodOptions_Mutation)(nil), 101 | } 102 | } 103 | 104 | var E_Schema = &proto.ExtensionDesc{ 105 | ExtendedType: (*descriptor.MethodOptions)(nil), 106 | ExtensionType: (*MethodOptions)(nil), 107 | Field: 91111, 108 | Name: "graphql.schema", 109 | Tag: "bytes,91111,opt,name=schema", 110 | Filename: "schema/schema.proto", 111 | } 112 | 113 | var E_Skip = &proto.ExtensionDesc{ 114 | ExtendedType: (*descriptor.MessageOptions)(nil), 115 | ExtensionType: (*bool)(nil), 116 | Field: 91112, 117 | Name: "graphql.skip", 118 | Tag: "varint,91112,opt,name=skip", 119 | Filename: "schema/schema.proto", 120 | } 121 | 122 | var E_Name = &proto.ExtensionDesc{ 123 | ExtendedType: (*descriptor.MessageOptions)(nil), 124 | ExtensionType: (*string)(nil), 125 | Field: 91114, 126 | Name: "graphql.name", 127 | Tag: "bytes,91114,opt,name=name", 128 | Filename: "schema/schema.proto", 129 | } 130 | 131 | var E_Type = &proto.ExtensionDesc{ 132 | ExtendedType: (*descriptor.MessageOptions)(nil), 133 | ExtensionType: (*string)(nil), 134 | Field: 91117, 135 | Name: "graphql.type", 136 | Tag: "bytes,91117,opt,name=type", 137 | Filename: "schema/schema.proto", 138 | } 139 | 140 | var E_FileSkip = &proto.ExtensionDesc{ 141 | ExtendedType: (*descriptor.FileOptions)(nil), 142 | ExtensionType: (*bool)(nil), 143 | Field: 91113, 144 | Name: "graphql.file_skip", 145 | Tag: "varint,91113,opt,name=file_skip", 146 | Filename: "schema/schema.proto", 147 | } 148 | 149 | var E_InputSkip = &proto.ExtensionDesc{ 150 | ExtendedType: (*descriptor.FieldOptions)(nil), 151 | ExtensionType: (*bool)(nil), 152 | Field: 91115, 153 | Name: "graphql.input_skip", 154 | Tag: "varint,91115,opt,name=input_skip", 155 | Filename: "schema/schema.proto", 156 | } 157 | 158 | var E_PayloadSkip = &proto.ExtensionDesc{ 159 | ExtendedType: (*descriptor.FieldOptions)(nil), 160 | ExtensionType: (*bool)(nil), 161 | Field: 91116, 162 | Name: "graphql.payload_skip", 163 | Tag: "varint,91116,opt,name=payload_skip", 164 | Filename: "schema/schema.proto", 165 | } 166 | 167 | var E_Id = &proto.ExtensionDesc{ 168 | ExtendedType: (*descriptor.FieldOptions)(nil), 169 | ExtensionType: (*bool)(nil), 170 | Field: 91120, 171 | Name: "graphql.id", 172 | Tag: "varint,91120,opt,name=id", 173 | Filename: "schema/schema.proto", 174 | } 175 | 176 | var E_FieldName = &proto.ExtensionDesc{ 177 | ExtendedType: (*descriptor.FieldOptions)(nil), 178 | ExtensionType: (*string)(nil), 179 | Field: 91121, 180 | Name: "graphql.field_name", 181 | Tag: "bytes,91121,opt,name=field_name", 182 | Filename: "schema/schema.proto", 183 | } 184 | 185 | func init() { 186 | proto.RegisterType((*MethodOptions)(nil), "graphql.MethodOptions") 187 | proto.RegisterExtension(E_Schema) 188 | proto.RegisterExtension(E_Skip) 189 | proto.RegisterExtension(E_Name) 190 | proto.RegisterExtension(E_Type) 191 | proto.RegisterExtension(E_FileSkip) 192 | proto.RegisterExtension(E_InputSkip) 193 | proto.RegisterExtension(E_PayloadSkip) 194 | proto.RegisterExtension(E_Id) 195 | proto.RegisterExtension(E_FieldName) 196 | } 197 | 198 | func init() { proto.RegisterFile("schema/schema.proto", fileDescriptor_98b0d2c3e7e0142d) } 199 | 200 | var fileDescriptor_98b0d2c3e7e0142d = []byte{ 201 | // 355 bytes of a gzipped FileDescriptorProto 202 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0x4f, 0x4f, 0xf2, 0x40, 203 | 0x10, 0x87, 0x5f, 0x08, 0xf0, 0xd2, 0x41, 0x2f, 0x35, 0x21, 0x44, 0x41, 0x89, 0x27, 0x4e, 0xdb, 204 | 0x44, 0xe3, 0x05, 0x13, 0x0f, 0x1c, 0x8c, 0x17, 0xd4, 0xd4, 0x9b, 0x17, 0xb2, 0xd0, 0xa5, 0xac, 205 | 0x6e, 0xbb, 0x4b, 0xbb, 0x3d, 0xf4, 0x13, 0xfa, 0x51, 0xfc, 0x9f, 0xe8, 0x37, 0x30, 0x3b, 0x5d, 206 | 0x48, 0x88, 0x24, 0xf5, 0xd4, 0x74, 0x67, 0x9e, 0x67, 0x7e, 0x3b, 0x59, 0xd8, 0x4b, 0x67, 0x0b, 207 | 0x16, 0x51, 0xaf, 0xf8, 0x10, 0x95, 0x48, 0x2d, 0xdd, 0xff, 0x61, 0x42, 0xd5, 0x62, 0x29, 0xf6, 208 | 0xfb, 0xa1, 0x94, 0xa1, 0x60, 0x1e, 0x1e, 0x4f, 0xb3, 0xb9, 0x17, 0xb0, 0x74, 0x96, 0x70, 0xa5, 209 | 0x65, 0x52, 0xb4, 0x1e, 0x8f, 0x61, 0x77, 0xcc, 0xf4, 0x42, 0x06, 0x37, 0x4a, 0x73, 0x19, 0xa7, 210 | 0x6e, 0x1b, 0xea, 0xcb, 0x8c, 0x25, 0x79, 0xa7, 0xd2, 0xaf, 0x0c, 0x9c, 0xab, 0x7f, 0x7e, 0xf1, 211 | 0xeb, 0x76, 0xa1, 0x19, 0x65, 0x9a, 0x9a, 0xa6, 0x4e, 0xd5, 0x96, 0xd6, 0x27, 0xa3, 0x06, 0xd4, 212 | 0x74, 0xae, 0xd8, 0xf0, 0x16, 0x1a, 0x45, 0x12, 0xf7, 0x90, 0x14, 0xb3, 0xc9, 0x6a, 0x36, 0xd9, 213 | 0x98, 0xd3, 0x79, 0x7e, 0xaa, 0xf7, 0x2b, 0x83, 0xd6, 0x49, 0x9b, 0xd8, 0xb0, 0x9b, 0x75, 0xdf, 214 | 0x7a, 0x86, 0x67, 0x50, 0x4b, 0x1f, 0xb9, 0x72, 0x8f, 0xb6, 0xf8, 0xd2, 0x94, 0x86, 0x6c, 0x25, 215 | 0x7c, 0x41, 0x61, 0xd3, 0xc7, 0x76, 0x83, 0xc5, 0x34, 0x62, 0xe5, 0xd8, 0x1b, 0x62, 0x8e, 0x8f, 216 | 0xed, 0x06, 0x33, 0xf7, 0x28, 0xc7, 0x3e, 0x57, 0x18, 0x5e, 0xfb, 0x1c, 0x9c, 0x39, 0x17, 0x6c, 217 | 0x82, 0x49, 0xbb, 0xbf, 0xd8, 0x4b, 0x2e, 0xd6, 0xe0, 0xab, 0x8d, 0xd9, 0x34, 0xc0, 0x9d, 0x89, 218 | 0x7a, 0x01, 0xc0, 0x63, 0x95, 0xe9, 0x82, 0xee, 0x6d, 0xa1, 0x99, 0x58, 0xaf, 0xed, 0xdd, 0xe2, 219 | 0x0e, 0x22, 0xc8, 0x8f, 0x60, 0x47, 0xd1, 0x5c, 0x48, 0x1a, 0xfc, 0xc9, 0xf0, 0x61, 0x0d, 0x2d, 220 | 0x0b, 0xa1, 0xc3, 0x83, 0x2a, 0x0f, 0xca, 0xc8, 0x2f, 0x4b, 0x56, 0x79, 0x60, 0x42, 0xcf, 0x4d, 221 | 0x6d, 0x82, 0x5b, 0x2e, 0x01, 0xbf, 0xed, 0xb2, 0x1c, 0x44, 0xae, 0x69, 0xc4, 0x46, 0xbd, 0xfb, 222 | 0x83, 0x50, 0x12, 0xaa, 0x94, 0xe4, 0xb1, 0xce, 0xc9, 0x4c, 0x46, 0xde, 0x03, 0xa5, 0xc2, 0xbe, 223 | 0xe3, 0x69, 0x03, 0x45, 0xa7, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x92, 0x49, 0x8a, 0xa4, 0xdf, 224 | 0x02, 0x00, 0x00, 225 | } 226 | -------------------------------------------------------------------------------- /schema/schema.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package graphql; 4 | 5 | 6 | import "google/protobuf/descriptor.proto"; 7 | 8 | option go_package="go.appointy.com/jaal/schema"; 9 | 10 | extend google.protobuf.MethodOptions { 11 | // schema is used to tag an rpc as query or mutation. 12 | MethodOptions schema = 91111; 13 | } 14 | 15 | extend google.protobuf.MessageOptions{ 16 | // skip is used to skip the registration of a message on the graphql schema. 17 | bool skip = 91112; 18 | // name is used to change default name of message on graphql schema. 19 | string name = 91114; 20 | // type is used to change go type of the message in the gq file. 21 | string type = 91117; 22 | } 23 | 24 | extend google.protobuf.FileOptions{ 25 | // file_skip is used to skip the generation of gq file. 26 | bool file_skip = 91113; 27 | } 28 | 29 | extend google.protobuf.FieldOptions{ 30 | // input_skip is used to skip the registration of the field on input object. 31 | bool input_skip = 91115; 32 | // payload_skip is used to skip the registration of the field on payload object. 33 | bool payload_skip = 91116; 34 | // id is used to expose the field as graphQL ID. Only string field can be tagged with this option. 35 | bool id = 91120; 36 | // field_name is used to change the default name of field on graphql schema. 37 | string field_name = 91121; 38 | } 39 | 40 | message MethodOptions { 41 | oneof type { 42 | string query = 1; 43 | string mutation = 2; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /templates.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "text/template" 4 | import "log" 5 | 6 | func getEnumTemplate() *template.Template { 7 | 8 | tmpl := ` 9 | func Register{{.Name}}(schema *schemabuilder.Schema){ 10 | {{$name:=.Name}} 11 | schema.Enum({{.Name}}(0), map[string]interface{}{ 12 | {{range .Values}} "{{.Value}}": {{$name}}({{.Index}}),{{"\n"}}{{end}} 13 | }) 14 | } 15 | ` 16 | 17 | t, err := template.New("enum").Parse(tmpl) 18 | if err != nil { 19 | log.Fatal("Parse: ", err) 20 | panic(err) 21 | } 22 | 23 | return t 24 | } 25 | 26 | func getUnionStructTemplate() *template.Template { 27 | 28 | tmpl := ` 29 | {{range .}} 30 | type {{.UnionName}} struct { 31 | schemabuilder.Union 32 | {{range .UnionFields}} 33 | {{.}}{{end}} 34 | } 35 | {{end}} 36 | ` 37 | 38 | t, err := template.New("UnionStruct").Parse(tmpl) 39 | if err != nil { 40 | log.Fatal("Parse: ", err) 41 | panic(err) 42 | } 43 | 44 | return t 45 | } 46 | 47 | func getInputTemplate() *template.Template { 48 | 49 | tmpl := ` 50 | func RegisterInput{{.Name}}(schema *schemabuilder.Schema) { 51 | input := schema.InputObject("{{.InputObjName}}", {{.Type}}{}) 52 | {{$name:=.Name}} 53 | {{range .Maps}} 54 | input.FieldFunc("{{.FieldName}}", func(target *{{$name}}, source *schemabuilder.Map) error { 55 | v := source.Value 56 | 57 | decodedValue, err := base64.StdEncoding.DecodeString(v) 58 | if err != nil { 59 | return err 60 | } 61 | 62 | data := make(map[{{.Key}}]{{.Value}}) 63 | if err := json.Unmarshal(decodedValue, &data); err != nil { 64 | return err 65 | } 66 | 67 | target.{{.TargetName}} = data 68 | return nil 69 | }){{end}} 70 | {{range .Fields}} 71 | input.FieldFunc("{{.FieldName}}", func(target *{{$name}}, source {{.FuncPara}}) { 72 | target.{{.TargetName}} = {{.TargetVal}} 73 | }){{end}} 74 | {{range .Durations}} 75 | input.FieldFunc("{{.FieldName}}", func(target *{{$name}}, source []*schemabuilder.Duration) { 76 | array := make([]*duration.Duration, 0 ,len(source)) 77 | for _, s:= range source{ 78 | array = append(array, (*duration.Duration)(s)) 79 | } 80 | target.{{.Name}} = array 81 | }){{end}} 82 | {{range .Ids}} 83 | input.FieldFunc("{{.FieldName}}", func(target *{{$name}}, source []schemabuilder.ID) { 84 | array:= make([]string,0,len(source)) 85 | for _, s:= range source{ 86 | array = append(array, (s.Value)) 87 | } 88 | target.{{.Name}}=array 89 | }){{end}} 90 | } 91 | ` 92 | 93 | t, err := template.New("InputType").Parse(tmpl) 94 | if err != nil { 95 | log.Fatal("Parse: ", err) 96 | panic(err) 97 | } 98 | 99 | return t 100 | } 101 | func getPayloadTemplate() *template.Template { 102 | 103 | tmpl := ` 104 | func RegisterPayload{{.Name}}(schema *schemabuilder.Schema) { 105 | payload := schema.Object("{{.PayloadObjName}}", {{.Name}}{}){{$name:=.Name}} 106 | {{range .Maps}} 107 | payload.FieldFunc("{{.FieldName}}", func(ctx context.Context, in *{{$name}}) (*schemabuilder.Map, error) { 108 | data, err := json.Marshal({{.TargetVal}}) 109 | if err != nil { 110 | return nil, err 111 | } 112 | 113 | return &schemabuilder.Map{Value:string(data)}, nil 114 | }){{end}} 115 | {{range .UnionObjects}} 116 | payload.FieldFunc("{{.FieldName}}", func(ctx context.Context, in *{{$name}}) {{.FuncReturn}} { 117 | switch v := in{{"."}}{{.SwitchName}}{{"."}}(type) { 118 | {{range .Fields}} 119 | case *{{.CaseName}}: 120 | return {{.ReturnType}}{ 121 | {{.CaseName}}: v, 122 | } 123 | {{end}} 124 | } 125 | return nil 126 | }) 127 | {{end}} 128 | {{range .Fields}} 129 | payload.FieldFunc("{{.FieldName}}", func(ctx context.Context, in *{{$name}}) {{.FuncPara}} { 130 | return {{.TargetVal}} 131 | }){{end}} 132 | {{range .Durations}} 133 | payload.FieldFunc("{{.FieldName}}", func(ctx context.Context, in *{{$name}}) []*schemabuilder.Duration { 134 | array := make([]*schemabuilder.Duration, 0, len(in.{{.Name}})) 135 | for _, d := range in.{{.Name}}{ 136 | array = append(array, (*schemabuilder.Duration)(d)) 137 | } 138 | return array 139 | }){{end}} 140 | {{range .Ids}} 141 | payload.FieldFunc("{{.FieldName}}", func(ctx context.Context, in *Class) []schemabuilder.ID { 142 | array := make([]schemabuilder.ID, 0, len(in.{{.Name}})) 143 | for _, d := range in.{{.Name}}{ 144 | array = append(array, schemabuilder.ID{Value:d}) 145 | } 146 | return array 147 | }){{end}} 148 | } 149 | ` 150 | 151 | t, err := template.New("Payload").Parse(tmpl) 152 | if err != nil { 153 | log.Fatal("Parse: ", err) 154 | panic(err) 155 | } 156 | 157 | return t 158 | } 159 | 160 | func getServiceTemplate() *template.Template { 161 | 162 | tmpl := ` 163 | func Register{{.Name}}Operations(schema *schemabuilder.Schema, client {{.Name}}Client) { 164 | {{range .Queries}} 165 | schema.Query().FieldFunc("{{.FieldName}}", func(ctx context.Context, args struct { 166 | {{range .InType}} 167 | {{.Name}} {{.Type}}{{end}} 168 | }) ({{.FirstReturnArgType}}, error) { 169 | {{range .MapsData}} 170 | v{{.Name}} := args.{{.Name}}.Value 171 | decodedValue{{.Name}}, err{{.Name}} := base64.StdEncoding.DecodeString(v{{.Name}}) 172 | if err{{.Name}} != nil { 173 | return nil,err{{.Name}} 174 | } 175 | {{.NewVarName}}Map := make(map[{{.Key}}]{{.Value}}) 176 | if err{{.Name}} := json.Unmarshal(decodedValue{{.Name}}, &{{.NewVarName}}Map); err{{.Name}} != nil { 177 | return nil,err{{.Name}} 178 | }{{end}} 179 | request := &{{.InputName}}{ 180 | {{range .ReturnType}} 181 | {{.Name}}: {{.Type}},{{end}} 182 | } 183 | {{range .Durations}} 184 | array{{.Name}} := make([]*duration.Duration, 0 ,len(args.{{.Name}})) 185 | for _, s:= range args.{{.Name}}{ 186 | array{{.Name}} = append(array{{.Name}}, (*duration.Duration)(s)) 187 | } 188 | request.{{.Name}}=array{{.Name}} 189 | {{end}} 190 | {{range .Ids}} 191 | array{{.Name}} := make([]string,0,len(args.{{.Name}} )) 192 | for _,s := range args.{{.Name}} { 193 | array{{.Name}} = append(array{{.Name}} ,s.Value) 194 | } 195 | request.Id=array{{.Name}} 196 | {{end}} 197 | {{range .Oneofs}} 198 | {{$oneOfNameQ:= .Name}} 199 | {{range .Fields}} 200 | if args.{{.Name}} != nil{ 201 | request.{{$oneOfNameQ}} = args.{{.Name}} 202 | } 203 | {{end}} 204 | {{end}} 205 | response, err := client{{"."}}{{.ReturnFunc}}(ctx, request) 206 | if err!= nil{ 207 | return {{.FirstReturnArgType}}{}, err 208 | } 209 | return *response, nil 210 | }) 211 | {{end}} 212 | {{range .Mutations}} 213 | schema.Mutation().FieldFunc("{{.FieldName}}", func(ctx context.Context, args struct { 214 | Input {{.InputType}} 215 | }) ({{.FirstReturnArgType}}, error) { 216 | request := {{.RequestType}}{ 217 | {{range .RequestFields}} 218 | {{.}}: args{{"."}}Input{{"."}}{{.}},{{end}} 219 | } 220 | {{range .OneOfs}} 221 | {{$oneOfName:= .Name}} 222 | {{range .Fields}} 223 | if args.Input.{{.Name}} != nil{ 224 | request.{{$oneOfName}} = args.Input.{{.Name}} 225 | }{{end}}{{end}} 226 | response, err := client{{"."}}{{.ResponseType}}(ctx, request) 227 | return {{.ReturnType}}{ 228 | Payload: response, 229 | ClientMutationId: args.Input.ClientMutationId, 230 | }, err 231 | }) 232 | {{end}} 233 | } 234 | ` 235 | 236 | t, err := template.New("service").Parse(tmpl) 237 | if err != nil { 238 | log.Fatal("Parse: ", err) 239 | panic(err) 240 | } 241 | 242 | return t 243 | } 244 | 245 | func getServiceStructInputTemplate() *template.Template { 246 | 247 | tmpl := ` 248 | {{range .}} 249 | type {{.RpcName}}Input struct { 250 | {{range .InputFields}} 251 | {{.Name}} {{.Type}}{{end}} 252 | ClientMutationId string 253 | } 254 | {{end}} 255 | ` 256 | 257 | t, err := template.New("serviceStruct").Parse(tmpl) 258 | if err != nil { 259 | log.Fatal("Parse: ", err) 260 | panic(err) 261 | } 262 | 263 | return t 264 | } 265 | 266 | func getServiceStructPayloadTemplate() *template.Template { 267 | 268 | tmpl := ` 269 | {{range .}} 270 | type {{.Name}}Payload struct { 271 | Payload {{.ReturnType}} 272 | ClientMutationId string 273 | } 274 | {{end}} 275 | ` 276 | 277 | t, err := template.New("ServiceStructPayload").Parse(tmpl) 278 | if err != nil { 279 | log.Fatal("Parse: ", err) 280 | panic(err) 281 | } 282 | 283 | return t 284 | } 285 | 286 | func getInitTemplate() *template.Template { 287 | 288 | tmpl := ` 289 | func init() { 290 | {{range $key, $value := . }} 291 | {{$key}}(gtypes.Schema){{end}} 292 | } 293 | ` 294 | 295 | t, err := template.New("Init").Parse(tmpl) 296 | if err != nil { 297 | log.Fatal("Parse: ", err) 298 | panic(err) 299 | } 300 | 301 | return t 302 | } 303 | 304 | func getOneofInputTemplate() *template.Template { 305 | 306 | tmpl := ` 307 | {{range .}} 308 | func RegisterInput{{.Name}}(schema *schemabuilder.Schema) { 309 | input := schema.InputObject("{{.SchemaObjectPara}}", {{.Name}}{}) 310 | input.FieldFunc("{{.FieldFuncPara}}", func(target *{{.Name}}, source *{{.FieldFuncSecondParaFuncPara}}) { 311 | target{{"."}}{{.TargetName}} ={{.TargetVal}} 312 | }) 313 | } 314 | {{end}} 315 | ` 316 | 317 | t, err := template.New("oneof").Parse(tmpl) 318 | if err != nil { 319 | log.Fatal("Parse: ", err) 320 | panic(err) 321 | } 322 | 323 | return t 324 | } 325 | 326 | func getOneofPayloadTemplate() *template.Template { 327 | 328 | tmpl := ` 329 | {{range .}} 330 | func RegisterPayload{{.Name}}(schema *schemabuilder.Schema) { 331 | payload := schema.Object("{{.Name}}", {{.Name}}{}) 332 | payload.FieldFunc("{{.FieldFuncPara}}", func(ctx context.Context, in *{{.Name}}) {{.FieldFuncSecondFuncReturn}} { 333 | return {{.FieldFuncReturn}} 334 | }) 335 | } 336 | {{end}} 337 | ` 338 | 339 | t, err := template.New("oneofPayload").Parse(tmpl) 340 | if err != nil { 341 | log.Fatal("Parse: ", err) 342 | panic(err) 343 | } 344 | 345 | return t 346 | } 347 | 348 | func getServiceStructInputFuncTemplate() *template.Template { 349 | 350 | tmpl := ` 351 | {{range .}} 352 | func RegisterInput{{.Name}}Input(schema *schemabuilder.Schema) { 353 | input := schema.InputObject("{{.Name}}Input", {{.Name}}Input{}) {{$name:=.Name}} 354 | {{range .Maps}} 355 | input.FieldFunc("{{.FieldName}}", func(target *{{$name}}Input, source *schemabuilder.Map) error { 356 | v := source.Value 357 | 358 | decodedValue, err := base64.StdEncoding.DecodeString(v) 359 | if err != nil { 360 | return err 361 | } 362 | 363 | data := make(map[{{.Key}}]{{.Value}}) 364 | if err := json.Unmarshal(decodedValue, &data); err != nil { 365 | return err 366 | } 367 | 368 | target.{{.TargetName}} = data 369 | return nil 370 | }){{end}} 371 | {{range .Fields}} 372 | input.FieldFunc("{{.FieldName}}", func(target *{{$name}}Input, source {{.FuncPara}}) { 373 | target{{"."}}{{.TargetName}} = {{.TargetVal}} 374 | }) 375 | {{end}} 376 | {{range .Durations}} 377 | input.FieldFunc("{{.Name}}", func(target *{{$name}}Input, source []*schemabuilder.Duration) { 378 | array := make([]*duration.Duration, 0 ,len(source)) 379 | for _, s:= range source{ 380 | array = append(array, (*duration.Duration)(s)) 381 | } 382 | target.{{.Name}} = array 383 | }){{end}} 384 | {{range .Ids}} 385 | input.FieldFunc("{{.FieldName}}", func(target *{{$name}}Input, source []schemabuilder.ID) { 386 | array:= make([]string,0,len(source)) 387 | for _, s:= range source{ 388 | array = append(array, (s.Value)) 389 | } 390 | target.{{.Name}}=array 391 | }){{end}} 392 | input.FieldFunc("clientMutationId", func(target *{{.Name}}Input, source string) { 393 | target.ClientMutationId = source 394 | }) 395 | } 396 | {{end}} 397 | ` 398 | 399 | t, err := template.New("inputMutationStruct").Parse(tmpl) 400 | if err != nil { 401 | log.Fatal("Parse: ", err) 402 | panic(err) 403 | } 404 | 405 | return t 406 | } 407 | 408 | func getServiceStructPayloadFuncTemplate() *template.Template { 409 | 410 | tmpl := ` 411 | {{range .}} 412 | func RegisterPayload{{.Name}}Payload(schema *schemabuilder.Schema) { 413 | payload := schema.Object("{{.Name}}Payload", {{.Name}}Payload{}){{$name:= .Name}} 414 | payload.FieldFunc("payload", func(ctx context.Context, in *{{.Name}}Payload) {{.ReturnType}} { 415 | return in.Payload 416 | }) 417 | payload.FieldFunc("clientMutationId", func(ctx context.Context, in *{{.Name}}Payload) string { 418 | return in.ClientMutationId 419 | }) 420 | } 421 | {{end}} 422 | ` 423 | 424 | t, err := template.New("inputMutationStruct").Parse(tmpl) 425 | if err != nil { 426 | log.Fatal("Parse: ", err) 427 | panic(err) 428 | } 429 | 430 | return t 431 | } 432 | --------------------------------------------------------------------------------