schemas.go 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973
  1. package got
  2. import (
  3. "fmt"
  4. . "git.eugeniocarvalho.dev/eugeniucarvalho/apicodegen/common" // . "git.eugeniocarvalho.dev/eugeniucarvalho/apicodegen/gen"
  5. G "github.com/dave/jennifer/jen"
  6. "regexp"
  7. "strings"
  8. "time" // "github.com/davecgh/go-spew/spew"
  9. )
  10. var (
  11. filled = map[string]*Entity{}
  12. numberRegex = regexp.MustCompile("(int|float).*")
  13. // bool
  14. // string
  15. // int int8 int16 int32 int64
  16. // uint uint8 uint16 uint32 uint64 uintptr
  17. // byte // alias for uint8
  18. // rune // alias for int32
  19. // // represents a Unicode code point
  20. // float32 float64
  21. // complex64 complex128
  22. primitiveRegex = regexp.MustCompile(`^(bool|string|u?int\d{0,2}|byte|rune|float\d{0,2}|interface|complex\d{2,3})$`)
  23. )
  24. func hasReplaceMethod(typ string) bool{
  25. typ = strings.Replace(typ, "*","", 1)
  26. return !primitiveRegex.MatchString(typ)
  27. }
  28. func replaceAccessType(typ string) (accessType string) {
  29. typ = strings.Replace(typ, "*","", 1)
  30. if strings.Contains(typ, "map"){
  31. accessType = "map"
  32. } else {
  33. accessType = "id"
  34. }
  35. return
  36. }
  37. func GenSchemas(p *Project) error {
  38. var (
  39. // requiredOnCreate bool
  40. hasAddOrRemove bool
  41. filename string
  42. inputName string
  43. typ string
  44. extName string
  45. f *G.File
  46. propertie *G.Statement
  47. extend *G.Statement
  48. base = "models/"
  49. posfix = ""
  50. properties G.Statement
  51. // values G.Dict
  52. cproperties map[string]*G.Statement
  53. addUpdate map[string]string
  54. replaceUpdate map[string]map[string]string
  55. removeUpdate map[string]map[string]string
  56. entityName string
  57. entityPatchName string
  58. propName string
  59. entityInfo *EntityInfo
  60. inputProperties G.Statement
  61. patchProperties G.Statement
  62. referenceProperties G.Statement
  63. entities, err = Schemas(p)
  64. // entities = p.Schemas
  65. // err error
  66. )
  67. if err != nil {
  68. return err
  69. }
  70. go GenModelIndex(p)
  71. if err = GenFilterEntityDocument(p); err != nil {
  72. return err
  73. }
  74. if err = GenEntityHelpers(p); err != nil {
  75. return err
  76. }
  77. for _, entity := range entities {
  78. entityInfo = p.ResponseEntity(entity.ID)
  79. if entityInfo.IsGeneric {
  80. continue
  81. }
  82. inputName = entity.ID
  83. entityPatchName = ""
  84. hasAddOrRemove = false
  85. // requiredOnCreate = false
  86. entityName = GenericPart.ReplaceAllString(entity.ID, "")
  87. filename = base + strings.ToLower(entityName)
  88. // f = G.NewFile(p.Package)
  89. f = G.NewFile("models")
  90. generateIndexsEntity(f, entity)
  91. // fmt.Println("Gerando entity: ", entityName)
  92. cproperties = map[string]*G.Statement{}
  93. // f.Comment(entity.Description)
  94. properties = G.Statement{}
  95. patchProperties = G.Statement{
  96. G.Qual(API_URL, "PatchHistoryRegister").Tag(map[string]string{
  97. "json": "-",
  98. "bson": ",inline",
  99. }),
  100. }
  101. entityModel := G.Qual(API_URL, "EntityModel").Tag(map[string]string{
  102. "json": "-",
  103. "bson": "-",
  104. })
  105. patchProperties = append(patchProperties, entityModel)
  106. // values = G.Dict{}
  107. // entityInfo = p.IsGenericEntity(entity.ID)
  108. inputProperties = G.Statement{
  109. // G.Qual(API_URL, "DotNotation").Tag(map[string]string{
  110. // "json": "-",
  111. // "bson": ",inline",
  112. // }),
  113. G.Id("DotNotation map[string]interface{}").Tag(map[string]string{
  114. "json": "-",
  115. "bson": ",inline",
  116. }),
  117. }
  118. // inputProperties = append(inputProperties, entityModel)
  119. referenceProperties = G.Statement{}
  120. addUpdate = map[string]string{}
  121. replaceUpdate = map[string]map[string]string{}
  122. removeUpdate = map[string]map[string]string{}
  123. for _, meta := range entity.Properties {
  124. // fmt.Println("Gerando ---------- ", entityName, "---------", propName, "----", meta.Array)
  125. if meta.Targets != "" && !strings.Contains(meta.Targets, "go") {
  126. continue
  127. }
  128. propName = strings.Title(meta.ID)
  129. propertie = G.Id(propName)
  130. meta.FillTags(p, propName)
  131. posfix = ""
  132. // Registra a relaao entre as entidades
  133. if meta.Relation {
  134. posfix = "Reference"
  135. SR.Add(&Relation{
  136. Source: meta.GetType(),
  137. Target: entity.ID,
  138. Attr: strings.Replace(meta.Tags["bson"], ",omitempty", "", 1),
  139. DB: entity.DB,
  140. Collection: entity.Collection,
  141. IsArray: meta.Array,
  142. })
  143. }
  144. typ = meta.Type + posfix
  145. if typ == "any" {
  146. typ = "interface{}"
  147. }
  148. if meta.Array {
  149. hasAddOrRemove = true
  150. propertie.Op("*")
  151. propertie.Index()
  152. if !meta.Readonly {
  153. extName = "Add" + propName
  154. addUpdate[extName] = meta.ID
  155. extend = G.Id(extName).Index().Id(ConvType(typ)).Tag(map[string]string{
  156. "json": extName + ",omitempty",
  157. "bson": extName + ",omitempty",
  158. })
  159. patchProperties = append(patchProperties, extend)
  160. if hasReplaceMethod(typ) {
  161. extName = "Replace" + propName
  162. replaceUpdate[extName] = map[string]string{
  163. "name": meta.ID,
  164. "type": replaceAccessType(typ),
  165. }
  166. extend = G.Id(extName).Index().Id(ConvType(typ)).Tag(map[string]string{
  167. "json": extName + ",omitempty",
  168. "bson": extName + ",omitempty",
  169. })
  170. patchProperties = append(patchProperties, extend)
  171. }
  172. extName = "Remove" + propName
  173. removeUpdate[extName] = map[string]string{
  174. "type": ConvType(typ),
  175. "propId": meta.ID,
  176. }
  177. extend = G.Id(extName).Index().Interface().Tag(map[string]string{
  178. "json": extName + ",omitempty",
  179. "bson": extName + ",omitempty",
  180. })
  181. patchProperties = append(patchProperties, extend)
  182. }
  183. }
  184. // propertie.Id(entityInfo.TranslateType(meta.Type) + posfix)
  185. if strings.Contains(typ, ".") {
  186. typt := strings.Split(typ, ".")
  187. propertie.Qual(ImportMap(typt[0]), ConvType(typt[1]))
  188. } else {
  189. propertie.Id(ConvType(typ))
  190. }
  191. entity.HasMode = true
  192. // Adiciona as tags caso sejam definidas
  193. if meta.Tags != nil {
  194. // if name, ok := meta.Tags["valid"]; ok && strings.Contains(name, "requiredOnCreate") {
  195. // entity.HasMode = true
  196. // }
  197. propertie.Tag(meta.Tags)
  198. // if name, ok := meta.Tags["json"]; ok {
  199. // // tsPropertieName = strings.Split(name, ",")[0]
  200. // }
  201. } else {
  202. // tsPropertieName = meta.ID
  203. }
  204. // Adiciona a crescricao como comentario
  205. // tsPropertie = &ts.Group{}
  206. if meta.Description != "" {
  207. propertie.Comment(meta.Description)
  208. // tsPropertie.Comment(meta.Description)
  209. }
  210. // if tsPropertieName != "" {
  211. // tsPropertie.Add(ts.Public().Id(tsPropertieName).Op(":").Id(ts.ConvType(meta.Type)))
  212. // if meta.Array {
  213. // tsPropertie.Add(ts.Index())
  214. // }
  215. // tsPropertie.Endl()
  216. // tsProperties = append(tsProperties, tsPropertie)
  217. // }
  218. cproperties[meta.ID] = propertie
  219. // Adiciona o valor padrao de inicializacao
  220. // spew.Dump(meta)
  221. // if tvalue, ok := meta.Autogenerate["create"]; ok {
  222. // switch tvalue {
  223. // case "objectId":
  224. // values[G.Id(propName)] = G.Qual(BSON_PRIMITIVE, "NewObjectID").Call()
  225. // case "now":
  226. // values[G.Id(propName)] = G.Qual("time", "Now").Call().Id(".Unix()")
  227. // case "time:now":
  228. // case "user:current":
  229. // default:
  230. // // Verifica se possui valor padrão
  231. // if meta.Default != nil {
  232. // values[G.Id(propName)] = G.Lit(meta.Default)
  233. // }
  234. // }
  235. // }
  236. // Adiciona as propriedades readonly na entidade principal
  237. // e as que tem permissao de escrita na entidade input
  238. if !meta.Readonly {
  239. inputProperties = append(inputProperties, propertie)
  240. } else {
  241. properties = append(properties, propertie)
  242. }
  243. // Adiciona a propriedade que é referencia a lista de propriedades
  244. // da entidade reference
  245. if meta.Reference {
  246. if propName == "Id" {
  247. propertie = G.Id("Id").Interface().Tag(map[string]string{
  248. "json": "_id",
  249. "bson": "_id",
  250. })
  251. }
  252. referenceProperties = append(referenceProperties, propertie)
  253. }
  254. }
  255. extend = G.Qual(API_URL, "EntityModel").Tag(map[string]string{
  256. "json": "-",
  257. "bson": ",inline",
  258. })
  259. inputProperties = append(G.Statement{extend}, inputProperties...)
  260. if len(inputProperties) > 0 {
  261. // if entity.HasMode {
  262. // values[G.Id("Mode")] = G.Qual(API_URL, "Mode").Values(G.Dict{
  263. // G.Id("M"): G.Lit("create"),
  264. // })
  265. // }
  266. if len(properties) == 0 {
  267. properties = inputProperties
  268. patchProperties = append(patchProperties, G.Id("Set").Op("*").Id(entityName).Tag(map[string]string{
  269. "json": "$set,omitempty",
  270. "bson": "$set,omitempty",
  271. }))
  272. } else {
  273. inputName = entity.ID + "Input"
  274. extend = G.Id(inputName).Tag(map[string]string{
  275. "json": ",inline",
  276. "bson": ",inline",
  277. })
  278. properties = append(G.Statement{extend}, properties...)
  279. f.Comment("Representação Input aplicada em operações de update.")
  280. f.Type().Id(inputName).Struct(inputProperties...)
  281. patchProperties = append(patchProperties, G.Id("Set").Op("*").Id(inputName).Tag(map[string]string{
  282. "json": "$set,omitempty",
  283. "bson": "-",
  284. }))
  285. }
  286. }
  287. if len(referenceProperties) > 0 {
  288. f.Comment("Representação reference aplicada quando a entidade é associada a outra.")
  289. f.Type().Id(entity.ID + "Reference").Struct(referenceProperties...)
  290. }
  291. // if entity.Representations != nil {
  292. // for k, rep := range entity.Representations {
  293. // tproperties = G.Statement{}
  294. // for _, attr := range rep {
  295. // tproperties = append(tproperties, cproperties[attr])
  296. // }
  297. // f.Comment("Representação " + k)
  298. // f.Type().Id(entity.ID + k).Struct(tproperties...)
  299. // }
  300. // }
  301. if len(patchProperties) > 1 {
  302. entityPatchName = fmt.Sprintf("%sPatchs", entityName)
  303. f.Comment(entity.Description).Line().Comment("Representação de atualização")
  304. // patchProperties = append(G.Statement{*entityModel}, patchProperties)
  305. f.Type().Id(entityPatchName).Struct(patchProperties...)
  306. }
  307. // Cria a entidade normal
  308. f.Comment(entity.Description).Line().Comment("Representação Completa")
  309. f.Type().Id(entityName).Struct(properties...)
  310. // Cria a entidade em typescript
  311. // tsModels.Line().Export().Class().Id(strings.Title(entity.ID)).Block(tsProperties...).Line()
  312. f.Comment("Cria uma instancia de " + entity.ID + ".")
  313. // Cria a função de instanciar um novo elemento com os valores padrão determinados
  314. f.Func().Id("New"+entityName).Params().Op("*").Id(entityName).Block(
  315. G.Id("entity").Op(":=").Op("&").Id(entityName).Values(),
  316. G.Do(func(x *G.Statement) {
  317. if entity.HasMode {
  318. x.Add(G.Id(`entity.SetMode("create")`))
  319. }
  320. }),
  321. G.Do(func(part *G.Statement) {
  322. // user := false
  323. for _, prop := range entity.Properties {
  324. typ := ConvType(prop.Type)
  325. isMap := strings.Contains(typ, "map")
  326. if prop.Relation {
  327. typ = typ + "Reference"
  328. }
  329. if def, ok := prop.Autogenerate["create"]; ok {
  330. switch def.Type {
  331. case "objectId":
  332. part.Add(G.Id("entity").Dot(strings.Title(prop.ID)).Op("=").Qual(BSON_PRIMITIVE, "NewObjectID()").Line())
  333. case "now":
  334. part.Add(G.Id("entity").Dot(strings.Title(prop.ID)).Op("=").Qual("time", "Now").Call().Id(".Unix()").Line())
  335. case "default":
  336. part.Add(G.Id("entity").Dot(strings.Title(prop.ID)).Op("=").Lit(prop.Default).Line())
  337. }
  338. } else if prop.Array {
  339. part.Add(G.Id("entity").Dot(strings.Title(prop.ID)).Op("= &").Index().Id(typ).Values().Line())
  340. } else if isMap {
  341. part.Add(G.Id("entity").Dot(strings.Title(prop.ID)).Op("=").Id(typ).Values().Line())
  342. }
  343. }
  344. }).Line(),
  345. G.Return(G.Id("entity")).Line(),
  346. )
  347. // update = bson.M{"$set": f.Entity}
  348. // if data, ok = f.Entity.Push(); ok {
  349. // update["$push"] = data
  350. // }
  351. // if data, ok = f.Entity.Pull(); ok {
  352. // update["$pull"] = data
  353. // }
  354. if entityPatchName != "" {
  355. f.Func().Params(
  356. G.Id("t").Op("*").Id(entityPatchName),
  357. ).Id("Patch").Params().Params(
  358. G.Op("*").Qual(BSON, "A"),
  359. G.Op("*").Qual("go.mongodb.org/mongo-driver/mongo/options", "UpdateOptions"),
  360. ).Block(
  361. G.Id("updt").Op(":=").Qual(BSON, "A").Values(),
  362. G.Id("patchOptions := ").Qual("go.mongodb.org/mongo-driver/mongo/options","Update").Call(),
  363. G.Do(func(y *G.Statement) {
  364. if !hasAddOrRemove {
  365. return
  366. }
  367. stmts := G.Statement{
  368. G.Id("hasAdd").Op(":=").Lit(0).Line(),
  369. G.Id("hasReplace").Op(":=").Lit(0).Line(),
  370. G.Id("hasRemove").Op(":=").Lit(0).Line(),
  371. G.Id("pull").Op(":=").Qual(BSON, "M").Values().Line(),
  372. G.Id("push").Op(":=").Qual(BSON, "M").Values().Line(),
  373. G.Id("dotNotation := map[string]interface{}{}").Line(),
  374. G.Id("arrayFilters := []interface{}{}").Line(),
  375. }
  376. // add entitys
  377. // spew.Dump(addUpdate)
  378. for prop, value := range addUpdate {
  379. stmts = append(stmts, G.If(G.Len(G.Id("t").Dot(prop)).Op(">").Lit(0)).Block(
  380. G.Id("hasAdd").Op("++"),
  381. G.Id("push").Index(G.Lit(value)).Op("=").Qual(BSON, "M").Values(G.Dict{
  382. G.Lit("$each"): G.Id("t").Dot(prop),
  383. }),
  384. ).Line())
  385. }
  386. // replace entitys
  387. // spew.Dump(addUpdate)
  388. for prop, value := range replaceUpdate {
  389. prop = fmt.Sprintf("t.%s", prop)
  390. stmts = append(stmts, G.If(G.Len(G.Id(prop)).Op(">").Lit(0)).Block(
  391. G.Id("hasReplace").Op("++"),
  392. G.Do(func(replace *G.Statement) {
  393. name := value["name"]
  394. typi := value["type"]
  395. arrayFiltersTemplates := map[string]string{
  396. "id": `arrayFilters = append(arrayFilters, bson.M{
  397. api.Format("%s._id", key): value.Id,
  398. })`,
  399. "map": `arrayFilters = append(arrayFilters, bson.M{
  400. api.Format("%s._id", key): value["_id"],
  401. })`,
  402. }
  403. arrayFilters := arrayFiltersTemplates[typi]
  404. template := fmt.Sprintf(`for idx, value := range %s {
  405. key := api.Format("%s%%d", idx)
  406. dotNotation[api.Format("%s.$[%%s]", key)] = value
  407. %s
  408. }`,
  409. prop,
  410. name,
  411. name,
  412. arrayFilters,
  413. )
  414. replace.Add(G.Id(template))
  415. }),
  416. ).Line())
  417. }
  418. // remove entitys
  419. for prop, value := range removeUpdate {
  420. stmts = append(stmts, G.If(G.Len(G.Id("t").Dot(prop)).Op(">").Lit(0)).Block(
  421. G.Id("hasRemove").Op("++"),
  422. G.Do(func(hasRemove *G.Statement) {
  423. var assign G.Dict
  424. // Se o array for de um atributo de tipo primitivo
  425. if primitiveRegex.MatchString(value["type"]) {
  426. assign = G.Dict{
  427. G.Lit("$in"): G.Id("t").Dot(prop),
  428. }
  429. } else {
  430. assign = G.Dict{
  431. G.Lit("_id"): G.Qual(BSON, "M").Values(G.Dict{
  432. G.Lit("$in"): G.Id("t").Dot(prop),
  433. }),
  434. }
  435. }
  436. hasRemove.Id("pull").Index(G.Lit(value["propId"])).Op("=").Qual(BSON, "M").Values(assign)
  437. }),
  438. ).Line())
  439. }
  440. // fmt.Println("Remove:", inputName, prop, value)
  441. // fmt.Printf("%#v", G.If(G.Len(G.Id("t").Dot(prop)).Op(">").Lit(0)).Block(
  442. // G.Id("hasRemove").Op("++"),
  443. // G.Id("pull").Index(G.Lit(value)).Op("=").Qual(BSON, "M").Values(G.Dict{
  444. // G.Lit("$in"): G.Id("t").Dot(prop),
  445. // }),
  446. // ).Line())
  447. // x.Add(G.If(G.Len(G.Id("t").Dot(prop)).Op(">").Lit(0)).Block(
  448. // G.Id("hasRemove").Op("++"),
  449. // G.Id("pull").Index(G.Lit(value)).Op("=").Qual(BSON, "M").Values(G.Dict{
  450. // G.Lit("$in"): G.Id("t").Dot(prop),
  451. // }),
  452. // ).Line())
  453. stmts = append(stmts, G.If(G.Id("hasAdd").Op(">").Lit(0)).Block(
  454. // G.Id("updt").Index(G.Lit("$push")).Op("=").Id("push"),
  455. G.Id("updt").Op("=").Append(
  456. G.Id("updt"),
  457. G.Qual(BSON, "M").Values(G.Lit("$push").Id(": push")),
  458. ),
  459. // .Index(G.Lit("$push")).Op("=").Id("push"),
  460. ).Line())
  461. stmts = append(stmts, G.If(G.Id("hasRemove").Op(">").Lit(0)).Block(
  462. // G.Id("updt").Index(G.Lit("$pull")).Op("=").Id("pull"),
  463. G.Id("updt").Op("=").Append(
  464. G.Id("updt"),
  465. G.Qual(BSON, "M").Values(G.Lit("$pull").Id(": pull")),
  466. ),
  467. ).Line())
  468. stmts = append(stmts, G.If(G.Id("hasReplace").Op(">").Lit(0)).Block(
  469. G.Id(fmt.Sprintf(`if t.Set == nil {
  470. t.Set = &%s{}
  471. }
  472. t.Set.DotNotation = dotNotation
  473. patchOptions.SetArrayFilters(options.ArrayFilters{
  474. Filters: arrayFilters,
  475. })
  476. `,
  477. inputName,
  478. )),
  479. ).Line())
  480. y.Add(stmts...)
  481. }),
  482. G.Id(`if t.Set != nil {
  483. updt = append(updt, bson.M{"$set": t.Set})
  484. }`).Line(),
  485. // []interface{}{bson.D{
  486. // {"elem._id", 1},
  487. // }}
  488. G.Return(
  489. G.Op("&").Id("updt"),
  490. G.Id("patchOptions"),
  491. ),
  492. )
  493. }
  494. // Salva o arquivo da entidade
  495. // if err := f.Save(fmt.Sprintf("%s/%s/%s_gen.go", p.OutPath, p.Package, filename)); err != nil {
  496. if err := Write(fmt.Sprintf("%s/%s/%s_gen.go", p.OutPath, p.Package, filename), f); err != nil {
  497. return err
  498. }
  499. } // Fim do for de schema
  500. return nil
  501. }
  502. func ConvType(ntype string) string {
  503. if len(ntype) < 3 {
  504. fmt.Printf("Invalid type name '%s':\n", ntype)
  505. }
  506. if ntype[0:3] == "map" {
  507. parts := strings.Split(ntype, "|")
  508. ntype = fmt.Sprintf("map[%s]%s", parts[1], parts[2])
  509. }
  510. return ntype
  511. }
  512. func GenModelIndex(p *Project) error {
  513. var (
  514. // Index = G.NewFile(p.Package)
  515. Index = G.NewFile("models")
  516. )
  517. Index.Id(`type Entity struct {}`).Line()
  518. Index.Comment("")
  519. Index.Var().Defs(
  520. G.Id("Api").Op("=").Op("&").Qual(API_URL, "Mongo").Values(),
  521. ).Line()
  522. return Write(fmt.Sprintf("%s/%s/models/index_gen.go", p.OutPath, p.Package), Index)
  523. }
  524. func fill(p *Project, schema *Entity) (*Entity, error) {
  525. var (
  526. // found, ok bool
  527. found bool
  528. cls *Entity
  529. // newSchema = &(*schema)
  530. err error
  531. )
  532. // Se o esquema ainda não esta completo com as classes extendidas caso possua
  533. if _, found = filled[schema.ID]; !found {
  534. // fEntity = schema
  535. //
  536. // fmt.Println("Fill ............ ", schema.ID)
  537. // fmt.Println("Nao estava cheio ", schema.ID)
  538. for _, entity := range schema.Extends {
  539. if cls, err = fill(p, p.GetSchema(entity)); err != nil {
  540. return nil, err
  541. }
  542. // fmt.Println("extends ", entity)
  543. // if cls, ok = filled[entity]; !ok {
  544. // cls = p.GetSchema(entity)
  545. // // fmt.Println("Não tava cheio", entity)
  546. // // fmt.Println("fill:", schema)
  547. // // fmt.Println(cls)
  548. // if err = fill(p, cls); err != nil {
  549. // return err
  550. // }
  551. // }
  552. // else {
  553. // fmt.Println("estava cheio ", entity)
  554. // fmt.Println("Merge ", schema.ID, cls.ID, ok)
  555. schema.Properties = append(schema.Properties, cls.Properties...)
  556. }
  557. // for _, x := range schema.Properties {
  558. // fmt.Println("fill----------------", schema.ID, x.ID, len(schema.Properties))
  559. // }
  560. filled[schema.ID] = schema
  561. }
  562. return filled[schema.ID], nil
  563. }
  564. func Schemas(p *Project) ([]*Entity, error) {
  565. var (
  566. err error
  567. entities = []*Entity{}
  568. )
  569. for _, schema := range p.Schemas {
  570. if schema, err = fill(p, schema); err != nil {
  571. return nil, err
  572. }
  573. if schema.Type != "abstract" {
  574. entities = append(entities, schema)
  575. }
  576. // for _, x := range schema.Properties {
  577. // fmt.Printf("%s , %s, %p\n", schema.ID, x.ID, x)
  578. // }
  579. }
  580. // for _, s := range entities {
  581. // for _, x := range s.Properties {
  582. // fmt.Printf("final-schemas....%s , %s, %p\n", s.ID, x.ID, x)
  583. // }
  584. // }
  585. return entities, nil
  586. }
  587. func GenFilterEntityDocument(p *Project) error {
  588. var (
  589. content string
  590. path string
  591. err error
  592. )
  593. for _, s := range p.Schemas {
  594. if s.Type == "nested.object" {
  595. continue
  596. }
  597. // fmt.Println("gerando filtro ", s.ID)
  598. filter := &ApiFilter{
  599. Id: strings.ToLower(s.ID),
  600. Date: time.Now().Unix(),
  601. }
  602. for _, attr := range s.Properties {
  603. if attr.Filter == nil {
  604. continue
  605. }
  606. // Atualiza o tipo do filtro na estrutura do campo
  607. for _, filterItem := range attr.Filter {
  608. FilterTypeMap(attr, filterItem)
  609. // Atualiza o path da query
  610. if filterItem.Type == "nested" {
  611. FilterPath(filter, p, attr, attr.ID)
  612. } else {
  613. if filterItem.Path == "" {
  614. filterItem.Path = attr.ID
  615. }
  616. if filterItem.UserEnumAsOptions {
  617. filterItem.Options = []FilterOption{}
  618. // if len(attr.Values) == 0 {
  619. // attr.Values = make([]string, len(attr.Enum))
  620. // }
  621. // values = make()
  622. // }
  623. for key, value := range attr.Enum {
  624. filterItem.Options = append(filterItem.Options, FilterOption{
  625. Value: attr.Values[key],
  626. Label: value,
  627. })
  628. }
  629. }
  630. filter.Fields = append(filter.Fields, filterItem)
  631. }
  632. }
  633. }
  634. if filter.Fields == nil {
  635. continue
  636. }
  637. if content, err = JSONStringfy(filter); err != nil {
  638. return err
  639. }
  640. path = fmt.Sprintf("%s/filters/%s.json", p.OutPath, strings.ToLower(s.ID))
  641. // fmt.Printf("storing filter '%s'\n",path)
  642. if err = FilePutContents(path, content, 0777); err != nil {
  643. return err
  644. }
  645. }
  646. return nil
  647. }
  648. func FilterPath(filter *ApiFilter, p *Project, attr *Propertie, path string) {
  649. var (
  650. nfil Filter
  651. npath string
  652. typ = attr.Type
  653. )
  654. if attr.Relation {
  655. typ += "Reference"
  656. }
  657. entity := p.GetSchema(typ)
  658. for _, x := range entity.Properties {
  659. if x.Filter == nil {
  660. continue
  661. }
  662. npath = fmt.Sprintf("%s.%s", path, x.ID)
  663. for _, item := range x.Filter {
  664. if item.Type != "nested" {
  665. nfil = *item
  666. if nfil.Path == "" {
  667. nfil.Path = npath
  668. }
  669. FilterTypeMap(x, item)
  670. filter.Fields = append(filter.Fields, &nfil)
  671. } else {
  672. FilterPath(filter, p, x, npath)
  673. }
  674. }
  675. }
  676. }
  677. func FilterTypeMap(attr *Propertie, f *Filter) {
  678. typ := f.Type
  679. if typ != "" {
  680. return
  681. }
  682. switch {
  683. // Se o tipo for numerico
  684. case numberRegex.Match([]byte(typ)):
  685. f.Type = "number"
  686. default:
  687. f.Type = attr.Type
  688. }
  689. }
  690. func generateIndexsEntity(file *G.File, entity *Entity) {
  691. // keys := G.{}
  692. // options := G.Dict{
  693. // G.Id("Unique"): G.Lit(true),
  694. // // G.Id("Key"): G.Lit("unique"),
  695. // // G.Id("Value"): G.Lit(true),
  696. // }
  697. var create = false
  698. indexOptions := G.Dict{
  699. G.Id("Keys"): G.Qual(BSONX, "Doc").ValuesFunc(func(keys *G.Group) {
  700. for _, propertie := range entity.Properties {
  701. if propertie.Unique {
  702. keys.Add(G.Values(G.Lit(propertie.ID), G.Qual(BSONX, "Int32").Call(G.Lit(1))))
  703. create = true
  704. // keys[G.Lit(propertie.ID)] = G.Lit(1)
  705. }
  706. }
  707. }),
  708. // G.Id("Options"): G.Qual(BSON, "M").Values(options),
  709. G.Id("Options"): G.Id("opts.SetUnique(true)"),
  710. }
  711. if create {
  712. file.Line().Func().Id("init").Call().Block(
  713. G.Id("models.Api.Ready().Subscribe").Call(
  714. G.Func().Call(G.Id("value").Id("...interface{}")).Block(
  715. G.Id("opts := &").Qual("go.mongodb.org/mongo-driver/mongo/options", "IndexOptions").Values(),
  716. G.Id(`index := `).Qual("go.mongodb.org/mongo-driver/mongo", "IndexModel").Values(indexOptions),
  717. // G.Qual("fmt", "Println").Call(G.Lit("create index for "), G.Lit(entity.ID)),
  718. G.Id(`if err := models.Api.CreateIndex(`).Lit(entity.DB).Id(`, `).Lit(entity.Collection).Id(`, index); err != nil`).
  719. Block(
  720. G.Id("panic").Call(
  721. G.Qual("fmt", "Sprintf").Call(
  722. // G.Lit(fmt.Sprintf("Index %s creation error %s", entity.ID, err.Error())),
  723. G.Id(`"Index`).Id(entity.ID).Id(`creation error %s", err.Error()`),
  724. ),
  725. ).Line(),
  726. // (`).
  727. // Call().
  728. // Line().
  729. ),
  730. ),
  731. ),
  732. )
  733. // .BlockFunc(func(statement *G.Group) {
  734. // statement.Add(
  735. // ,
  736. // )
  737. // statement.Add(G.Line().Id("})"))
  738. // })
  739. }
  740. }
  741. func GenEntityHelpers(project *Project) error {
  742. file := G.NewFile("models")
  743. entities, err := Schemas(project)
  744. if err != nil {
  745. return err
  746. }
  747. file.Add(G.Id(`
  748. import(
  749. "reflect"
  750. "git.eugeniocarvalho.dev/eugeniucarvalho/apicodegen/api/errs"
  751. context "github.com/kataras/iris/v12/context"
  752. "go.mongodb.org/mongo-driver/bson"
  753. "go.mongodb.org/mongo-driver/bson/primitive"
  754. )
  755. `))
  756. getStmtsTmpl, getStmtsErr := ParseTemplate(`
  757. func Get{{.entity}}ByID(
  758. ctx context.Context,
  759. id interface{},
  760. ) (
  761. entity *{{.entity}},
  762. err *errs.Error,
  763. ) {
  764. var errd error
  765. entity = &{{.entity}}{}
  766. value := reflect.ValueOf(id)
  767. if value.Kind() == reflect.Ptr {
  768. id = value.Elem()
  769. }
  770. if idString, ok := id.(string); ok {
  771. if id, errd = primitive.ObjectIDFromHex(idString); errd != nil {
  772. return
  773. }
  774. }
  775. options := Filter{{.entity}}Options(ctx)
  776. options.Query = &bson.M{"_id": id}
  777. options.Entity = entity
  778. if _, err = Api.FindOne(options); err != nil {
  779. return
  780. }
  781. return
  782. }
  783. `)
  784. if getStmtsErr != nil {
  785. return getStmtsErr
  786. }
  787. for _, entity := range entities {
  788. entityInfo := project.ResponseEntity(entity.ID)
  789. if entityInfo.IsGeneric || entity.Type != "object" {
  790. continue
  791. }
  792. context := map[string]interface{}{
  793. "entity": strings.Title(entity.ID),
  794. }
  795. out, _ := TemplateToString(getStmtsTmpl, context)
  796. file.Add(G.Id(out).Line())
  797. }
  798. if err := Write(
  799. fmt.Sprintf("../project/include/go/models/get_helpers.go"),
  800. file,
  801. ); err != nil {
  802. return err
  803. }
  804. return nil
  805. }