From 0461b59150b517b32fc6f2ee6e1751161417c7e3 Mon Sep 17 00:00:00 2001 From: Tom Wright Date: Sat, 8 Aug 2020 00:21:39 +0100 Subject: [PATCH] Rework some things to prepare for the cleanup of old diagrams --- cmd/app/main.go | 73 ++++++------------------------------- go.mod | 2 + go.sum | 2 + internal/cleanup_service.go | 41 +++++++++++++++++++++ internal/generator.go | 10 +++++ internal/http.go | 4 +- internal/http_service.go | 47 ++++++++++++++++++++++++ 7 files changed, 115 insertions(+), 64 deletions(-) create mode 100644 go.sum create mode 100644 internal/cleanup_service.go create mode 100644 internal/http_service.go diff --git a/cmd/app/main.go b/cmd/app/main.go index 3a115dd..f040261 100644 --- a/cmd/app/main.go +++ b/cmd/app/main.go @@ -1,61 +1,14 @@ package main import ( + "context" "flag" "fmt" + "github.com/tomwright/lifetime" "github.com/tomwright/mermaid-server/internal" - "log" - "net/http" "os" ) -var testInput = []byte(` -graph TB - - subgraph "Jira" - createTicket["Create ticket"] - updateTicket["Update ticket"] - fireWebhook["Fire webhook"] - - createTicket-->fireWebhook - updateTicket-->fireWebhook - end - - subgraph "Jira Webhook" - receiveWebhook["Receive webhook"] - storeEvent["Store event in immutable list"] - publishNewStoredEventEvent["Publish message to notify system of new event"] - - createEvent["Create internal event that will be stored"] - setCreatedAt["Set created at date to now"] - setSourceJira["Set source to Jira webhook"] - - receiveWebhook-->createEvent-->setCreatedAt-->setSourceJira-->storeEvent - storeEvent-->publishNewStoredEventEvent - end - - fireWebhook-->receiveWebhook - - subgraph "Play Event" - publishEventUpdated["Publish message to notify system of new status"] - - verifyEventSource["Verify event source"] - parsePayload["Parse event payload using source to determine structure"] - findEventHandler["Find the handler for the specific event type + version"] - getLatestPersistedState["Get latest persisted state"] - changeInMemoryStateUsingEventData["Change in-memory state using event data"] - persistUpdatedState["Persist updated state"] - - verifyEventSource-->parsePayload - parsePayload-->findEventHandler - findEventHandler-->getLatestPersistedState-->changeInMemoryStateUsingEventData-->persistUpdatedState - - persistUpdatedState-->publishEventUpdated - end - - publishNewStoredEventEvent-->verifyEventSource -`) - func main() { mermaid := flag.String("mermaid", "", "The full path to the mermaidcli executable.") in := flag.String("in", "", "Directory to store input files.") @@ -81,20 +34,16 @@ func main() { cache := internal.NewDiagramCache() generator := internal.NewGenerator(cache, *mermaid, *in, *out, *puppeteer) - httpHandler := internal.GenerateHTTPHandler(generator) + httpService := internal.NewHTTPService(generator) + cleanupService := internal.NewCleanupService(generator) - r := http.NewServeMux() - r.Handle("/generate", http.HandlerFunc(httpHandler)) + lt := lifetime.New(context.Background()).Init() - httpServer := &http.Server{ - Addr: ":80", - Handler: r, - } - log.Printf("Listening on address %s", httpServer.Addr) - if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed { - log.Printf("Could not listen for http connections: %s", err) - os.Exit(1) - } + // Start the http service. + lt.Start(httpService) + // Start the cleanup service. + lt.Start(cleanupService) - log.Printf("Shutdown") + // Wait for all routines to stop running. + lt.Wait() } diff --git a/go.mod b/go.mod index b85d516..902a778 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,5 @@ module github.com/tomwright/mermaid-server go 1.13 + +require github.com/tomwright/lifetime v1.0.0 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..f176d16 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/tomwright/lifetime v1.0.0 h1:Yzj+Td38eUUdZ1ewvOegywFBmKyaCh+8HjKBmeXw6OM= +github.com/tomwright/lifetime v1.0.0/go.mod h1:GUCHgRaR/zStvtJiOd3B4gIZayeiz3TgApC9kNYAOQI= diff --git a/internal/cleanup_service.go b/internal/cleanup_service.go new file mode 100644 index 0000000..2858857 --- /dev/null +++ b/internal/cleanup_service.go @@ -0,0 +1,41 @@ +package internal + +import ( + "log" + "time" +) + +// NewCleanupService returns a service that can be used cleanup old diagrams. +func NewCleanupService(generator Generator) *cleanupService { + return &cleanupService{ + generator: generator, + stopCh: make(chan struct{}), + } +} + +// cleanupService is a service that can be used cleanup old diagrams. +type cleanupService struct { + generator Generator + stopCh chan struct{} +} + +// Start starts the cleanup service. +func (s *cleanupService) Start() error { + for { + if err := s.generator.CleanUp(time.Hour); err != nil { + log.Printf("error when cleaning up: %s", err.Error()) + } + + select { + case <-time.After(time.Minute * 5): + continue + case <-s.stopCh: + return nil + } + } +} + +// Stop stops the cleanup service. +func (s *cleanupService) Stop() { + close(s.stopCh) +} diff --git a/internal/generator.go b/internal/generator.go index 1419c15..1468edc 100644 --- a/internal/generator.go +++ b/internal/generator.go @@ -8,14 +8,18 @@ import ( "log" "os" "os/exec" + "time" ) // Generator provides the ability to generate a diagram. type Generator interface { // Generate generates the given diagram. Generate(diagram *Diagram) error + // CleanUp removes any diagrams that haven't used within the given duration. + CleanUp(duration time.Duration) error } +// NewGenerator returns a generator that can be used to generate diagrams. func NewGenerator(cache DiagramCache, mermaidCLIPath string, inPath string, outPath string, puppeteerConfigPath string) Generator { return &cachingGenerator{ cache: cache, @@ -102,3 +106,9 @@ func (c cachingGenerator) generate(diagram *Diagram) error { return nil } + +// CleanUp removes any diagrams that haven't used within the given duration. +func (c cachingGenerator) CleanUp(duration time.Duration) error { + // todo : loop through all cached diagrams and delete any that haven't been used within duration. + return nil +} diff --git a/internal/http.go b/internal/http.go index ccb28c6..ea55d65 100644 --- a/internal/http.go +++ b/internal/http.go @@ -80,8 +80,8 @@ func getDiagramFromPOST(rw http.ResponseWriter, r *http.Request) *Diagram { return d } -// GenerateHTTPHandler returns a HTTP handler used to generate a diagram. -func GenerateHTTPHandler(generator Generator) func(rw http.ResponseWriter, r *http.Request) { +// generateHTTPHandler returns a HTTP handler used to generate a diagram. +func generateHTTPHandler(generator Generator) func(rw http.ResponseWriter, r *http.Request) { return func(rw http.ResponseWriter, r *http.Request) { var diagram *Diagram diff --git a/internal/http_service.go b/internal/http_service.go new file mode 100644 index 0000000..934552a --- /dev/null +++ b/internal/http_service.go @@ -0,0 +1,47 @@ +package internal + +import ( + "net/http" +) + +// NewHTTPService returns a service that can be used to start a http server +// that will generate diagrams. +func NewHTTPService(generator Generator) *httpService { + return &httpService{ + generator: generator, + } +} + +// httpService is a service that can be used to start a http server +// that will generate diagrams. +type httpService struct { + httpServer *http.Server + generator Generator +} + +// Start starts the HTTP server. +func (s *httpService) Start() error { + httpHandler := generateHTTPHandler(s.generator) + + r := http.NewServeMux() + r.Handle("/generate", http.HandlerFunc(httpHandler)) + + s.httpServer = &http.Server{ + Addr: ":80", + Handler: r, + } + + if err := s.httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed { + if err != http.ErrServerClosed { + return err + } + } + + return nil +} + +func (s *httpService) Stop() { + if s != nil { + _ = s.httpServer.Close() + } +}