Skip to content
This repository has been archived by the owner on Jan 19, 2023. It is now read-only.

Commit

Permalink
Set correct content path on websocket reconnect
Browse files Browse the repository at this point in the history
  • Loading branch information
bryanl committed Sep 18, 2019
1 parent 6dba0c9 commit 90d87d3
Show file tree
Hide file tree
Showing 41 changed files with 428 additions and 465 deletions.
28 changes: 0 additions & 28 deletions internal/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"net"
"net/http"
"os"
"path"
"strings"

"github.com/gorilla/mux"
Expand All @@ -21,7 +20,6 @@ import (
"github.com/vmware/octant/internal/log"
"github.com/vmware/octant/internal/mime"
"github.com/vmware/octant/internal/module"
"github.com/vmware/octant/pkg/navigation"
)

//go:generate mockgen -destination=./fake/mock_service.go -package=fake github.com/vmware/octant/internal/api Service
Expand Down Expand Up @@ -164,29 +162,3 @@ func (a *API) Handler(ctx context.Context) (*mux.Router, error) {

return router, nil
}

type apiNavSections struct {
modules []module.Module
}

func newAPINavSections(modules []module.Module) *apiNavSections {
return &apiNavSections{
modules: modules,
}
}

func (ans *apiNavSections) Sections(ctx context.Context, namespace string) ([]navigation.Navigation, error) {
var sections []navigation.Navigation

for _, m := range ans.modules {
contentPath := path.Join("/content", m.ContentPath())
navList, err := m.Navigation(ctx, namespace, contentPath)
if err != nil {
return nil, err
}

sections = append(sections, navList...)
}

return sections, nil
}
109 changes: 52 additions & 57 deletions internal/api/content_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import (
const (
RequestSetContentPath = "setContentPath"
RequestSetNamespace = "setNamespace"
RequestSetQueryParams = "setQueryParams"
)

// ContentManagerOption is an option for configuring ContentManager.
Expand Down Expand Up @@ -53,14 +52,16 @@ type ContentManager struct {
logger log.Logger
contentGenerateFunc ContentGenerateFunc
poller Poller
updateContentCh chan struct{}
}

// NewContentManager creates an instance of ContentManager.
func NewContentManager(moduleManager module.ManagerInterface, logger log.Logger, options ...ContentManagerOption) *ContentManager {
cm := &ContentManager{
moduleManager: moduleManager,
logger: logger,
poller: NewInterruptiblePoller(),
moduleManager: moduleManager,
logger: logger,
poller: NewInterruptiblePoller("content"),
updateContentCh: make(chan struct{}, 1),
}
cm.contentGenerateFunc = cm.generateContent

Expand All @@ -75,38 +76,32 @@ var _ StateManager = (*ContentManager)(nil)

// Start starts the manager.
func (cm *ContentManager) Start(ctx context.Context, state octant.State, s OctantClient) {
updateContentPathCh := make(chan struct{}, 1)
defer func() {
close(updateContentPathCh)
close(cm.updateContentCh)
}()

updateCancel := state.OnContentPathUpdate(func(_ string) {
updateContentPathCh <- struct{}{}
updateCancel := state.OnContentPathUpdate(func(contentPath string) {
cm.updateContentCh <- struct{}{}
})
defer updateCancel()

cm.poller.Run(ctx, updateContentPathCh, cm.runUpdate(state, s), event.DefaultScheduleDelay)
cm.poller.Run(ctx, cm.updateContentCh, cm.runUpdate(state, s), event.DefaultScheduleDelay)
}

func (cm *ContentManager) runUpdate(state octant.State, s OctantClient) PollerFunc {
return func(ctx context.Context) bool {
for {
contentResponse, rerun, err := cm.contentGenerateFunc(ctx, state)
if err != nil {
cm.logger.
WithErr(err).
With("contentPath", state.GetContentPath()).
Errorf("generate content")
return false
}
contentPath := state.GetContentPath()
if contentPath == "" {
return false
}

if ctx.Err() == nil {
s.Send(CreateContentEvent(contentResponse))
}
contentResponse, _, err := cm.contentGenerateFunc(ctx, state)
if err != nil {
return false
}

if !rerun {
break
}
if ctx.Err() == nil {
s.Send(CreateContentEvent(contentResponse, state.GetNamespace(), contentPath, state.GetQueryParams()))
}

return false
Expand All @@ -115,38 +110,33 @@ func (cm *ContentManager) runUpdate(state octant.State, s OctantClient) PollerFu

func (cm *ContentManager) generateContent(ctx context.Context, state octant.State) (component.ContentResponse, bool, error) {
contentPath := state.GetContentPath()
if contentPath != "" {
logger := cm.logger.With("contentPath", contentPath)
logger := cm.logger.With("contentPath", contentPath)

now := time.Now()
now := time.Now()
defer func() {
logger.With("elapsed", time.Since(now)).Debugf("generating content")
}()

contentPath = strings.TrimPrefix(contentPath, "/content/")
m, ok := cm.moduleManager.ModuleForContentPath(contentPath)
if !ok {
return component.EmptyContentResponse, false, errors.Errorf("unable to find module for content path %q", contentPath)
}
modulePath := strings.TrimPrefix(contentPath, m.Name())
options := module.ContentOptions{
LabelSet: FiltersToLabelSet(state.GetFilters()),
}
contentResponse, err := m.Content(ctx, modulePath, options)
if err != nil {
if nfe, ok := err.(notFound); ok && nfe.NotFound() {
logger.Debugf("path not found, redirecting to parent")
state.SetContentPath(notFoundRedirectPath(contentPath))
return component.EmptyContentResponse, true, nil
} else {
return component.EmptyContentResponse, false, errors.Wrap(err, "generate content")
}
m, ok := cm.moduleManager.ModuleForContentPath(contentPath)
if !ok {
return component.EmptyContentResponse, false, errors.Errorf("unable to find module for content path %q", contentPath)
}
modulePath := strings.TrimPrefix(contentPath, m.Name())
options := module.ContentOptions{
LabelSet: FiltersToLabelSet(state.GetFilters()),
}
contentResponse, err := m.Content(ctx, modulePath, options)
if err != nil {
if nfe, ok := err.(notFound); ok && nfe.NotFound() {
logger.Debugf("path not found, redirecting to parent")
state.SetContentPath(notFoundRedirectPath(contentPath))
return component.EmptyContentResponse, true, nil
} else {
return component.EmptyContentResponse, false, errors.Wrap(err, "generate content")
}

logger.With(
"elapsed", time.Since(now),
).Debugf("generate content")
return contentResponse, false, nil
}

return component.EmptyContentResponse, false, nil
return contentResponse, false, nil
}

// Handlers returns a slice of client request handlers.
Expand All @@ -160,10 +150,6 @@ func (cm *ContentManager) Handlers() []octant.ClientRequestHandler {
RequestType: RequestSetNamespace,
Handler: cm.SetNamespace,
},
{
RequestType: RequestSetQueryParams,
Handler: cm.SetQueryParams,
},
}
}

Expand Down Expand Up @@ -199,6 +185,10 @@ func (cm *ContentManager) SetContentPath(state octant.State, payload action.Payl
if err != nil {
return errors.Wrap(err, "extract contentPath from payload")
}
if err := cm.SetQueryParams(state, payload); err != nil {
return errors.Wrap(err, "extract query params from payload")
}

state.SetContentPath(contentPath)
return nil
}
Expand All @@ -209,9 +199,14 @@ type notFound interface {
}

// CreateContentEvent creates a content event.
func CreateContentEvent(contentResponse component.ContentResponse) octant.Event {
func CreateContentEvent(contentResponse component.ContentResponse, namespace, contentPath string, queryParams map[string][]string) octant.Event {
return octant.Event{
Type: octant.EventTypeContent,
Data: contentResponse,
Data: map[string]interface{}{
"content": contentResponse,
"namespace": namespace,
"contentPath": contentPath,
"queryParams": queryParams,
},
}
}
9 changes: 7 additions & 2 deletions internal/api/content_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,21 @@ func TestContentManager_Handlers(t *testing.T) {
AssertHandlers(t, manager, []string{
api.RequestSetContentPath,
api.RequestSetNamespace,
api.RequestSetQueryParams,
})
}

func TestContentManager_GenerateContent(t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()

params := map[string][]string{}

moduleManager := moduleFake.NewMockManagerInterface(controller)
state := octantFake.NewMockState(controller)

state.EXPECT().GetContentPath().Return("/path")
state.EXPECT().GetNamespace().Return("default")
state.EXPECT().GetQueryParams().Return(params)
state.EXPECT().OnContentPathUpdate(gomock.Any()).DoAndReturn(func(fn octant.ContentPathUpdateFunc) octant.UpdateCancelFunc {
fn("foo")
return func() {}
Expand All @@ -53,7 +58,7 @@ func TestContentManager_GenerateContent(t *testing.T) {
contentResponse := component.ContentResponse{
IconName: "fake",
}
contentEvent := api.CreateContentEvent(contentResponse)
contentEvent := api.CreateContentEvent(contentResponse, "default", "/path", params)
octantClient.EXPECT().Send(contentEvent).AnyTimes()

logger := log.NopLogger()
Expand Down
2 changes: 1 addition & 1 deletion internal/api/context_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ var _ StateManager = (*ContextManager)(nil)
func NewContextManager(dashConfig config.Dash, options ...ContextManagerOption) *ContextManager {
cm := &ContextManager{
dashConfig: dashConfig,
poller: NewInterruptiblePoller(),
poller: NewInterruptiblePoller("context"),
}

cm.contextGenerateFunc = cm.generateContexts
Expand Down
20 changes: 14 additions & 6 deletions internal/api/filter_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package api

import (
"context"
"fmt"
"strings"

"github.com/pkg/errors"
Expand All @@ -18,9 +19,9 @@ import (
)

const (
RequestAddFilter = "addFilter"
RequestClearFilters = "clearFilters"
RequestRemoveFilters = "removeFilters"
RequestAddFilter = "addFilter"
RequestClearFilters = "clearFilters"
RequestRemoveFilter = "removeFilter"
)

// FilterManager manages filters.
Expand Down Expand Up @@ -50,8 +51,8 @@ func (fm *FilterManager) Handlers() []octant.ClientRequestHandler {
Handler: fm.ClearFilters,
},
{
RequestType: RequestRemoveFilters,
Handler: fm.RemoveFilters,
RequestType: RequestRemoveFilter,
Handler: fm.RemoveFilter,
},
}
}
Expand All @@ -60,20 +61,27 @@ func (fm *FilterManager) Handlers() []octant.ClientRequestHandler {
func (fm *FilterManager) AddFilter(state octant.State, payload action.Payload) error {
if filter, ok := FilterFromPayload(payload); ok {
state.AddFilter(filter)
message := fmt.Sprintf("Added filter for label %s", filter.String())
state.SendAlert(action.CreateAlert(action.AlertTypeInfo, message, action.DefaultAlertExpiration))
}

return nil
}

// ClearFilters clears all filters.
func (fm *FilterManager) ClearFilters(state octant.State, payload action.Payload) error {
state.SetFilters([]octant.Filter{})
message := "Cleared filters"
state.SendAlert(action.CreateAlert(action.AlertTypeInfo, message, action.DefaultAlertExpiration))
return nil
}

// RemoveFilters removes a filter.
func (fm *FilterManager) RemoveFilters(state octant.State, payload action.Payload) error {
func (fm *FilterManager) RemoveFilter(state octant.State, payload action.Payload) error {
if filter, ok := FilterFromPayload(payload); ok {
state.RemoveFilter(filter)
message := fmt.Sprintf("Removed filter for label %s", filter.String())
state.SendAlert(action.CreateAlert(action.AlertTypeInfo, message, action.DefaultAlertExpiration))
}
return nil
}
Expand Down
7 changes: 5 additions & 2 deletions internal/api/filter_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func TestFilterManager_Handlers(t *testing.T) {
expected := []string{
api.RequestClearFilters,
api.RequestAddFilter,
api.RequestRemoveFilters,
api.RequestRemoveFilter,
}
sort.Strings(expected)

Expand All @@ -47,6 +47,7 @@ func TestFilterManager_AddFilter(t *testing.T) {

state := octantFake.NewMockState(controller)
state.EXPECT().AddFilter(octant.Filter{Key: "foo", Value: "bar"})
state.EXPECT().SendAlert(gomock.Any())

manager := api.NewFilterManager()

Expand All @@ -66,6 +67,7 @@ func TestFilterManager_ClearFilters(t *testing.T) {

state := octantFake.NewMockState(controller)
state.EXPECT().SetFilters([]octant.Filter{})
state.EXPECT().SendAlert(gomock.Any())

manager := api.NewFilterManager()

Expand All @@ -79,6 +81,7 @@ func TestFilterManager_RemoveFilter(t *testing.T) {

state := octantFake.NewMockState(controller)
state.EXPECT().RemoveFilter(octant.Filter{Key: "foo", Value: "bar"})
state.EXPECT().SendAlert(gomock.Any())

manager := api.NewFilterManager()

Expand All @@ -89,7 +92,7 @@ func TestFilterManager_RemoveFilter(t *testing.T) {
},
}

require.NoError(t, manager.RemoveFilters(state, payload))
require.NoError(t, manager.RemoveFilter(state, payload))
}

func TestFilterFromPayload(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion internal/api/namespaces_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ var _ StateManager = (*NamespacesManager)(nil)
func NewNamespacesManager(config NamespaceManagerConfig, options ...NamespacesManagerOption) *NamespacesManager {
n := &NamespacesManager{
config: config,
poller: NewInterruptiblePoller(),
poller: NewInterruptiblePoller("namespaces"),
namespacesGeneratorFunc: NamespacesGenerator,
}

Expand Down
Loading

0 comments on commit 90d87d3

Please sign in to comment.