diff --git a/client/src/app/ctl/phase/phase.component.css b/client/src/app/ctl/phase/phase.component.css index 65361f8..4b82b27 100644 --- a/client/src/app/ctl/phase/phase.component.css +++ b/client/src/app/ctl/phase/phase.component.css @@ -39,6 +39,11 @@ flex-direction: row; } +.unloaded-phase { + display: flex; + flex-direction: row; +} + .docless-phase-btn { padding-left: 20px; padding-right: 0px; diff --git a/client/src/app/ctl/phase/phase.component.html b/client/src/app/ctl/phase/phase.component.html index a1e2823..9f17552 100755 --- a/client/src/app/ctl/phase/phase.component.html +++ b/client/src/app/ctl/phase/phase.component.html @@ -20,9 +20,31 @@ -
+
+ + + + + + + + +
+
- diff --git a/client/src/app/ctl/phase/phase.component.ts b/client/src/app/ctl/phase/phase.component.ts index 1538722..c1520b5 100755 --- a/client/src/app/ctl/phase/phase.component.ts +++ b/client/src/app/ctl/phase/phase.component.ts @@ -44,6 +44,7 @@ export class PhaseComponent implements WsReceiver { activeLink = 'overview'; phaseTree: KustomNode[] = []; + clickedNode: KustomNode; treeControl = new NestedTreeControl(node => node.children); dataSource = new MatTreeNestedDataSource(); @@ -84,6 +85,9 @@ export class PhaseComponent implements WsReceiver { case WsConstants.GET_PHASE: this.handleGetPhase(message); break; + case WsConstants.GET_PHASE_SOURCE_FILES: + this.handleGetPhaseSourceFiles(message); + break; case WsConstants.GET_YAML: this.handleGetYaml(message); break; @@ -109,6 +113,13 @@ export class PhaseComponent implements WsReceiver { } } + handleGetPhaseSourceFiles(message: WsMessage): void { + this.clickedNode.running = false; + const data: KustomNode[] = []; + Object.assign(data, message.data); + this.updateTree(data); + } + handleValidatePhase(message: WsMessage): void { this.websocketService.printIfToast(message); } @@ -288,4 +299,30 @@ export class PhaseComponent implements WsReceiver { } } } + + getPhaseSourceFiles(node: KustomNode): void { + const msg = new WsMessage(this.type, this.component, WsConstants.GET_PHASE_SOURCE_FILES); + msg.id = JSON.stringify(node.phaseId); + this.websocketService.sendMessage(msg); + } + + refreshTreeData(): void { + const tmpdata = this.dataSource.data; + this.dataSource.data = null; + this.dataSource.data = tmpdata; + } + + loadPhase(node: KustomNode): void { + this.clickedNode = node; + this.clickedNode.running = true; + this.getPhaseSourceFiles(this.clickedNode); + } + + updateTree(data: KustomNode[]): void { + if (this.clickedNode !== undefined) { + this.clickedNode.children = data; + this.clickedNode.hasDocuments = false; + this.refreshTreeData(); + } + } } diff --git a/client/src/app/ctl/phase/phase.models.ts b/client/src/app/ctl/phase/phase.models.ts index 39365bd..2ebe666 100644 --- a/client/src/app/ctl/phase/phase.models.ts +++ b/client/src/app/ctl/phase/phase.models.ts @@ -16,7 +16,7 @@ export class KustomNode { id: string; phaseId: { Name: string, Namespace: string}; name: string; - canLoadChildren: boolean; + hasDocuments: boolean; children: KustomNode[]; isPhaseNode: boolean; running: boolean; diff --git a/client/src/services/ws/ws.models.ts b/client/src/services/ws/ws.models.ts index 2631809..52b5d4f 100755 --- a/client/src/services/ws/ws.models.ts +++ b/client/src/services/ws/ws.models.ts @@ -81,6 +81,7 @@ export class WsConstants { public static readonly GET_DOCUMENT_BY_SELECTOR = 'getDocumentsBySelector'; public static readonly GET_EXECUTOR_DOC = 'getExecutorDoc'; public static readonly GET_PHASE = 'getPhase'; + public static readonly GET_PHASE_SOURCE_FILES = 'getPhaseSourceFiles'; public static readonly GET_PHASE_TREE = 'getPhaseTree'; public static readonly GET_TARGET = 'getTarget'; public static readonly GET_YAML = 'getYaml'; diff --git a/pkg/configs/configs.go b/pkg/configs/configs.go index 7998391..2f2966b 100644 --- a/pkg/configs/configs.go +++ b/pkg/configs/configs.go @@ -178,7 +178,7 @@ const ( GetYaml WsSubComponentType = "getYaml" GetRendered WsSubComponentType = "getRendered" GetPhaseTree WsSubComponentType = "getPhaseTree" - GetPhaseSourceFiles WsSubComponentType = "getPhaseSource" + GetPhaseSourceFiles WsSubComponentType = "getPhaseSourceFiles" GetDocumentsBySelector WsSubComponentType = "getDocumentsBySelector" GetPhase WsSubComponentType = "getPhase" GetExecutorDoc WsSubComponentType = "getExecutorDoc" diff --git a/pkg/ctl/phase.go b/pkg/ctl/phase.go index 1b7ae07..3184e2e 100644 --- a/pkg/ctl/phase.go +++ b/pkg/ctl/phase.go @@ -89,6 +89,8 @@ func HandlePhaseRequest(user *string, request configs.WsMessage) configs.WsMessa s := "rendered" message = &s response.Name, response.YAML, err = client.GetExecutorDoc(id) + case configs.GetPhaseSourceFiles: + response.Data, err = client.getPhaseSource(id) default: err = fmt.Errorf("Subcomponent %s not found", request.SubComponent) } @@ -103,6 +105,17 @@ func HandlePhaseRequest(user *string, request configs.WsMessage) configs.WsMessa return response } +func (c *Client) getPhaseSource(id string) ([]KustomNode, error) { + phaseID := ifc.ID{} + + err := json.Unmarshal([]byte(id), &phaseID) + if err != nil { + return nil, err + } + + return c.GetPhaseSourceFiles(phaseID) +} + // this helper function will likely disappear once a clear workflow for // phase validation takes shape in UI. For now, it simply returns a // string message to be displayed as a toast in frontend client diff --git a/pkg/ctl/tree.go b/pkg/ctl/tree.go index dafbbde..7acb541 100644 --- a/pkg/ctl/tree.go +++ b/pkg/ctl/tree.go @@ -70,26 +70,14 @@ func (client *Client) GetPhaseTree() ([]KustomNode, error) { for _, p := range phases { pNode := KustomNode{ - ID: uuid.New().String(), - PhaseID: ifc.ID{Name: p.Name, Namespace: p.Namespace}, - Name: fmt.Sprintf("Phase: %s", p.Name), - IsPhaseNode: true, - Children: []KustomNode{}, + ID: uuid.New().String(), + PhaseID: ifc.ID{Name: p.Name, Namespace: p.Namespace}, + Name: fmt.Sprintf("Phase: %s", p.Name), + IsPhaseNode: true, + HasDocuments: p.Config.DocumentEntryPoint != "", + Children: []KustomNode{}, } - // some phases don't have any associated documents, so don't look - // for children unless a DocumentEntryPoint has been specified - if p.Config.DocumentEntryPoint != "" { - children, err := client.GetPhaseSourceFiles(pNode.PhaseID) - if err != nil { - // TODO(mfuller): push an error to UI so it can be handled by - // toastr service, pending refactor of webservice and configs pkgs - log.Errorf("Error building tree for phase '%s': %s", p.Name, err) - pNode.HasError = true - } else { - pNode.Children = children - } - } nodes = append(nodes, pNode) } @@ -181,12 +169,13 @@ func (client *Client) GetPhaseSourceFiles(id ifc.ID) ([]KustomNode, error) { // KustomNode structure to represent the kustomization tree for a given phase // bundle to be consumed by the UI frontend type KustomNode struct { - ID string `json:"id"` // UUID for backend node index - PhaseID ifc.ID `json:"phaseId"` - Name string `json:"name"` // name used for display purposes (cli, ui) - IsPhaseNode bool `json:"isPhaseNode"` - HasError bool `json:"hasError"` - Children []KustomNode `json:"children"` + ID string `json:"id"` + PhaseID ifc.ID `json:"phaseId"` + Name string `json:"name"` + IsPhaseNode bool `json:"isPhaseNode"` + HasError bool `json:"hasError"` + HasDocuments bool `json:"hasDocuments"` + Children []KustomNode `json:"children"` } func contains(dirs []string, val string) bool {