| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207 |
- // package mod_http -- http-модуль для архитектуры
- package mod_http
- import (
- "bytes"
- _ "embed"
- "encoding/json"
- "fmt"
- "log"
- "time"
- svg "github.com/ajstarks/svgo"
- "github.com/gofiber/fiber/v2"
- "gitp78su.ipnodns.ru/svi/goarch/lev0/alias"
- "gitp78su.ipnodns.ru/svi/goarch/lev0/cons"
- "gitp78su.ipnodns.ru/svi/goarch/lev0/types"
- "gitp78su.ipnodns.ru/svi/goarch/pkg/elems/canvas"
- "gitp78su.ipnodns.ru/svi/goarch/pkg/views/view_actor"
- "gitp78su.ipnodns.ru/svi/goarch/pkg/views/view_link"
- "gitp78su.ipnodns.ru/svi/goarch/pkg/views/view_use_case"
- "gitp78su.ipnodns.ru/svi/goarch/pkg/views/view_use_group"
- "gitp78su.ipnodns.ru/svi/kern/v3"
- "gitp78su.ipnodns.ru/svi/kern/v3/krn/ktypes"
- )
- //go:embed index.html
- var indexHtml []byte
- // ModHttp -- http-модуль для архитектуры
- type ModHttp struct {
- // fibApp *fiber.App
- kCtx ktypes.IKernelCtx
- diaMode string // Режим диаграммы
- oldBinArch []byte // Кеш старой архитектуры
- }
- // NewModHttp -- возвращает новый сервис
- func NewModHttp() *ModHttp {
- sf := &ModHttp{
- kCtx: kern.GetKernelCtx(),
- diaMode: cons.ModeUseCase,
- }
- sHttp := kern.GetKernelServerHttp()
- fibApp := sHttp.Fiber()
- fibApp.Static("/static", "./static", fiber.Static{
- Compress: true,
- Browse: true,
- CacheDuration: time.Second * 10,
- })
- fibApp.Get("/", sf.indexGet)
- fibApp.Get("/use_case", sf.setModeUseCase)
- fibApp.Post("/arch", sf.archPost)
- return sf
- }
- // FormArch -- извлекает данные из текстового поля для обновления
- type FormArch struct {
- StrData string `form:"text"`
- }
- // Возвращает новый холст
- func (sf *ModHttp) canvas(wr *bytes.Buffer, lstElem []map[string]interface{}) (*svg.SVG, error) {
- var (
- elem map[string]interface{}
- isFind, isOk bool
- _section interface{}
- )
- for _, elem = range lstElem {
- _section, isOk = elem["type"]
- if !isOk {
- continue
- }
- section, isOk := _section.(string)
- if !isOk {
- return nil, fmt.Errorf("ServHttp.canvas(): in mapElem, type(%#+v) not string<br>%#+v", _section, elem)
- }
- if section == cons.SectionConfig {
- isFind = true
- break
- }
- }
- if !isFind {
- return nil, fmt.Errorf("ServHttp.canvas(): in mapElem, config not found")
- }
- confCanvas := canvas.NewCanvas(sf.diaMode, elem)
- sizeX, sizeY := confCanvas.Size()
- canvas := svg.New(wr)
- canvas.Start(int(sizeX), int(sizeY))
- confCanvas.Draw(canvas)
- return canvas, nil
- }
- // По запросы парсит, что получилось -- обновляет архитектуру
- func (sf *ModHttp) archPost(ctx *fiber.Ctx) error {
- form := &FormArch{}
- if err := ctx.BodyParser(form); err != nil {
- return ctx.SendString(fmt.Sprintf("ServHttp.archPost(): in BodyParser, err=<br>%v", err))
- }
- binData := []byte(form.StrData)
- if len(binData) == len(sf.oldBinArch) {
- return ctx.Send(sf.oldBinArch)
- }
- mapElem := []map[string]interface{}{}
- err := json.Unmarshal(binData, &mapElem)
- if err != nil {
- return ctx.SendString(fmt.Sprintf("ServHttp.archPost(): in Decode JSON, err=<br>%v", err))
- }
- wr := bytes.NewBuffer(nil)
- canvas, err := sf.canvas(wr, mapElem)
- if err != nil {
- return ctx.SendString(fmt.Sprintf("ServHttp.archPost(): in create canvas, err=<br>%v", err))
- }
- switch sf.diaMode {
- case cons.ModeUseCase: // режим вариантов использования
- if err := sf.useCase(canvas, mapElem); err != nil {
- return ctx.SendString(fmt.Sprintf("ServHttp.archPost(): in useCase, err=<br>%v", err))
- }
- default: // неизвестный режим
- return ctx.SendString(fmt.Sprintf("ServHttp.archPost(): unknown mode=%q", sf.diaMode))
- }
- canvas.End()
- binData = wr.Bytes()
- return ctx.Send(binData)
- }
- // Формирует варианты использования
- func (sf *ModHttp) useCase(canvas *svg.SVG, lstElem []map[string]interface{}) error {
- mapDraw := make(map[alias.Id]types.IElemDrawer)
- for _, elem := range lstElem {
- _id, isOk := elem["id"]
- if !isOk {
- return fmt.Errorf("ServHttp.useCase(): field 'id' not found<br>%#+v", elem)
- }
- id0, isOk := _id.(string)
- if !isOk {
- return fmt.Errorf("ServHttp.useCase(): field 'id'(%T, %+v) not alias.Id", _id, _id)
- }
- id := alias.Id(id0)
- if id == "" {
- return fmt.Errorf("ServHttp.useCase(): id=%q, elem is empty<br>%#+v", id, elem)
- }
- _, isOk = mapDraw[id]
- if isOk {
- return fmt.Errorf("ServHttp.useCase(): id=%q already exists", id)
- }
- binElem, _ := json.MarshalIndent(elem, "", " ")
- _type, isOk := elem["type"]
- if !isOk {
- return fmt.Errorf("ServHttp.useCase(): id=%q, field 'type' not found<br>%+v", id, string(binElem))
- }
- strType, isOk := _type.(string)
- if !isOk {
- return fmt.Errorf("ServHttp.useCase(): id=%q, field 'type'(%+v) not string<br>%+v", id, _type, string(binElem))
- }
- var (
- drawer types.IElemDrawer
- err error
- )
- switch strType {
- case cons.TypeUseActor: // Нарисовать актора
- drawer, err = view_actor.NewViewActor(elem)
- if err != nil {
- return fmt.Errorf("ServHttp.useCase(): id=%q, in create Actor, err=<br>%v", id, err)
- }
- case cons.TypeUseCase: // Нарисовать вариант использования
- drawer, err = view_use_case.NewViewUseCase(elem)
- if err != nil {
- return fmt.Errorf("ServHttp.useCase(): id=%q, err=%w<br>%+v", id, err, string(binElem))
- }
- case cons.TypeUseLink: // Нарисовать связь
- drawer, err = view_link.NewViewLink(elem)
- if err != nil {
- return fmt.Errorf("ServHttp.useCase(): id=%q, in create Link, err=%w<br>%+v", id, err, string(binElem))
- }
- case cons.TypeUseGroup: // Нарисовать группу вариантов использования
- drawer, err = view_use_group.NewViewUseGroup(elem)
- if err != nil {
- return fmt.Errorf("ServHttp.useCase(): id=%q, in create UseGroup, err=%w<br>%+v", id, err, string(binElem))
- }
- }
- if drawer != nil {
- mapDraw[id] = drawer
- log.Printf("ServHttp.useCase(): id=%q, elem=\n%v\n\n", id, elem)
- }
- }
- for _, drawer := range mapDraw {
- switch drawer.(type) {
- case types.ILinker:
- }
- drawer.Draw(canvas, mapDraw)
- }
- return nil
- }
- // Возвращает картинку архитектуры
- func (sf *ModHttp) setModeUseCase(ctx *fiber.Ctx) error {
- sf.diaMode = cons.ModeUseCase
- return ctx.SendString("[mode='" + sf.diaMode + "']")
- }
- // Возвращает главную страницу сервиса
- func (sf *ModHttp) indexGet(ctx *fiber.Ctx) error {
- ctx.Response().Header.Set("Content-Type", "text/html")
- return ctx.Send(indexHtml)
- }
|