import classNames from 'classnames'; import { has } from 'lodash'; import { $interpolate } from 'ngimport'; import React from 'react'; import type { Subscription } from 'rxjs'; import { merge } from 'rxjs/operators'; import { ServerGroupHeader } from './ServerGroupHeader'; import type { Application } from '../application'; import { SETTINGS } from '../config'; import type { IInstance, IServerGroup } from '../domain'; import type { ISortFilter } from '../filterModel'; import { InstanceList } from '../instance/InstanceList'; import { Instances } from '../instance/Instances'; import { ReactInjector } from '../reactShims'; import { ClusterState } from '../state'; import { logger, ScrollToService } from '../utils'; export interface IJenkinsViewModel { number: number; href?: string; } export interface IDockerViewModel { digest: string; image: string; tag: string; href?: string; } export interface IServerGroupProps { cluster: string; serverGroup: IServerGroup; application: Application; sortFilter: ISortFilter; hasLoadBalancers: boolean; hasDiscovery: boolean; hasManager?: boolean; } export interface IServerGroupState { jenkins: IJenkinsViewModel; docker: IDockerViewModel; instances: IInstance[]; images?: string[]; isSelected: boolean; // single select mode isMultiSelected: boolean; // multiselect mode } export class ServerGroup extends React.Component { private stateChangeSubscription: Subscription; private serverGroupsSubscription: Subscription; constructor(props: IServerGroupProps) { super(props); this.state = this.getState(props); } private getState(props: IServerGroupProps): IServerGroupState { const { serverGroup } = props; const instances = serverGroup.instances.filter((i) => ClusterState.filterService.shouldShowInstance(i)); const isSelected = this.isSelected(serverGroup); const isMultiSelected = this.isMultiSelected(props.sortFilter.multiselect, serverGroup); const jenkinsConfig = serverGroup.buildInfo && serverGroup.buildInfo.jenkins; const dockerConfig = serverGroup.buildInfo && serverGroup.buildInfo.docker; let jenkins: IJenkinsViewModel = null; let images: string[] = null; let docker: IDockerViewModel = null; if (jenkinsConfig && (jenkinsConfig.host || jenkinsConfig.fullUrl || serverGroup.buildInfo.buildInfoUrl)) { const fromHost = jenkinsConfig.host && [jenkinsConfig.host + 'job', jenkinsConfig.name, jenkinsConfig.number, ''].join('/'); const fromFullUrl = jenkinsConfig.fullUrl; const fromBuildInfo = serverGroup.buildInfo.buildInfoUrl; jenkins = { number: jenkinsConfig.number, href: fromBuildInfo || fromFullUrl || fromHost, }; } if (SETTINGS.dockerInsights.enabled && dockerConfig) { docker = { digest: dockerConfig.digest, tag: dockerConfig.tag, image: dockerConfig.image, href: $interpolate(SETTINGS.dockerInsights.url)(serverGroup) + 'images/' + encodeURIComponent(dockerConfig.image) + '/' + (dockerConfig.tag || dockerConfig.digest), }; } else if (has(serverGroup, 'buildInfo.images')) { images = serverGroup.buildInfo.images.map((val: string) => this.getShortSha(val)); } return { jenkins, instances, images, docker, isSelected, isMultiSelected, }; } private getShortSha(val: string): string { const sha = val.lastIndexOf('sha256:'); if (sha > -1) { // 14 = "sha256:" + 7 characters of hash val = val.substring(0, sha + 14) + '...'; } return val; } private isSelected(serverGroup: IServerGroup) { const params = { region: serverGroup.region, accountId: serverGroup.account, serverGroup: serverGroup.name, provider: serverGroup.type, }; return ReactInjector.$state.includes('**.serverGroup', params); } private isMultiSelected(multiselect: boolean, serverGroup: IServerGroup) { return multiselect && ClusterState.multiselectModel.serverGroupIsSelected(serverGroup); } private onServerGroupsChanged = () => { const isMultiSelected = this.isMultiSelected(this.props.sortFilter.multiselect, this.props.serverGroup); this.setState({ isMultiSelected }); // Enables the (angular) details pane to detect the changes ReactInjector.$rootScope.$applyAsync(() => false); }; private onStateChanged = () => { this.setState({ isSelected: this.isSelected(this.props.serverGroup) }); }; public componentDidMount(): void { const { serverGroupsStream, instancesStream } = ClusterState.multiselectModel; this.serverGroupsSubscription = serverGroupsStream .pipe(merge(instancesStream)) .subscribe(this.onServerGroupsChanged); this.stateChangeSubscription = ReactInjector.$uiRouter.globals.success$.subscribe(this.onStateChanged); this.onStateChanged(); } public componentWillUnmount() { this.stateChangeSubscription.unsubscribe(); this.serverGroupsSubscription.unsubscribe(); } public componentWillReceiveProps(nextProps: IServerGroupProps) { this.setState(this.getState(nextProps)); } public loadDetails(event: React.MouseEvent): void { event.persist(); setTimeout(() => { if (event.isDefaultPrevented() || event.nativeEvent.defaultPrevented) { return; } ClusterState.multiselectModel.toggleServerGroup(this.props.serverGroup); event.preventDefault(); }); } private handleServerGroupClicked = (event: React.MouseEvent) => { logger.log({ category: 'Cluster Pod', action: 'Load Server Group Details' }); this.loadDetails(event); }; public render() { const { instances, images, jenkins, docker, isSelected, isMultiSelected } = this.state; const { serverGroup, application, sortFilter, hasDiscovery, hasLoadBalancers, hasManager } = this.props; const { account, region, name } = serverGroup; const { showAllInstances, listInstances } = sortFilter; const key = ScrollToService.toDomId(['serverGroup', account, region, name].join('-')); const serverGroupClassName = classNames({ 'server-group': true, 'rollup-pod-server-group': true, clickable: true, 'clickable-row': true, disabled: serverGroup.isDisabled, active: isSelected, managed: hasManager, }); return (
{showAllInstances && (
{listInstances ? (
) : (
)}
)}
); } }