This is an automated email from the ASF dual-hosted git repository.

arshad pushed a commit to branch frontend-refactor
in repository https://gitbox.apache.org/repos/asf/ambari.git


The following commit(s) were added to refs/heads/frontend-refactor by this push:
     new c759443780 AMBARI-26363 : Ambari Web React: Cluster Installation 
Wizard - Step7 (#4069)
c759443780 is described below

commit c759443780b71a539e1eae670147cff0a7a30de8
Author: Himanshu Maurya <[email protected]>
AuthorDate: Sun Sep 14 21:47:33 2025 +0530

    AMBARI-26363 : Ambari Web React: Cluster Installation Wizard - Step7 (#4069)
---
 .../screens/ClusterWizard/Step7/AccountsTab.tsx    | 276 +++++++++++++++++++++
 .../ClusterWizard/Step7/AllConfigurationsTab.tsx   |  27 ++
 .../screens/ClusterWizard/Step7/CredentialsTab.tsx | 266 ++++++++++++++++++++
 .../screens/ClusterWizard/Step7/DatabasesTab.tsx   |  27 ++
 .../screens/ClusterWizard/Step7/DirectoriesTab.tsx |  27 ++
 .../src/screens/ClusterWizard/types/step7Types.ts  |  13 +
 .../latest/src/screens/ClusterWizard/utils.ts      |  88 +++++++
 7 files changed, 724 insertions(+)

diff --git a/ambari-web/latest/src/screens/ClusterWizard/Step7/AccountsTab.tsx 
b/ambari-web/latest/src/screens/ClusterWizard/Step7/AccountsTab.tsx
new file mode 100644
index 0000000000..42e5c928c3
--- /dev/null
+++ b/ambari-web/latest/src/screens/ClusterWizard/Step7/AccountsTab.tsx
@@ -0,0 +1,276 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { cloneDeep, get, isEmpty, set } from "lodash";
+import { useEffect, useRef, useState } from "react";
+import { Card, Col, Container, Row } from "react-bootstrap";
+import { getDependentConfigChanges, isValidUserName } from "../utils";
+import Table from "../../../components/Table";
+import Modal from "../../../components/Modal";
+import TooltipInput from "../../../components/TooltipInput";
+import Spinner from "../../../components/Spinner";
+
+interface AccountsTabProps {
+  configProperties:any,
+  setConfigProperties:any,
+  services: string[];
+}
+
+enum UserGroupsProperties {
+  USER = "USER",
+  GROUP = "GROUP",
+  ADDITIONAL_USER_PROPERTY = "ADDITIONAL_USER_PROPERTY",
+  ADDITIONAL_GROUP_PROPERTY = "ADDITIONAL_GROUP_PROPERTY",
+}
+
+export default function AccountsTab({
+  configProperties,
+  setConfigProperties,
+  services,
+}: AccountsTabProps) {
+  const [accountConfigs, setAccountConfigs] = useState(configProperties);
+  const [showWarning, setShowWarning] = useState<boolean>(false);
+  const affectedProperties = useRef<any[]>([]);
+  const allConfigs = useRef<any[]>([]);
+
+  const serviceName = "MISC";
+  const configType = "Users and Groups";
+
+  useEffect(() => {
+    setConfigProperties(accountConfigs);
+  }, [accountConfigs]);
+  
+  
+  useEffect(() => {
+    allConfigs.current = getAllConfigs();
+  }, [accountConfigs]);
+
+  const getAllConfigs = () => {
+    let allConfigs: any[] = [];
+    Object.keys(accountConfigs).forEach((serviceName) => {
+      Object.keys(accountConfigs[serviceName]).forEach((configType) => {
+        
Object.keys(accountConfigs[serviceName][configType]?.properties).forEach((property)
 => {
+          const configuration = 
accountConfigs[serviceName][configType]?.properties[property];
+          allConfigs.push(configuration);
+        });
+      });
+    });
+
+    return allConfigs;
+  };
+
+  const columnInWarningTable = [
+    {
+      header: "Service",
+      accessorKey: "serviceName",
+    },
+    {
+      header: "Property",
+      accessorKey: "propertyName",
+    },
+    {
+      header: "Current Value",
+      accessorKey: "propertyValue",
+    },
+    {
+      header: "Adjusted Value",
+      accessorKey: "new_value",
+    },
+  ];
+
+  const updateAllConfigs = (affectedProperties: any[]) => {
+    const configPropertiesCopy = cloneDeep(accountConfigs);
+
+    affectedProperties.forEach((affectedProperty) => {
+ 
+      
configPropertiesCopy[affectedProperty.serviceName][affectedProperty.type].properties[affectedProperty.propertyName].value
 = affectedProperty.new_value;
+    });
+    setAccountConfigs(configPropertiesCopy);
+  };
+
+  const applyConfigChanges = () => {
+    setShowWarning(false);
+    let affectedPropertiesCopy = cloneDeep(affectedProperties.current);
+    affectedPropertiesCopy.forEach((affectedProperty) => {
+      set(
+        affectedProperty,
+        "value",
+        get(affectedProperty, "new_value")
+      );
+    });
+    affectedProperties.current = affectedPropertiesCopy;
+    updateAllConfigs(affectedProperties.current);
+    //Get the configs for config tab from context then update it using 
affectedProperties and push it back
+  };
+
+  if (isEmpty(accountConfigs)) {
+    return <Spinner/>;
+  }
+
+  
+
+  return (
+    <div>
+      {showWarning ? (
+        <Modal
+          isOpen={showWarning}
+          onClose={() => setShowWarning(false)}
+          modalTitle="Warning: you must also change these Service properties"
+          modalBody={
+            <Table
+              data={affectedProperties.current}
+              columns={columnInWarningTable}
+            />
+          }
+          successCallback={applyConfigChanges}
+          options={{
+            modalSize: "modal-lg",
+            buttonSize: "sm",
+            okButtonText: "APPLY",
+            cancelButtonText: "CANCEL",
+            cancelableViaIcon: false,
+            cancelableViaBtn: true,
+            okButtonVariant: "success",
+            extraButtons: [
+              {
+                text: "IGNORE",
+                onClick: () => setShowWarning(false),
+                variant: "warning",
+                order: 1,
+              },
+            ],
+          }}
+        />
+      ) : null}
+      <Card>
+        <Card.Body>
+          <div className="mb-4 text-muted ps-3">
+            Please review these settings for Service Accounts
+          </div>
+          <div className="p-2">
+          {accountConfigs[serviceName][configType] && 
Object.keys(accountConfigs[serviceName][configType].properties).map((propertyName:any)
 => {
+                const config = 
accountConfigs[serviceName][configType]?.properties[propertyName];
+                return ( 
+                  
config.propertyType.includes(UserGroupsProperties.ADDITIONAL_USER_PROPERTY) || 
config.propertyType.includes(UserGroupsProperties.ADDITIONAL_GROUP_PROPERTY) ? (
+                    <div key={config.propertyName} className="ps-3">
+                      <TooltipInput
+                        tooltipProps={{
+                          heading: config.propertyDisplayname + " - " + 
get(config,"propertyName"),
+                          message: config.propertyDescription,
+                          placement: "right",
+                        }}
+                        formControlProps={{
+                          type: "checkbox",
+                          label: config.propertyDisplayname,
+                          checked: config.value === "true",
+                          onChange: (e) => {
+                            let tempConfig = config;
+                            tempConfig.value = e.target.checked ? "true" : 
"false";
+                            setAccountConfigs({
+                              ...accountConfigs,
+                              [serviceName]: {
+                                ...accountConfigs[serviceName],
+                                [configType]: {
+                                  ...accountConfigs[serviceName][configType],
+                                  properties: {
+                                    
...accountConfigs[serviceName][configType].properties,
+                                    [propertyName]: tempConfig,
+                                  },
+                                },
+                              },
+                            });
+                          },
+                          className: "custom-checkbox",
+                        }}
+                      />
+                    </div>
+                ):null);
+                
+              })}
+          </div>
+          <div className="p-2 w-50">
+            <Container>
+              <Row className="p-2">
+                <Col className="fw-bolder">Users/Groups</Col>
+                <Col className="fw-bolder">Username</Col>
+              </Row>
+              {accountConfigs[serviceName][configType] && 
Object.keys(accountConfigs[serviceName][configType].properties).map((propertyName:any)
 => {
+                const config = 
accountConfigs[serviceName][configType].properties[propertyName];
+                    if(propertyName==="dfs.permissions.superusergroup") return;
+                    return ( 
+                      config.propertyType.includes(UserGroupsProperties.USER) 
|| config.propertyType.includes(UserGroupsProperties.GROUP) ? (
+                      <Row key={propertyName} className="p-2 text-muted">
+                        <Col 
className="pt-2">{config.propertyDisplayname?config.propertyDisplayname : 
config.propertyName}</Col>
+                        <Col>
+                          <TooltipInput
+                            tooltipProps={{
+                              heading: config.propertyDisplayname + " - " + 
get(config,"propertyName"),
+                              message: config.propertyDescription,
+                              placement: "right",
+                            }}
+                            formControlProps={{
+                              type: "text",
+                              value: config.value,
+                              onChange: (e) => {
+                                let tempConfig = config;
+                                tempConfig.value = e.target.value;
+                                setAccountConfigs({
+                                  ...accountConfigs,
+                                  [serviceName]: {
+                                    ...accountConfigs[serviceName],
+                                    [configType]: {
+                                      
...accountConfigs[serviceName][configType],
+                                      properties: {
+                                        
...accountConfigs[serviceName][configType].properties,
+                                        [propertyName]: tempConfig,
+                                      },
+                                    },
+                                  },
+                                });
+                              },
+                              onBlur: () => {
+                                const allAffectedProperties =
+                                  getDependentConfigChanges(
+                                    config,
+                                    services,
+                                    allConfigs.current
+                                  ) || [];
+                                if (allAffectedProperties.length > 0) {
+                                  affectedProperties.current =
+                                    allAffectedProperties;
+                                  setShowWarning(true);
+                                }
+                              },
+                              className: isValidUserName(config.value)
+                                ? "rounded-0"
+                                : "rounded-0 border-danger",
+                            }}
+                          />
+                        </Col>
+                      </Row>
+                    ):null)
+
+                    
+                  })}
+            </Container>
+          </div>
+        </Card.Body>
+      </Card>
+    </div>
+  );
+}
diff --git 
a/ambari-web/latest/src/screens/ClusterWizard/Step7/AllConfigurationsTab.tsx 
b/ambari-web/latest/src/screens/ClusterWizard/Step7/AllConfigurationsTab.tsx
new file mode 100644
index 0000000000..89c82afe2a
--- /dev/null
+++ b/ambari-web/latest/src/screens/ClusterWizard/Step7/AllConfigurationsTab.tsx
@@ -0,0 +1,27 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Card } from "react-bootstrap";
+
+export default function AllConfigurationsTab() {
+  return (
+    <Card>
+      <h5 className="p-4">All Configurations Tab</h5>
+    </Card>
+  );
+}
\ No newline at end of file
diff --git 
a/ambari-web/latest/src/screens/ClusterWizard/Step7/CredentialsTab.tsx 
b/ambari-web/latest/src/screens/ClusterWizard/Step7/CredentialsTab.tsx
new file mode 100644
index 0000000000..947dcdce0d
--- /dev/null
+++ b/ambari-web/latest/src/screens/ClusterWizard/Step7/CredentialsTab.tsx
@@ -0,0 +1,266 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { cloneDeep, get, set } from "lodash";
+import { useEffect, useState } from "react";
+import {Card, Form } from "react-bootstrap";
+import { CredentialConfigType } from "../types/step7Types";
+import { isValidUserName } from "../utils";
+import TooltipInput from "../../../components/TooltipInput";
+
+interface CredentialsTabProps {
+  themes: Object;
+  configs: Object;
+  setIsNextEnabled:Function;
+}
+export default function CredentialsTab({
+  themes,
+  configs,
+  setIsNextEnabled
+}: CredentialsTabProps) {
+  const [allCredentials, setAllCredentials] = useState<CredentialConfigType[]>(
+    []
+  );
+
+  useEffect(() => {
+    setAllCredentials(processDataForCredentialsTab(configs, themes));
+  }, [themes, configs]);
+
+  const updateConfig = (key: string, value: string, config: any) => {
+    let newConfig = { ...config };
+    set(newConfig, key, value);
+    setAllCredentials(
+      allCredentials.map((c) => (c === config ? newConfig : c))
+    );
+  };
+
+  const isValidPassword = (config: Object) => {
+    return (
+      get(config, "passwordProperty.property_value", "").length > 0 &&
+      get(config, "passwordProperty.property_value", "") ===
+        get(config, "confirmPasswordProperty.property_value", "")
+    );
+  };
+
+  useEffect(()=>{
+    const allCredentialsValid=allCredentials.every((config) => {
+      return (
+        (get(config, "usernameProperty")
+          ? isValidUserName(get(config, "usernameProperty.property_value", ""))
+          : true) && isValidPassword(config)
+      );
+    });
+    console.log("All Credentials Valid",allCredentialsValid)
+    if(allCredentialsValid){
+      setIsNextEnabled(true);
+    }
+    else{
+      setIsNextEnabled(false);
+    }
+  },[allCredentials])
+
+
+  const getTooltipInput = (config: any, propertyType: string) => {
+    const property_display_name = get(
+      config,
+      propertyType + ".property_display_name",
+      ""
+    );
+    const property_name = get(config, propertyType + ".property_name", "");
+    const property_description = get(
+      config,
+      propertyType + ".property_description",
+      ""
+    );
+    const property_value = get(config, propertyType + ".property_value", "");
+    const isPasswordProperty = propertyType.toLowerCase().includes("password");
+    const isInputValid = isPasswordProperty
+      ? isValidPassword(config)
+      : isValidUserName(property_value);
+
+    let tooltipHeading = "";
+    if (property_display_name && property_name) {
+      tooltipHeading = property_display_name + " " + property_name;
+    } else {
+      if (isPasswordProperty) {
+        tooltipHeading = "password";
+      } else {
+        tooltipHeading = "username";
+      }
+    }
+
+    let tooltipMessage = property_description;
+    if (isPasswordProperty) {
+      tooltipMessage +=
+        " For security purposes, password changes will not be shown in 
configuration version comparisons";
+    }
+
+    const formControlProps = {
+      type: isPasswordProperty ? "password" : "text",
+      value: property_value,
+      onChange: (e: any) =>
+        updateConfig(propertyType + ".property_value", e.target.value, config),
+      className: isInputValid ? "" : "border-danger",
+      placeholder: propertyType === "passwordProperty" ? "Type password" : "",
+    };
+
+    return (
+      <TooltipInput
+        tooltipProps={{
+          message: tooltipMessage,
+          heading: tooltipHeading,
+          placement: "left",
+        }}
+        formControlProps={formControlProps as any}
+      />
+    );
+  };
+
+  return (
+    <div>
+      <Card>
+        <Card.Body>
+          <div className="mb-4 text-muted">
+            Please provide credentials for these services
+          </div>
+          <div className="d-flex text-muted">
+            <div className="w-25 ps-3"></div>
+            <div className="w-25 ps-3">Username*</div>
+            <div className="w-25 ps-3">Password*</div>
+            <div className="w-25 ps-3">Confirm Password*</div>
+          </div>
+          <hr className="mb-2" />
+          <Form>
+            {allCredentials.map((config) => {
+              return (
+                <div key={get(config, "name", "")}>
+                  <div className="d-flex text-muted">
+                    <div className="w-25 pt-3 ps-2">
+                      {get(config, "display-name", "")}
+                    </div>
+                    <Form.Group className="w-25 p-2">
+                      {get(config, "usernameProperty") ? (
+                        getTooltipInput(config, "usernameProperty")
+                      ) : (
+                        <div className="pt-2 ps-2">N/A</div>
+                      )}
+                    </Form.Group>
+                    <Form.Group className="w-25 p-2">
+                      {getTooltipInput(config, "passwordProperty")}
+                    </Form.Group>
+                    <Form.Group className="w-25 p-2">
+                      {getTooltipInput(config, "confirmPasswordProperty")}
+                    </Form.Group>
+                  </div>
+                  <hr className="m-1" />
+                </div>
+              );
+            })}
+          </Form>
+        </Card.Body>
+      </Card>
+    </div>
+  );
+}
+
+export const processDataForCredentialsTab = (
+  configsData: Object,
+  themesData: Object
+) => {
+  let allConfigs: any[] = [];
+  get(configsData, "items", []).forEach((item) => {
+    get(item, "configurations", []).forEach((configuration) => {
+      allConfigs.push(get(configuration, "StackConfigurations", {}));
+    });
+  });
+
+  let tempCredentials: CredentialConfigType[] = [];
+  get(themesData, "items", []).forEach((item) => {
+    get(item, "themes", [])
+      .filter(
+        (theme) =>
+          get(theme, "ThemeInfo.theme_data.Theme.name") === "credentials"
+      )
+      .forEach((theme) => {
+        let currCredLayout: CredentialConfigType[] = [];
+
+        get(
+          theme,
+          "ThemeInfo.theme_data.Theme.configuration.layouts",
+          []
+        ).forEach((layout) => {
+          get(layout, "tabs", []).forEach((tab) => {
+            get(tab, "layout.sections", []).forEach((section) => {
+              get(section, "subsections", []).forEach((subsection: any) => {
+                if (subsection) {
+                  currCredLayout.push(subsection);
+                }
+              });
+            });
+          });
+        });
+
+        if (currCredLayout.length) {
+          get(
+            theme,
+            "ThemeInfo.theme_data.Theme.configuration.placement.configs",
+            []
+          ).forEach((config) => {
+            const [propertyType, propertyName] = get(
+              config,
+              "config",
+              ""
+            ).split("/");
+            const configMatchingProperty = allConfigs.find(
+              (c) =>
+                get(c, "type", "").startsWith(propertyType) &&
+                get(c, "property_name", "") === propertyName
+            );
+            if (configMatchingProperty) {
+              currCredLayout.forEach((section) => {
+                if (
+                  get(section, "name", "") ===
+                  get(config, "subsection-name", "")
+                ) {
+                  if (
+                    get(
+                      configMatchingProperty,
+                      "property_value_attributes.type",
+                      ""
+                    ) === "password"
+                  ) {
+                    set(section, "passwordProperty", configMatchingProperty);
+                    set(
+                      section,
+                      "confirmPasswordProperty",
+                      cloneDeep(configMatchingProperty)
+                    );
+                  } else {
+                    set(section, "usernameProperty", configMatchingProperty);
+                  }
+                }
+              });
+            }
+          });
+        }
+
+        tempCredentials = [...tempCredentials, ...currCredLayout];
+      });
+  });
+  return tempCredentials;
+};
diff --git a/ambari-web/latest/src/screens/ClusterWizard/Step7/DatabasesTab.tsx 
b/ambari-web/latest/src/screens/ClusterWizard/Step7/DatabasesTab.tsx
new file mode 100644
index 0000000000..bd01572932
--- /dev/null
+++ b/ambari-web/latest/src/screens/ClusterWizard/Step7/DatabasesTab.tsx
@@ -0,0 +1,27 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Card } from "react-bootstrap";
+
+export default function DatabasesTab() {
+  return (
+    <Card>
+      <h5 className="p-4">Databases Tab</h5>
+    </Card>
+  );
+}
\ No newline at end of file
diff --git 
a/ambari-web/latest/src/screens/ClusterWizard/Step7/DirectoriesTab.tsx 
b/ambari-web/latest/src/screens/ClusterWizard/Step7/DirectoriesTab.tsx
new file mode 100644
index 0000000000..b5dd7572a8
--- /dev/null
+++ b/ambari-web/latest/src/screens/ClusterWizard/Step7/DirectoriesTab.tsx
@@ -0,0 +1,27 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Card } from "react-bootstrap";
+
+export default function DirectoriesTab() {
+  return (
+    <Card>
+      <h5 className="p-4">Directories Tab</h5>
+    </Card>
+  );
+}
\ No newline at end of file
diff --git a/ambari-web/latest/src/screens/ClusterWizard/types/step7Types.ts 
b/ambari-web/latest/src/screens/ClusterWizard/types/step7Types.ts
new file mode 100644
index 0000000000..7c49aa4935
--- /dev/null
+++ b/ambari-web/latest/src/screens/ClusterWizard/types/step7Types.ts
@@ -0,0 +1,13 @@
+export interface CredentialConfigType {
+    final: string;
+    property_name: string;
+    property_type: string[];
+    property_value: string;
+    service_name: string;
+    stack_name: string;
+    stack_version: string;
+    type: string;
+    passwordProperty?: Object;
+    usernameProperty?: Object;
+    [key: string]: any;
+  }
\ No newline at end of file
diff --git a/ambari-web/latest/src/screens/ClusterWizard/utils.ts 
b/ambari-web/latest/src/screens/ClusterWizard/utils.ts
index 8503741c12..f3fb6793c0 100644
--- a/ambari-web/latest/src/screens/ClusterWizard/utils.ts
+++ b/ambari-web/latest/src/screens/ClusterWizard/utils.ts
@@ -1,3 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { get } from "lodash";
+
 export const isHostname = (hostname: string): boolean => {
   const regex = new RegExp(
     
/(?=^.{3,254}$)(^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*(\.[a-zA-Z]{1,62})$)/
@@ -9,3 +29,71 @@ export const isValidUserName = (username: string): boolean => 
{
   const regex = new RegExp(/^[a-z]([-a-z0-9]{0,30})$/);
   return regex.test(username);
 };
+
+export const getDependentConfigChanges = (
+  changedConfig: any,
+  selectedServices: string[],
+  allConfigs: any
+) => {
+  const propertyName = get(changedConfig, "propertyName");
+  const propertyValue = get(changedConfig, "value");
+  let allAffectedProperties: any[] = [];
+  if (propertyName === "hdfs_user") {
+    const affectedPropertyNames = [
+      // "dfs.permissions.superusergroup",
+      "dfs.cluster.administrators",
+    ];
+    allConfigs.forEach((config: any) => {
+      if (
+        get(config, "serviceName") === "HDFS" &&
+        affectedPropertyNames.includes(get(config, "propertyName")) &&
+        propertyValue.trim() !== get(config, "value", "").trim()
+      ) {
+        allAffectedProperties.push({ ...config, new_value: propertyValue });
+      }
+    });
+  } else if (propertyName === "yarn_user") {
+    const affectedPropertyNames = ["yarn.admin.acl"];
+    allConfigs.forEach((config: any) => {
+      if (
+        get(config, "serviceName") === "YARN" &&
+        affectedPropertyNames.includes(get(config, "propertyName")) &&
+        propertyValue.trim() !== get(config, "value").trim()
+      ) {
+        allAffectedProperties.push({ ...config, new_value: propertyValue });
+      }
+    });
+  } else if (propertyName === "user_group") {
+    if (!selectedServices.includes("YARN")) {
+      return;
+    }
+    if (selectedServices.includes("MAPREDUCE2")) {
+      const affectedPropertyNames = ["mapreduce.cluster.administrators"];
+      allConfigs.forEach((config: any) => {
+        if (
+          get(config, "serviceName") === "MAPREDUCE2" &&
+          affectedPropertyNames.includes(get(config, "propertyName")) &&
+          propertyValue.trim() !== get(config, "value").trim()
+        ) {
+          allAffectedProperties.push({ ...config, new_value: propertyValue });
+        }
+      });
+    }
+    if (selectedServices.includes("YARN")) {
+      const affectedPropertyNames = [
+        "yarn.nodemanager.linux-container-executor.group",
+      ];
+      allConfigs.forEach((config: any) => {
+        if (
+          get(config, "serviceName") === "YARN" &&
+          affectedPropertyNames.includes(get(config, "propertyName")) &&
+          propertyValue.trim() !== get(config, "value").trim()
+        ) {
+          allAffectedProperties.push({ ...config, new_value: propertyValue });
+        }
+      });
+    }
+  }
+
+  return allAffectedProperties;
+};


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to