serv_http.go 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. // package mod_http -- http-модуль для архитектуры
  2. package mod_http
  3. import (
  4. "bytes"
  5. _ "embed"
  6. "encoding/json"
  7. "fmt"
  8. "log"
  9. "time"
  10. svg "github.com/ajstarks/svgo"
  11. "github.com/gofiber/fiber/v2"
  12. "gitp78su.ipnodns.ru/svi/goarch/lev0/alias"
  13. "gitp78su.ipnodns.ru/svi/goarch/lev0/cons"
  14. "gitp78su.ipnodns.ru/svi/goarch/lev0/types"
  15. "gitp78su.ipnodns.ru/svi/goarch/pkg/elems/canvas"
  16. "gitp78su.ipnodns.ru/svi/goarch/pkg/views/view_actor"
  17. "gitp78su.ipnodns.ru/svi/goarch/pkg/views/view_link"
  18. "gitp78su.ipnodns.ru/svi/goarch/pkg/views/view_use_case"
  19. "gitp78su.ipnodns.ru/svi/goarch/pkg/views/view_use_group"
  20. "gitp78su.ipnodns.ru/svi/kern/v3"
  21. "gitp78su.ipnodns.ru/svi/kern/v3/krn/ktypes"
  22. )
  23. //go:embed index.html
  24. var indexHtml []byte
  25. // ModHttp -- http-модуль для архитектуры
  26. type ModHttp struct {
  27. // fibApp *fiber.App
  28. kCtx ktypes.IKernelCtx
  29. diaMode string // Режим диаграммы
  30. oldBinArch []byte // Кеш старой архитектуры
  31. }
  32. // NewModHttp -- возвращает новый сервис
  33. func NewModHttp() *ModHttp {
  34. sf := &ModHttp{
  35. kCtx: kern.GetKernelCtx(),
  36. diaMode: cons.ModeUseCase,
  37. }
  38. sHttp := kern.GetKernelServerHttp()
  39. fibApp := sHttp.Fiber()
  40. fibApp.Static("/static", "./static", fiber.Static{
  41. Compress: true,
  42. Browse: true,
  43. CacheDuration: time.Second * 10,
  44. })
  45. fibApp.Get("/", sf.indexGet)
  46. fibApp.Get("/use_case", sf.setModeUseCase)
  47. fibApp.Post("/arch", sf.archPost)
  48. return sf
  49. }
  50. // FormArch -- извлекает данные из текстового поля для обновления
  51. type FormArch struct {
  52. StrData string `form:"text"`
  53. }
  54. // Возвращает новый холст
  55. func (sf *ModHttp) canvas(wr *bytes.Buffer, lstElem []map[string]interface{}) (*svg.SVG, error) {
  56. var (
  57. elem map[string]interface{}
  58. isFind, isOk bool
  59. _section interface{}
  60. )
  61. for _, elem = range lstElem {
  62. _section, isOk = elem["type"]
  63. if !isOk {
  64. continue
  65. }
  66. section, isOk := _section.(string)
  67. if !isOk {
  68. return nil, fmt.Errorf("ServHttp.canvas(): in mapElem, type(%#+v) not string<br>%#+v", _section, elem)
  69. }
  70. if section == cons.SectionConfig {
  71. isFind = true
  72. break
  73. }
  74. }
  75. if !isFind {
  76. return nil, fmt.Errorf("ServHttp.canvas(): in mapElem, config not found")
  77. }
  78. confCanvas := canvas.NewCanvas(sf.diaMode, elem)
  79. sizeX, sizeY := confCanvas.Size()
  80. canvas := svg.New(wr)
  81. canvas.Start(int(sizeX), int(sizeY))
  82. confCanvas.Draw(canvas)
  83. return canvas, nil
  84. }
  85. // По запросы парсит, что получилось -- обновляет архитектуру
  86. func (sf *ModHttp) archPost(ctx *fiber.Ctx) error {
  87. form := &FormArch{}
  88. if err := ctx.BodyParser(form); err != nil {
  89. return ctx.SendString(fmt.Sprintf("ServHttp.archPost(): in BodyParser, err=<br>%v", err))
  90. }
  91. binData := []byte(form.StrData)
  92. if len(binData) == len(sf.oldBinArch) {
  93. return ctx.Send(sf.oldBinArch)
  94. }
  95. mapElem := []map[string]interface{}{}
  96. err := json.Unmarshal(binData, &mapElem)
  97. if err != nil {
  98. return ctx.SendString(fmt.Sprintf("ServHttp.archPost(): in Decode JSON, err=<br>%v", err))
  99. }
  100. wr := bytes.NewBuffer(nil)
  101. canvas, err := sf.canvas(wr, mapElem)
  102. if err != nil {
  103. return ctx.SendString(fmt.Sprintf("ServHttp.archPost(): in create canvas, err=<br>%v", err))
  104. }
  105. switch sf.diaMode {
  106. case cons.ModeUseCase: // режим вариантов использования
  107. if err := sf.useCase(canvas, mapElem); err != nil {
  108. return ctx.SendString(fmt.Sprintf("ServHttp.archPost(): in useCase, err=<br>%v", err))
  109. }
  110. default: // неизвестный режим
  111. return ctx.SendString(fmt.Sprintf("ServHttp.archPost(): unknown mode=%q", sf.diaMode))
  112. }
  113. canvas.End()
  114. binData = wr.Bytes()
  115. return ctx.Send(binData)
  116. }
  117. // Формирует варианты использования
  118. func (sf *ModHttp) useCase(canvas *svg.SVG, lstElem []map[string]interface{}) error {
  119. mapDraw := make(map[alias.Id]types.IElemDrawer)
  120. for _, elem := range lstElem {
  121. _id, isOk := elem["id"]
  122. if !isOk {
  123. return fmt.Errorf("ServHttp.useCase(): field 'id' not found<br>%#+v", elem)
  124. }
  125. id0, isOk := _id.(string)
  126. if !isOk {
  127. return fmt.Errorf("ServHttp.useCase(): field 'id'(%T, %+v) not alias.Id", _id, _id)
  128. }
  129. id := alias.Id(id0)
  130. if id == "" {
  131. return fmt.Errorf("ServHttp.useCase(): id=%q, elem is empty<br>%#+v", id, elem)
  132. }
  133. _, isOk = mapDraw[id]
  134. if isOk {
  135. return fmt.Errorf("ServHttp.useCase(): id=%q already exists", id)
  136. }
  137. binElem, _ := json.MarshalIndent(elem, "", " ")
  138. _type, isOk := elem["type"]
  139. if !isOk {
  140. return fmt.Errorf("ServHttp.useCase(): id=%q, field 'type' not found<br>%+v", id, string(binElem))
  141. }
  142. strType, isOk := _type.(string)
  143. if !isOk {
  144. return fmt.Errorf("ServHttp.useCase(): id=%q, field 'type'(%+v) not string<br>%+v", id, _type, string(binElem))
  145. }
  146. var (
  147. drawer types.IElemDrawer
  148. err error
  149. )
  150. switch strType {
  151. case cons.TypeUseActor: // Нарисовать актора
  152. drawer, err = view_actor.NewViewActor(elem)
  153. if err != nil {
  154. return fmt.Errorf("ServHttp.useCase(): id=%q, in create Actor, err=<br>%v", id, err)
  155. }
  156. case cons.TypeUseCase: // Нарисовать вариант использования
  157. drawer, err = view_use_case.NewViewUseCase(elem)
  158. if err != nil {
  159. return fmt.Errorf("ServHttp.useCase(): id=%q, err=%w<br>%+v", id, err, string(binElem))
  160. }
  161. case cons.TypeUseLink: // Нарисовать связь
  162. drawer, err = view_link.NewViewLink(elem)
  163. if err != nil {
  164. return fmt.Errorf("ServHttp.useCase(): id=%q, in create Link, err=%w<br>%+v", id, err, string(binElem))
  165. }
  166. case cons.TypeUseGroup: // Нарисовать группу вариантов использования
  167. drawer, err = view_use_group.NewViewUseGroup(elem)
  168. if err != nil {
  169. return fmt.Errorf("ServHttp.useCase(): id=%q, in create UseGroup, err=%w<br>%+v", id, err, string(binElem))
  170. }
  171. }
  172. if drawer != nil {
  173. mapDraw[id] = drawer
  174. log.Printf("ServHttp.useCase(): id=%q, elem=\n%v\n\n", id, elem)
  175. }
  176. }
  177. for _, drawer := range mapDraw {
  178. switch drawer.(type) {
  179. case types.ILinker:
  180. }
  181. drawer.Draw(canvas, mapDraw)
  182. }
  183. return nil
  184. }
  185. // Возвращает картинку архитектуры
  186. func (sf *ModHttp) setModeUseCase(ctx *fiber.Ctx) error {
  187. sf.diaMode = cons.ModeUseCase
  188. return ctx.SendString("[mode='" + sf.diaMode + "']")
  189. }
  190. // Возвращает главную страницу сервиса
  191. func (sf *ModHttp) indexGet(ctx *fiber.Ctx) error {
  192. ctx.Response().Header.Set("Content-Type", "text/html")
  193. return ctx.Send(indexHtml)
  194. }