| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233 |
- // package mod_http -- http-модуль для архитектуры
- package mod_http
- import (
- "bytes"
- _ "embed"
- "encoding/json"
- "fmt"
- "time"
- svg "github.com/ajstarks/svgo"
- "github.com/gofiber/fiber/v2"
- "gopkg.in/yaml.v3"
- "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/lev1/coord"
- "gitp78su.ipnodns.ru/svi/goarch/lev1/size"
- "gitp78su.ipnodns.ru/svi/goarch/lev1/view_use_case"
- "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_group"
- "gitp78su.ipnodns.ru/svi/kern/v3"
- "gitp78su.ipnodns.ru/svi/kern/v3/kc/log_buf"
- "gitp78su.ipnodns.ru/svi/kern/v3/krn/ktypes"
- )
- //go:embed index.html
- var indexHtml []byte
- // CanvasSize -- размер хоста
- type CanvasSize struct {
- SizeX_ int `form:"w"`
- SizeY_ int `form:"h"`
- }
- func (sf *CanvasSize) String() string {
- return fmt.Sprintf("w:%v; h:%v", sf.SizeX_, sf.SizeY_)
- }
- // ModHttp -- http-модуль для архитектуры
- type ModHttp struct {
- // fibApp *fiber.App
- kCtx ktypes.IKernelCtx
- log ktypes.ILogBuf
- diaMode string // Режим диаграммы
- oldBinArch []byte // Кеш старой архитектуры
- canvSize CanvasSize // Размер холста
- size types.ISize // Размер холста в его координатах
- pos types.ICoord // Координаты холста
- }
- // NewModHttp -- возвращает новый сервис
- func NewModHttp() *ModHttp {
- optTerm := log_buf.OptIsTerm(true)
- optPref := log_buf.OptPrefix("ModHttp")
- log := log_buf.NewLogBuf(optTerm, optPref)
- sf := &ModHttp{
- kCtx: kern.GetKernelCtx(),
- log: log,
- diaMode: cons.ModeUseCase,
- size: size.NewSize(800, 600),
- pos: coord.NewCoord(0, 0),
- }
- sHttp := kern.GetKernelServerHttp()
- fibApp := sHttp.Fiber()
- fibApp.Static("/static", "./static", fiber.Static{
- Compress: true,
- Browse: true,
- CacheDuration: time.Second * 10,
- })
- fibApp.Get("/", sf.getIndex)
- fibApp.Get("/api/use_case", sf.postSetModeUseCase)
- fibApp.Post("/api/arch", sf.postArch)
- fibApp.Post("/api/canvas_size", sf.postSetCanvasSize)
- return sf
- }
- // FormArch -- извлекает данные из текстового поля для обновления
- type FormArch struct {
- StrData string `form:"text"`
- }
- // Устанавливает размер холста
- func (sf *ModHttp) postSetCanvasSize(ctx *fiber.Ctx) error {
- err := ctx.BodyParser(&sf.canvSize)
- if err != nil {
- sf.log.Err("postSetCanvasSize(): on body parser, err=\n\t%v", err)
- }
- if sf.canvSize.SizeX_ > 0 {
- sf.size.W_ = alias.SizeX(sf.canvSize.SizeX_ - 10)
- }
- if sf.canvSize.SizeY_ > 0 {
- sf.size.H_ = alias.SizeY(sf.canvSize.SizeY_ - 10)
- }
- return ctx.SendString(sf.canvSize.String())
- }
- // Возвращает новый холст
- 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, sf.size, sf.pos)
- sizeX, sizeY := confCanvas.Size()
- canvas := svg.New(wr)
- canvas.Start(int(sizeX), int(sizeY))
- confCanvas.Draw(canvas)
- return canvas, nil
- }
- // По запросу парсит, что получилось -- обновляет архитектуру
- func (sf *ModHttp) postArch(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 := yaml.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.IDrawer)
- 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.IDrawer
- )
- switch strType {
- case cons.TypeUseActor: // Нарисовать актора
- view_actor.NewViewActor(actor, elem)
- case cons.TypeUseCase: // Нарисовать вариант использования
- drawer = view_use_case.NewViewUseCase(useCase, elem)
- case cons.TypeUseLink: // Нарисовать связь
- drawer = view_link.NewViewLink(useLink)
- case cons.TypeUseGroup: // Нарисовать группу вариантов использования
- drawer = view_use_group.NewViewUseGroup(useGroup, elem)
- }
- if drawer != nil {
- mapDraw[id] = drawer
- sf.log.Debug("useCase(): id=%q, elem=\n%v\n", id, elem)
- }
- }
- for _, drawer := range mapDraw {
- switch drawer.(type) {
- case types.IElemLinker:
- }
- drawer.Draw(canvas, mapDraw)
- }
- return nil
- }
- // Возвращает картинку архитектуры
- func (sf *ModHttp) postSetModeUseCase(ctx *fiber.Ctx) error {
- sf.diaMode = cons.ModeUseCase
- return ctx.SendString("[mode='" + sf.diaMode + "']")
- }
- // Возвращает главную страницу сервиса
- func (sf *ModHttp) getIndex(ctx *fiber.Ctx) error {
- ctx.Response().Header.Set("Content-Type", "text/html")
- return ctx.Send(indexHtml)
- }
|