blob: be179a981bffbc9458a7d124227fcafaeaa731ef [file] [log] [blame]
/*
* 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 React, { Component } from "react";
import { Button, Modal, Table, Row, Col } from "react-bootstrap";
import { Form, Field } from "react-final-form";
import { toast } from "react-toastify";
import arrayMutators from "final-form-arrays";
import { FieldArray } from "react-final-form-arrays";
import Select from "react-select";
import AsyncSelect from "react-select/async";
import { RegexValidation } from "Utils/XAEnums";
import { fetchApi } from "Utils/fetchAPI";
import ServiceAuditFilter from "./ServiceAuditFilter";
import TestConnection from "./TestConnection";
import {
commonBreadcrumb,
navigateTo,
serverError,
updateTagActive
} from "../../utils/XAUtils";
import {
BlockUi,
Condition,
CustomPopover,
Loader,
scrollToError
} from "../../components/CommonComponents";
import {
difference,
flatMap,
groupBy,
keys,
map,
find,
reject,
uniq,
isEmpty,
isUndefined,
has,
split,
without,
maxBy,
isArray,
cloneDeep
} from "lodash";
import withRouter from "Hooks/withRouter";
import { RangerPolicyType } from "../../utils/XAEnums";
import { getServiceDef } from "../../utils/appState";
class ServiceForm extends Component {
constructor(props) {
super(props);
this.serviceDefData = cloneDeep(getServiceDef());
this.configsJson = {};
this.initialValuesObj = {
isEnabled: "true",
configs: {},
customConfigs: [undefined],
auditFilters: []
};
this.state = {
serviceDef: {},
service: {},
tagService: [],
editInitialValues: {},
showDelete: false,
loader: true,
blockUI: false,
defaultTagOptions: [],
loadingOptions: false
};
}
showDeleteModal = () => {
this.setState({ showDelete: true });
};
hideDeleteModal = () => {
this.setState({ showDelete: false });
};
componentDidMount() {
let servicedefs;
servicedefs = this.serviceDefData.allServiceDefs.find((id) => {
return id.id == this.props.params.serviceDefId;
});
if (servicedefs == undefined) {
return navigateTo.navigate("/pageNotFound");
}
this.fetchServiceDef();
}
onSubmit = async (values) => {
let serviceId;
let apiMethod;
let apiUrl;
let apiSuccess;
let apiError;
if (this.props.params.serviceId !== undefined) {
serviceId = this.props.params.serviceId;
apiMethod = "put";
apiUrl = `plugins/services/${serviceId}`;
apiSuccess = "updated";
apiError = "Error occurred while updating a service";
} else {
apiMethod = "post";
apiUrl = `plugins/services`;
apiSuccess = "created";
apiError = "Error occurred while creating a service!";
}
try {
this.setState({ blockUI: true });
await fetchApi({
url: apiUrl,
method: apiMethod,
data: this.getServiceConfigsToSave(values)
});
this.setState({ blockUI: false });
toast.success(`Successfully ${apiSuccess} the service`);
if (this.props?.location?.state != "services") {
if (this?.props?.params?.serviceId !== undefined) {
this.props.navigate(
`/service/${this.props.params.serviceId}/policies/${RangerPolicyType.RANGER_ACCESS_POLICY_TYPE.value}`
);
} else {
return this.props.navigate(
this.state?.serviceDef?.name === "tag"
? "/policymanager/tag"
: "/policymanager/resource"
);
}
} else {
this.props.navigate(
this.state?.serviceDef?.name === "tag"
? "/policymanager/tag"
: "/policymanager/resource"
);
}
} catch (error) {
this.setState({ blockUI: false });
serverError(error);
console.error(apiError);
}
};
getServiceConfigsToSave = (values) => {
const serviceJson = {};
if (this.props.params.serviceId !== undefined) {
serviceJson["id"] = this.props.params.serviceId;
}
serviceJson["name"] = values?.name;
serviceJson["displayName"] = values?.displayName;
serviceJson["description"] = values?.description;
serviceJson["type"] = this.state?.serviceDef?.name;
serviceJson["tagService"] =
values?.tagService == null ? "" : values.tagService.value;
serviceJson["isEnabled"] = values?.isEnabled === "true";
serviceJson["configs"] = {};
for (const config in values?.configs) {
for (const jsonConfig in this.configsJson) {
if (config === this.configsJson[jsonConfig]) {
serviceJson["configs"][jsonConfig] = values?.configs[config];
}
}
}
if (values?.customConfigs !== undefined) {
values.customConfigs?.map((config) => {
config?.name !== undefined &&
config?.value !== undefined &&
(serviceJson["configs"][config.name] = config.value);
});
}
if (values?.isAuditFilter) {
serviceJson["configs"]["ranger.plugin.audit.filters"] =
this.getAuditFiltersToSave(values.auditFilters);
} else {
serviceJson["configs"]["ranger.plugin.audit.filters"] = "";
}
return { ...this.state.service, ...serviceJson };
};
getAuditFiltersToSave = (auditFilters) => {
let auditFiltersArray = [];
let serviceDef = this.state.serviceDef;
auditFilters.map((item) => {
let obj = {};
if (!isUndefined(item) && !isEmpty(item)) {
Object.entries(item).map(([key, value]) => {
if (key === "isAudited") {
obj.isAudited = value === "true";
}
if (key === "accessResult" && !isEmpty(value)) {
obj.accessResult = value?.value;
}
if (key === "resources" && !isEmpty(value)) {
obj.resources = {};
let levels = uniq(map(serviceDef.resources, "level"));
levels.map((level) => {
let resourceObj = find(serviceDef.resources, {
level: level,
name: value[`resourceName-${level}`]?.name
});
if (
value[`resourceName-${level}`] !== undefined &&
value[`value-${level}`] !== undefined
) {
obj.resources[value[`resourceName-${level}`].name] = {
values: isArray(value[`value-${level}`])
? map(value[`value-${level}`], "value")
: [value[`value-${level}`].value]
};
if (
value[`isRecursiveSupport-${level}`] !== undefined &&
resourceObj.recursiveSupported
) {
obj.resources[
value[`resourceName-${level}`].name
].isRecursive = value[`isRecursiveSupport-${level}`];
} else if (
value[`isRecursiveSupport-${level}`] === undefined &&
resourceObj.recursiveSupported
) {
obj.resources[
value[`resourceName-${level}`].name
].isRecursive = resourceObj.recursiveSupported;
}
if (
value[`isExcludesSupport-${level}`] !== undefined &&
resourceObj.excludesSupported
) {
obj.resources[
value[`resourceName-${level}`].name
].isExcludes = value[`isExcludesSupport-${level}`];
} else if (
value[`isExcludesSupport-${level}`] === undefined &&
resourceObj.excludesSupported
) {
obj.resources[
value[`resourceName-${level}`].name
].isExcludes = resourceObj.excludesSupported;
}
}
});
}
if (key === "actions") {
obj.actions = map(value, "value");
}
if (key === "accessTypes") {
if (serviceDef.name == "tag") {
obj.accessTypes = flatMap(map(value.tableList, "permission"));
} else {
obj.accessTypes = map(value, "value");
}
}
if (key === "users") {
obj.users = map(value, "value");
}
if (key === "groups") {
obj.groups = map(value, "value");
}
if (key === "roles") {
obj.roles = map(value, "value");
}
});
if (!has(obj, "isAudited")) {
obj.isAudited = true;
}
auditFiltersArray.push(obj);
}
});
return JSON.stringify(auditFiltersArray).replace(/"/g, "'");
};
fetchServiceDef = () => {
const serviceJson = this.initialValuesObj;
let serviceDef;
let serviceDefId = this.props.params.serviceDefId;
let isTagView = false;
serviceDef = this.serviceDefData.allServiceDefs.find((servicedef) => {
return servicedef.id == serviceDefId;
});
isTagView =
serviceDef?.name !== undefined && serviceDef?.name === "tag"
? true
: false;
updateTagActive(isTagView);
if (serviceDef?.resources !== undefined) {
for (const obj of serviceDef.resources) {
if (
this.props.params.serviceId === undefined &&
obj.lookupSupported !== undefined &&
obj.lookupSupported
) {
obj.lookupSupported = false;
}
}
}
let auditFilters = find(serviceDef?.configs, {
name: "ranger.plugin.audit.filters"
});
serviceJson["auditFilters"] = [];
if (
auditFilters &&
auditFilters !== undefined &&
this.props.params.serviceId === undefined
) {
auditFilters = isEmpty(auditFilters?.defaultValue)
? []
: JSON.parse(auditFilters.defaultValue.replace(/'/g, '"'));
serviceJson["isAuditFilter"] = auditFilters.length > 0 ? true : false;
serviceJson["auditFilters"] = this.getAuditFilters(
auditFilters,
serviceDef
);
}
this.setState({
serviceDef: serviceDef,
createInitialValues: serviceJson,
loader: this.props.params.serviceId !== undefined ? true : false
});
if (this.props.params.serviceId !== undefined) {
this.fetchService();
}
};
fetchService = async () => {
let serviceResp;
let serviceId = this.props.params.serviceId;
try {
serviceResp = await fetchApi({
url: `plugins/services/${serviceId}`
});
} catch (error) {
console.error(
`Error occurred while fetching Service or CSRF headers! ${error}`
);
}
const serviceJson = {};
serviceJson["name"] = serviceResp?.data?.name;
serviceJson["displayName"] = serviceResp?.data?.displayName;
serviceJson["description"] = serviceResp?.data?.description;
serviceJson["isEnabled"] = JSON.stringify(serviceResp?.data?.isEnabled);
serviceJson["tagService"] =
serviceResp?.data?.tagService !== undefined
? {
value: serviceResp?.data?.tagService,
label: serviceResp?.data?.tagService
}
: null;
serviceJson["configs"] = {};
let serviceDefConfigs = map(this.state?.serviceDef?.configs, "name");
let serviceCustomConfigs = without(
difference(keys(serviceResp?.data?.configs), serviceDefConfigs),
"ranger.plugin.audit.filters"
);
serviceDefConfigs.map((config) => {
serviceJson["configs"][config.replaceAll(".", "_").replaceAll("-", "_")] =
serviceResp?.data?.configs?.[config];
});
let editCustomConfigs = serviceCustomConfigs.map((config) => {
return { name: config, value: serviceResp?.data?.configs[config] };
});
serviceJson["customConfigs"] =
editCustomConfigs.length == 0 ? [undefined] : editCustomConfigs;
let editAuditFilters =
serviceResp?.data?.configs?.["ranger.plugin.audit.filters"];
serviceJson["auditFilters"] = [];
if (
editAuditFilters &&
editAuditFilters !== undefined &&
this.props.params.serviceId !== undefined
) {
editAuditFilters = isEmpty(editAuditFilters)
? []
: JSON.parse(editAuditFilters.replace(/'/g, '"'));
serviceJson["isAuditFilter"] = editAuditFilters.length > 0 ? true : false;
serviceJson["auditFilters"] = this.getAuditFilters(
editAuditFilters,
this.state.serviceDef
);
}
this.setState({
service: serviceResp?.data,
editInitialValues: serviceJson,
loader: false
});
};
fetchTagService = async (inputValue) => {
let params = { serviceNamePartial: inputValue || "" };
let op = [];
const tagServiceResp = await fetchApi({
url: `plugins/services?serviceType=tag`,
params: params
});
op = tagServiceResp?.data?.services;
return op?.map((obj) => ({
label: obj.displayName,
value: obj.displayName
}));
};
onFocusTagService = () => {
this.setState({ loadingOptions: true });
this.fetchTagService().then((opts) => {
this.setState({ defaultTagOptions: opts, loadingOptions: false });
});
};
deleteService = async (serviceId) => {
this.hideDeleteModal();
try {
this.setState({ blockUI: true });
await fetchApi({
url: `plugins/services/${serviceId}`,
method: "delete"
});
this.setState({ blockUI: false });
toast.success("Successfully deleted the service");
this.props.navigate(
this.state?.serviceDef?.name === "tag"
? "/policymanager/tag"
: "/policymanager/resource"
);
} catch (error) {
this.setState({ blockUI: false });
serverError(error);
console.error(
`Error occurred while deleting Service id - ${serviceId}! ${error}`
);
}
};
getAuditFilters = (auditFilters, serviceDef) => {
let auditFiltersArray = [];
auditFilters.map((item) => {
let obj = {};
Object.entries(item).map(([key, value]) => {
if (key === "isAudited") {
obj.isAudited = JSON.stringify(value);
}
if (key === "accessResult") {
obj.accessResult = { value: value, label: value };
}
if (key === "resources") {
obj.resources = {};
let lastResourceLevel = [];
Object.entries(item.resources)?.map(([key, value]) => {
let setResources = find(serviceDef?.resources, ["name", key]);
obj.resources[`resourceName-${setResources?.level}`] = setResources;
obj.resources[`value-${setResources?.level}`] = value.values?.map(
(m) => {
return { label: m, value: m };
}
);
if (setResources?.excludesSupported) {
obj.resources[`isExcludesSupport-${setResources.level}`] =
value?.isExcludes != false;
}
if (setResources?.recursiveSupported) {
obj.resources[`isRecursiveSupport-${setResources.level}`] =
value.isRecursive != false;
}
lastResourceLevel.push({
level: setResources?.level,
name: setResources?.name
});
});
lastResourceLevel = maxBy(lastResourceLevel, "level");
let setLastResources = find(serviceDef?.resources, [
"parent",
lastResourceLevel?.name
]);
if (setLastResources) {
obj.resources[`resourceName-${setLastResources.level}`] = {
label: "None",
value: "none"
};
}
}
if (key === "actions") {
obj.actions = value.map((action) => {
return { value: action, label: action };
});
}
if (key === "accessTypes") {
if (serviceDef.name == "tag") {
let accessTypes = groupBy(value, function (obj) {
return split(obj, ":", 1);
});
let accessTypeObj = {};
accessTypeObj["tableList"] = [];
Object.entries(accessTypes).map(([key, values]) =>
accessTypeObj["tableList"].push({
serviceName: key,
permission: values
})
);
obj.accessTypes = accessTypeObj;
} else {
obj.accessTypes = value.map((accessType) => {
let accessTypeObj = find(serviceDef.accessTypes, [
"name",
accessType
]);
return { value: accessType, label: accessTypeObj.label };
});
}
}
if (key === "users") {
obj.users = value.map((user) => {
return { value: user, label: user };
});
}
if (key === "groups") {
obj.groups = value.map((group) => {
return { value: group, label: group };
});
}
if (key === "roles") {
obj.roles = value.map((role) => {
return { value: role, label: role };
});
}
});
auditFiltersArray.push(obj);
});
return auditFiltersArray;
};
getConfigInfo = (configInfo) => {
if (configInfo !== undefined && configInfo !== "") {
let infoObj = JSON.parse(configInfo);
return infoObj.info !== undefined ? [infoObj.info] : [];
}
return [];
};
getServiceConfigs = (serviceDef) => {
if (serviceDef?.configs !== undefined) {
let formField = [];
const filterServiceConfigs = reject(serviceDef.configs, {
name: "ranger.plugin.audit.filters"
});
filterServiceConfigs.map((configParam) => {
this.configsJson[configParam.name] = configParam.name
.replaceAll(".", "_")
.replaceAll("-", "_");
this.initialValuesObj.configs[this.configsJson[configParam.name]] =
configParam.defaultValue;
let configInfo = this.getConfigInfo(configParam.uiHint);
switch (configParam.type) {
case "string":
case "int":
formField.push(
<Field
name={"configs." + this.configsJson[configParam.name]}
key={configParam.itemId}
validate={this.validateRequired(configParam.mandatory)}
>
{({ input, meta }) => (
<Row className="form-group">
<Col xs={3}>
<label className="form-label pull-right">
{configParam.label !== undefined
? configParam.label
: configParam.name}
{configParam.mandatory ? " * " : ""}
</label>
</Col>
<Col xs={4}>
<input
{...input}
type="text"
id={
meta.error && meta.touched
? "isError"
: "configs." + this.configsJson[configParam.name]
}
className={
meta.error && meta.touched
? "form-control border-danger"
: "form-control"
}
data-cy={
"configs." + this.configsJson[configParam.name]
}
/>
</Col>
{configInfo.length === 1 && (
<span className="service-config-info-icon">
<CustomPopover
title=""
content={configInfo}
placement="right"
icon="fa-fw fa fa-info-circle"
trigger={["hover", "focus"]}
dangerousInnerHtml={true}
/>
</span>
)}
{meta.error && meta.touched && (
<div className="col-sm-6 offset-sm-3 invalid-field">
{meta.error}
</div>
)}
</Row>
)}
</Field>
);
break;
case "enum": {
const paramEnum = serviceDef.enums.find(
(e) => e.name == configParam.subType
);
formField.push(
<Field
name={"configs." + this.configsJson[configParam.name]}
key={configParam.itemId}
validate={this.validateRequired(configParam.mandatory)}
>
{({ input, meta }) => (
<Row className="form-group">
<Col xs={3}>
<label className="form-label pull-right">
{configParam.label !== undefined
? configParam.label
: configParam.name}
{configParam.mandatory ? " * " : ""}
</label>
</Col>
<Col xs={4}>
<select
{...input}
id={
meta.error && meta.touched
? "isError"
: "configs." + this.configsJson[configParam.name]
}
className={
meta.error && meta.touched
? "form-control border-danger"
: "form-control"
}
>
{this.enumOptions(paramEnum)}
</select>
</Col>
{configInfo.length === 1 && (
<span className="d-inline">
<CustomPopover
title=""
content={configInfo}
placement="right"
icon="fa-fw fa fa-info-circle"
trigger={["hover", "focus"]}
/>
</span>
)}
{meta.error && meta.touched && (
<span className="col-sm-6 offset-sm-3 invalid-field">
{meta.error}
</span>
)}
</Row>
)}
</Field>
);
break;
}
case "bool":
formField.push(
<Field
name={"configs." + this.configsJson[configParam.name]}
key={configParam.itemId}
validate={this.validateRequired(configParam.mandatory)}
>
{({ input, meta }) => (
<Row className="form-group">
<Col xs={3}>
<label className="form-label pull-right">
{configParam.label !== undefined
? configParam.label
: configParam.name}
{configParam.mandatory ? " * " : ""}
</label>
</Col>
<Col xs={4}>
<select
{...input}
id={
meta.error && meta.touched
? "isError"
: "configs." + this.configsJson[configParam.name]
}
className={
meta.error && meta.touched
? "form-control border-danger"
: "form-control"
}
data-js="isAudited"
data-cy="isAudited"
>
{this.booleanOptions(configParam.subType)}
</select>
</Col>
{configInfo.length === 1 && (
<span className="d-inline">
<CustomPopover
title=""
content={configInfo}
placement="right"
icon="fa-fw fa fa-info-circle"
trigger={["hover", "focus"]}
/>
</span>
)}
{meta.error && meta.touched && (
<span className="col-sm-6 offset-sm-3 invalid-field">
{meta.error}
</span>
)}
</Row>
)}
</Field>
);
break;
case "password":
formField.push(
<Field
name={"configs." + this.configsJson[configParam.name]}
key={configParam.itemId}
validate={this.validateRequired(configParam.mandatory)}
>
{({ input, meta }) => (
<Row className="form-group">
<Col xs={3}>
<label className="form-label pull-right">
{configParam.label !== undefined
? configParam.label
: configParam.name}
{configParam.mandatory ? " * " : ""}
</label>
</Col>
<Col xs={4}>
<input
{...input}
type="password"
autoComplete="off"
id={
meta.error && meta.touched
? "isError"
: "configs." + this.configsJson[configParam.name]
}
className={
meta.error && meta.touched
? "form-control border-danger"
: "form-control"
}
/>
</Col>
{configInfo.length === 1 && (
<span className="d-inline">
<CustomPopover
title=""
content={configInfo}
placement="right"
icon="fa-fw fa fa-info-circle"
trigger={["hover", "focus"]}
/>
</span>
)}
{meta.error && meta.touched && (
<span className="col-sm-6 offset-sm-3 invalid-field">
{meta.error}
</span>
)}
</Row>
)}
</Field>
);
break;
}
});
return formField;
}
};
enumOptions = (paramEnum) => {
let optionField = [];
paramEnum.elements.map((e) => {
optionField.push(
<option value={e.name} key={e.name}>
{e.label}
</option>
);
});
return optionField;
};
booleanOptions = (paramBool) => {
let optionField = [];
let b = paramBool.split(":");
b = [b[0].substr(0, b[0].length - 4), b[1].substr(0, b[1].length - 5)];
b.map((e) => {
optionField.push(
<option value={e === "Yes" ? true : false} key={e}>
{e}
</option>
);
});
return optionField;
};
validateRequired = (isRequired) =>
isRequired ? (value) => (value ? undefined : "Required") : () => {};
validateServiceName = (value) =>
!RegexValidation.NAME_VALIDATION.regexforServiceNameValidation.test(value)
? RegexValidation.NAME_VALIDATION.serviceNameValidationMessage
: undefined;
composeValidators =
(...validators) =>
(value) =>
validators.reduce(
(error, validator) => error || validator(value),
undefined
);
SelectField = ({ input, ...rest }) => (
<Select {...input} {...rest} searchable />
);
AsyncSelectField = ({ input, ...rest }) => (
<AsyncSelect {...input} {...rest} cacheOptions />
);
fetchUsers = async (inputValue) => {
let params = { name: inputValue || "", isVisible: 1 };
let op = [];
try {
const userResp = await fetchApi({
url: "xusers/lookup/users",
params: params
});
op = userResp.data?.vXStrings;
} catch (error) {
console.error(`Error occurred while fetching Users ! ${error}`);
}
return map(op, function (obj) {
return { value: obj.value, label: obj.value };
});
};
fetchGroups = async (inputValue) => {
let params = { name: inputValue || "", isVisible: 1 };
let op = [];
try {
const groupResp = await fetchApi({
url: "xusers/lookup/groups",
params: params
});
op = groupResp.data?.vXStrings;
} catch (error) {
console.error(`Error occurred while fetching Groups ! ${error}`);
}
return map(op, function (obj) {
return { label: obj.value, value: obj.value };
});
};
fetchRoles = async (inputValue) => {
let params = { roleNamePartial: inputValue || "" };
let op = [];
try {
const roleResp = await fetchApi({
url: "roles/roles",
params: params
});
op = roleResp.data?.roles;
} catch (error) {
console.error(`Error occurred while fetching Roles ! ${error}`);
}
return map(op, function (obj) {
return { label: obj.name, value: obj.name };
});
};
ServiceDefnBreadcrumb = () => {
let serviceDetails = {};
serviceDetails["serviceDefId"] = this.state.serviceDef?.id;
serviceDetails["serviceId"] = this.props.params.serviceId;
if (this.state.serviceDef?.name === "tag") {
return commonBreadcrumb(
[
"TagBasedServiceManager",
this.props.params.serviceId !== undefined
? "ServiceEdit"
: "ServiceCreate"
],
serviceDetails
);
} else {
return commonBreadcrumb(
[
"ServiceManager",
this.props.params.serviceId !== undefined
? "ServiceEdit"
: "ServiceCreate"
],
serviceDetails
);
}
};
render() {
return (
<React.Fragment>
<div className="clearfix">
<div className="header-wraper">
<h3 className="wrap-header bold">
{this.props.params.serviceId !== undefined ? `Edit` : `Create`}{" "}
Service
</h3>
{this.ServiceDefnBreadcrumb()}
</div>
</div>
{this.state.loader ? (
<Loader />
) : (
<div className="wrap">
<div className="row">
<div className="col-sm-12">
<Form
onSubmit={this.onSubmit}
mutators={{
...arrayMutators
}}
initialValues={
this.props.params.serviceId !== undefined
? this.state.editInitialValues
: this.state.createInitialValues
}
render={({
handleSubmit,
submitting,
values,
invalid,
errors,
form: {
mutators: { push: addItem }
}
}) => (
<form
onSubmit={(event) => {
let selector;
if (invalid) {
if (errors?.configs !== undefined) {
selector =
document.getElementById("isError") ||
document.querySelector(
`input[name=${Object.keys(errors)[0]}]`
) ||
document.querySelector(
`input[name="configs.${
Object.keys(errors.configs)[0]
}`
);
} else {
selector =
document.getElementById("isError") ||
document.querySelector(
`input[name=${Object.keys(errors)[0]}]`
);
}
scrollToError(selector);
}
handleSubmit(event);
}}
>
<Row>
<Col xs={12}>
<p className="form-header">Service Details :</p>
<Field
name="name"
id="name"
validate={this.composeValidators(
this.validateRequired(true),
this.validateServiceName
)}
>
{({ input, meta }) => (
<Row className="form-group">
<Col xs={3}>
<label className="form-label pull-right">
Service Name *
</label>
</Col>
<Col xs={4}>
<input
{...input}
type="text"
id={
meta.error && meta.touched
? "isError"
: "name"
}
className={
meta.error && meta.touched
? "form-control border-danger"
: "form-control"
}
data-cy="name"
/>
</Col>
{meta.error && meta.touched && (
<span className="col-sm-6 offset-sm-3 invalid-field">
{meta.error}
</span>
)}
</Row>
)}
</Field>
<Field
name="displayName"
validate={this.composeValidators(
this.validateServiceName)}>
{({ input, meta }) => (
<Row className="form-group">
<Col xs={3}>
<label className="form-label pull-right">
Display Name
</label>
</Col>
<Col xs={4}>
<input
{...input}
type="text"
className={
meta.error && meta.touched
? "form-control border border-danger"
: "form-control"
}
data-cy="displayName"
/>
</Col>
{meta.error && meta.touched && (
<span className="col-sm-6 offset-sm-3 invalid-field">
{meta.error}
</span>
)}
</Row>
)}
</Field>
<Field name="description">
{({ input, meta }) => (
<Row className="form-group">
<Col xs={3}>
<label className="form-label pull-right">
Description
</label>
</Col>
<Col xs={4}>
<textarea
{...input}
className={
meta.error && meta.touched
? "form-control border border-danger"
: "form-control"
}
data-cy="description"
/>
</Col>
{meta.error && meta.touched && (
<span className="col-sm-6 offset-sm-3 invalid-field">
{meta.error}
</span>
)}
</Row>
)}
</Field>
<Row className="form-group">
<Col xs={3}>
<label className="form-label pull-right">
Active Status
</label>
</Col>
<Col xs={4}>
<span>
<div className="form-control border-0">
<div className="form-check form-check-inline">
<Field
name="isEnabled"
component="input"
type="radio"
className="form-check-input"
value="true"
data-cy="isEnabled"
/>
<label className="form-check-label">
Enabled
</label>
</div>
<div className="form-check form-check-inline">
{" "}
<Field
name="isEnabled"
className="form-check-input"
component="input"
type="radio"
value="false"
data-cy="isEnabled"
/>
<label className="form-check-label">
Disabled
</label>
</div>
</div>
</span>
</Col>
</Row>
{this.state?.serviceDef?.name !== "tag" && (
<Row className="form-group">
<Col xs={3}>
<label className="form-label pull-right">
Select Tag Service
</label>
</Col>
<Col xs={4}>
<Field
name="tagService"
component={this.AsyncSelectField}
loadOptions={this.fetchTagService}
onFocus={() => {
this.onFocusTagService();
}}
defaultOptions={this.state.defaultTagOptions}
placeholder="Select Tag Service"
isLoading={this.state.loadingOptions}
isClearable={true}
cacheOptions
/>
</Col>
</Row>
)}
</Col>
</Row>
<Row>
<Col xs={12}>
<p className="form-header">Config Properties :</p>
{this.getServiceConfigs(this.state.serviceDef)}
<Row className="form-group">
<Col xs={3}>
<label className="form-label pull-right">
Add New Configurations
</label>
</Col>
<Col xs={5}>
<Table bordered size="sm" className="no-bg-color">
<thead>
<tr>
<th className="text-center">Name</th>
<th className="text-center" colSpan="2">
Value
</th>
</tr>
</thead>
<tbody>
<FieldArray name="customConfigs">
{({ fields }) =>
fields.map((name, index) => (
<tr key={name}>
<td className="text-center">
<Field
name={`${name}.name`}
component="input"
className="form-control"
data-js="name"
data-cy="name"
/>
</td>
<td className="text-center">
<Field
name={`${name}.value`}
component="input"
className="form-control"
data-js="value"
data-cy="value"
/>
</td>
<td className="text-center">
<Button
variant="danger"
size="sm"
className="btn-mini"
title="Remove"
onClick={() =>
fields.remove(index)
}
data-action="delete"
data-cy="delete"
>
<i className="fa-fw fa fa-remove"></i>
</Button>
</td>
</tr>
))
}
</FieldArray>
</tbody>
</Table>
</Col>
</Row>
<Row className="form-group">
<div className="col-sm-4 offset-sm-3">
<Button
variant="outline-dark"
className="btn-mini btn-sm"
onClick={() =>
addItem("customConfigs", undefined)
}
data-action="addGroup"
data-cy="addGroup"
>
<i className="fa-fw fa fa-plus"></i>
</Button>
</div>
</Row>
</Col>
</Row>
<div className="row">
<div className="col-sm-12">
<div className="form-group row form-header p-0">
<label className="col-sm-1 col-form-label form-check-label mr-2">
Audit Filter :
</label>
<div className="col-sm-10 mt-2">
<Field
name="isAuditFilter"
component="input"
type="checkbox"
className="form-check-input"
/>
</div>
</div>
<div className="row">
<div className="col-sm-12">
<Condition when="isAuditFilter" is={true}>
<ServiceAuditFilter
serviceDetails={this.state.service}
serviceDefDetails={this.state.serviceDef}
fetchUsersData={this.fetchUsers}
fetchGroupsData={this.fetchGroups}
fetchRolesData={this.fetchRoles}
addAuditFilter={addItem}
formValues={values}
/>
</Condition>
</div>
</div>
<div className="form-group row mt-2 text-right">
<div className="col-sm-3 col-form-label">
{!isEmpty(this.state.serviceDef) && (
<TestConnection
getTestConnConfigs={
this.getServiceConfigsToSave
}
formValues={values}
/>
)}
</div>
</div>
</div>
</div>
<div className="row form-actions">
<div className="col-md-9 offset-md-3">
<Button
variant="primary"
type="submit"
className="btn-sm"
size="sm"
disabled={submitting}
data-id="save"
data-cy="save"
>
{this.props.params.serviceId !== undefined
? `Save`
: `Add`}
</Button>
<Button
variant="secondary"
type="button"
className="btn-sm"
size="sm"
onClick={() => {
if (this.props?.location?.state != "services") {
if (
this?.props?.params?.serviceId !== undefined
) {
return this.props.navigate(
`/service/${this.props.params.serviceId}/policies/${RangerPolicyType.RANGER_ACCESS_POLICY_TYPE.value}`
);
} else {
return this.props.navigate(
this.state?.serviceDef?.name === "tag"
? "/policymanager/tag"
: "/policymanager/resource"
);
}
} else {
return this.props.navigate(
this.state?.serviceDef?.name === "tag"
? "/policymanager/tag"
: "/policymanager/resource"
);
}
}}
data-id="cancel"
data-cy="cancel"
>
Cancel
</Button>
{this.props.params.serviceId !== undefined && (
<Button
variant="danger"
type="button"
className="btn-sm"
size="sm"
onClick={() => {
this.showDeleteModal();
}}
>
Delete
</Button>
)}
</div>
{this.props.params.serviceId !== undefined && (
<Modal
show={this.state.showDelete}
onHide={this.hideDeleteModal}
>
<Modal.Header closeButton>
<span className="text-word-break">
Are you sure want to delete service&nbsp;"
<b>{`${this?.state?.service?.displayName}`}</b>"
?
</span>
</Modal.Header>
<Modal.Footer>
<Button
variant="secondary"
size="sm"
title="Cancel"
onClick={this.hideDeleteModal}
>
Cancel
</Button>
<Button
variant="primary"
size="sm"
title="Yes"
onClick={() =>
this.deleteService(
this.props.params.serviceId
)
}
>
Yes
</Button>
</Modal.Footer>
</Modal>
)}
</div>
</form>
)}
/>
</div>
</div>
<BlockUi isUiBlock={this.state.blockUI} />
</div>
)}
</React.Fragment>
);
}
}
export default withRouter(ServiceForm);