This is an automated email from the ASF dual-hosted git repository.
ppawar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/atlas.git
The following commit(s) were added to refs/heads/master by this push:
new 30244d710 ATLAS-5169: Atlas UI creates multi-valued Business Metadata
with SINGLE cardinality (#492)
30244d710 is described below
commit 30244d7107302db513dc2d54c1c19345dc9e340b
Author: Prasad Pawar <[email protected]>
AuthorDate: Fri Jan 2 10:06:39 2026 +0530
ATLAS-5169: Atlas UI creates multi-valued Business Metadata with SINGLE
cardinality (#492)
---
.../BusinessMetadataAtrributeForm.tsx | 113 ++++++++++++++-------
.../BusinessMetadata/BusinessMetadataForm.tsx | 23 ++++-
.../BusinessMetadataAtrribute.tsx | 16 ++-
.../BusinessMetadataDetailsLayout.tsx | 11 +-
dashboardv2/public/css/scss/business-metadata.scss | 8 ++
.../BusinessMetadataAttributeItemView_tmpl.html | 12 ++-
.../BusinessMetadataAttrTableLayoutView.js | 30 ++++++
.../BusinessMetadataAttributeItemView.js | 79 ++++++++++++++
.../BusinessMetadataTableLayoutView.js | 18 +++-
.../CreateBusinessMetadataLayoutView.js | 28 ++++-
dashboardv3/public/css/scss/business-metadata.scss | 8 ++
.../BusinessMetadataAttributeItemView_tmpl.html | 12 ++-
.../BusinessMetadataAttrTableLayoutView.js | 30 ++++++
.../BusinessMetadataAttributeItemView.js | 79 ++++++++++++++
.../BusinessMetadataTableLayoutView.js | 18 +++-
.../CreateBusinessMetadataLayoutView.js | 28 ++++-
16 files changed, 460 insertions(+), 53 deletions(-)
diff --git
a/dashboard/src/views/BusinessMetadata/BusinessMetadataAtrributeForm.tsx
b/dashboard/src/views/BusinessMetadata/BusinessMetadataAtrributeForm.tsx
index 1f4bc2acf..883015078 100644
--- a/dashboard/src/views/BusinessMetadata/BusinessMetadataAtrributeForm.tsx
+++ b/dashboard/src/views/BusinessMetadata/BusinessMetadataAtrributeForm.tsx
@@ -37,7 +37,9 @@ import {
tooltipClasses,
TooltipProps,
styled,
- FilterOptionsState
+ FilterOptionsState,
+ ToggleButton,
+ ToggleButtonGroup
} from "@mui/material";
import { customSortBy, isEmpty, serverError } from "@utils/Utils";
import { Controller, useForm } from "react-hook-form";
@@ -463,41 +465,82 @@ const BusinessMetadataAttributeForm = ({
watched?.[index] &&
watched?.[index]?.multiValueSelect) ||
isEmpty(editbmAttribute)) && (
- <Controller
- control={control}
- name={`attributeDefs.${index}.multiValueSelect` as const}
- key={`attributeDefs.${index}.multiValueSelect`}
- data-cy={`attributeDefs.${index}.multiValueSelect`}
- defaultValue={field?.multiValueSelect}
- render={({ field: { value, onChange } }) => (
- <>
- <Grid
- container
- columnSpacing={{ xs: 1, sm: 2, md: 2 }}
- marginBottom="1rem"
- alignItems="center"
- >
- <Grid item md={3} textAlign="right">
- <InputLabel>Enable Multivalues</InputLabel>
- </Grid>
- <Grid item md={7}>
- {" "}
- <FormControlLabel
- control={
- <Checkbox
- disabled={isEmpty(editbmAttribute) ? false :
true}
- size="small"
- checked={value}
- onChange={onChange}
+ <>
+ <Controller
+ control={control}
+ name={`attributeDefs.${index}.multiValueSelect` as const}
+ key={`attributeDefs.${index}.multiValueSelect`}
+ data-cy={`attributeDefs.${index}.multiValueSelect`}
+ defaultValue={field?.multiValueSelect}
+ render={({ field: { value, onChange } }) => (
+ <>
+ <Grid
+ container
+ columnSpacing={{ xs: 1, sm: 2, md: 2 }}
+ marginBottom="1rem"
+ alignItems="center"
+ >
+ <Grid item md={3} textAlign="right">
+ <InputLabel>Enable Multivalues</InputLabel>
+ </Grid>
+ <Grid item md={7}>
+ <Stack direction="row" spacing={2}
alignItems="center">
+ <FormControlLabel
+ control={
+ <Checkbox
+ disabled={isEmpty(editbmAttribute) ? false :
true}
+ size="small"
+ checked={value}
+ onChange={(e) => {
+ onChange(e.target.checked);
+ // Reset cardinality toggle when
multivalues is unchecked
+ if (!e.target.checked) {
+ attributeDefsSetValue(
+
`attributeDefs.${index}.cardinalityToggle`,
+ "SET"
+ );
+ }
+ }}
+ />
+ }
+ label={undefined}
/>
- }
- label={undefined}
- />
- </Grid>{" "}
- </Grid>
- </>
- )}
- />
+ {value && (
+ <Controller
+ control={control}
+
name={`attributeDefs.${index}.cardinalityToggle` as const}
+
key={`attributeDefs.${index}.cardinalityToggle`}
+ defaultValue={field?.cardinalityToggle ||
"SET"}
+ render={({ field: { value: toggleValue,
onChange: toggleOnChange } }) => (
+ <ToggleButtonGroup
+ size="small"
+ value={toggleValue || "SET"}
+ exclusive
+ disabled={!isEmpty(editbmAttribute)}
+ onChange={(e, newValue) => {
+ if (newValue !== null) {
+ toggleOnChange(newValue);
+ }
+ }}
+ aria-label="cardinality toggle"
+ >
+ <ToggleButton value="SET" aria-label="SET">
+ SET
+ </ToggleButton>
+ <ToggleButton value="LIST"
aria-label="LIST">
+ LIST
+ </ToggleButton>
+ </ToggleButtonGroup>
+ )}
+ />
+ )}
+ </Stack>
+ </Grid>
+ </Grid>
+ </>
+ )}
+ />
+ </>
)}
{watched?.[index] && watched?.[index]?.typeName == "string" && (
<Controller
diff --git a/dashboard/src/views/BusinessMetadata/BusinessMetadataForm.tsx
b/dashboard/src/views/BusinessMetadata/BusinessMetadataForm.tsx
index 5fcbb22ed..4a3ed8a18 100644
--- a/dashboard/src/views/BusinessMetadata/BusinessMetadataForm.tsx
+++ b/dashboard/src/views/BusinessMetadata/BusinessMetadataForm.tsx
@@ -115,7 +115,9 @@ const BusinessMetaDataForm = ({
...(currentTypeName == "enumeration" && {
enumValues: enumTypeOptions
}),
- multiValueSelect: str.indexOf("<") != -1 ? true : false
+ multiValueSelect: str.indexOf("<") != -1 ? true : false,
+ // Set cardinalityToggle based on existing cardinality (SET or LIST),
default to SET
+ cardinalityToggle: editbmAttribute?.cardinality === "LIST" ? "LIST" :
"SET"
}
];
const dispatchState = useAppDispatch();
@@ -142,6 +144,7 @@ const BusinessMetaDataForm = ({
},
isOptional: true,
cardinality: "SINGLE",
+ cardinalityToggle: "SET", // Default toggle value when
multivalues is enabled
valuesMinCount: 0,
valuesMaxCount: 1,
isUnique: false,
@@ -157,7 +160,7 @@ const BusinessMetaDataForm = ({
control,
name: "attributeDefs",
rules: {
- required: true
+ required: false // Allow creating BM without attributes
}
});
@@ -203,7 +206,9 @@ const BusinessMetaDataForm = ({
...(currentTypeName == "enumeration" && {
enumValues: enumTypeOptions
}),
- multiValueSelect: str.indexOf("<") != -1 ? true : false
+ multiValueSelect: str.indexOf("<") != -1 ? true : false,
+ // Set cardinalityToggle based on existing cardinality (SET or LIST),
default to SET
+ cardinalityToggle: editbmAttribute?.cardinality === "LIST" ? "LIST" :
"SET"
}
];
if (!isEmpty(editbmAttribute)) {
@@ -255,10 +260,19 @@ const BusinessMetaDataForm = ({
let attributes = !isEmpty(attributeDefsData)
? attributeDefsData.map((item) => {
- const { multiValueSelect, enumType, enumValues, ...rest } = item;
+ const { multiValueSelect, enumType, enumValues, cardinality,
cardinalityToggle, ...rest } = item;
+
+ // Determine cardinality based on multiValueSelect and cardinality
toggle
+ let finalCardinality = "SINGLE";
+ if (multiValueSelect) {
+ // If multivalues is enabled, use the cardinalityToggle (SET or
LIST)
+ // Default to SET if not specified
+ finalCardinality = cardinalityToggle === "LIST" ? "LIST" :
(cardinality === "LIST" ? "LIST" : "SET");
+ }
const baseObj = {
...rest,
+ cardinality: finalCardinality,
options: {
applicableEntityTypes: JSON.stringify(
rest.options.applicableEntityTypes
@@ -508,6 +522,7 @@ const BusinessMetaDataForm = ({
},
isOptional: true,
cardinality: "SINGLE",
+ cardinalityToggle: "SET", // Default toggle
value when multivalues is enabled
valuesMinCount: 0,
valuesMaxCount: 1,
isUnique: false,
diff --git
a/dashboard/src/views/DetailPage/BusinessMetadataDetails/BusinessMetadataAtrribute.tsx
b/dashboard/src/views/DetailPage/BusinessMetadataDetails/BusinessMetadataAtrribute.tsx
index 8ed291d90..af9c1a355 100644
---
a/dashboard/src/views/DetailPage/BusinessMetadataDetails/BusinessMetadataAtrribute.tsx
+++
b/dashboard/src/views/DetailPage/BusinessMetadataDetails/BusinessMetadataAtrribute.tsx
@@ -84,6 +84,18 @@ const BusinessMetadataAtrribute = ({ componentProps, row }:
any) => {
header: "Enable Multivalues",
enableSorting: false
},
+ {
+ accessorKey: "cardinality",
+ cell: (info: any) =>
+ !isEmpty(info.getValue()) ? (
+ <Typography>{info.getValue()}</Typography>
+ ) : (
+ <span>N/A</span>
+ ),
+ header: "Cardinality",
+ enableSorting: true,
+ show: true
+ },
{
accessorKey: "maxStrLength",
cell: (info: any) => {
@@ -217,7 +229,9 @@ const BusinessMetadataAtrribute = ({ componentProps, row }:
any) => {
...(currentTypeName == "enumeration" && {
enumValues: enumTypeOptions
}),
- multiValueSelect: str.indexOf("<") != -1 ? true : false
+ multiValueSelect: str.indexOf("<") != -1 ? true : false,
+ // Set cardinalityToggle based on existing cardinality
(SET or LIST), default to SET
+ cardinalityToggle: original?.cardinality === "LIST" ?
"LIST" : "SET"
}
];
setForm(true);
diff --git
a/dashboard/src/views/DetailPage/BusinessMetadataDetails/BusinessMetadataDetailsLayout.tsx
b/dashboard/src/views/DetailPage/BusinessMetadataDetails/BusinessMetadataDetailsLayout.tsx
index 1638565ff..b3d419246 100644
---
a/dashboard/src/views/DetailPage/BusinessMetadataDetails/BusinessMetadataDetailsLayout.tsx
+++
b/dashboard/src/views/DetailPage/BusinessMetadataDetails/BusinessMetadataDetailsLayout.tsx
@@ -179,10 +179,19 @@ const BusinessMetadataDetailsLayout = () => {
let attributeDefsData = [...formAttributes];
let attributes = attributeDefsData.map((item) => {
- const { multiValueSelect, enumValues, enumType, ...rest } = item;
+ const { multiValueSelect, enumValues, enumType, cardinality,
cardinalityToggle, ...rest } = item;
+
+ // Determine cardinality based on multiValueSelect and cardinality toggle
+ let finalCardinality = "SINGLE";
+ if (multiValueSelect) {
+ // If multivalues is enabled, use the cardinalityToggle (SET or LIST)
+ // Default to SET if not specified
+ finalCardinality = cardinalityToggle === "LIST" ? "LIST" :
(cardinality === "LIST" ? "LIST" : "SET");
+ }
return {
...rest,
+ cardinality: finalCardinality,
...{
options: {
applicableEntityTypes: JSON.stringify(
diff --git a/dashboardv2/public/css/scss/business-metadata.scss
b/dashboardv2/public/css/scss/business-metadata.scss
index 5bcbb5df9..2cadd14bf 100644
--- a/dashboardv2/public/css/scss/business-metadata.scss
+++ b/dashboardv2/public/css/scss/business-metadata.scss
@@ -240,4 +240,12 @@
.business-metadata-detail-attr-key {
width: 30%;
}
+}
+
+// Disabled state for cardinality toggle buttons
+.cardinality-btn.disabled,
+.cardinality-btn[disabled] {
+ opacity: 0.6;
+ cursor: not-allowed;
+ pointer-events: none;
}
\ No newline at end of file
diff --git
a/dashboardv2/public/js/templates/business_metadata/BusinessMetadataAttributeItemView_tmpl.html
b/dashboardv2/public/js/templates/business_metadata/BusinessMetadataAttributeItemView_tmpl.html
index aedb6dabe..8ffb689b4 100644
---
a/dashboardv2/public/js/templates/business_metadata/BusinessMetadataAttributeItemView_tmpl.html
+++
b/dashboardv2/public/js/templates/business_metadata/BusinessMetadataAttributeItemView_tmpl.html
@@ -68,7 +68,17 @@
<div class="form-group" data-id="multiValueSelect">
<label class="control-label col-sm-3" for="multiValSelect">Enable
Multivalues</label>
<div class="col-sm-8">
- <input type="checkbox" class="form-check-input multi-value-select"
data-id="multiValueSelectStatus">
+ <div class="row">
+ <div class="col-sm-6">
+ <input type="checkbox" class="form-check-input
multi-value-select" data-id="multiValueSelectStatus">
+ </div>
+ <div class="col-sm-6" data-id="cardinalityToggleContainer"
style="display: none;">
+ <div class="btn-group" role="group"
data-id="cardinalityToggle">
+ <button type="button" class="btn btn-sm btn-default
cardinality-btn" data-value="SET" data-id="cardinalitySET">SET</button>
+ <button type="button" class="btn btn-sm btn-default
cardinality-btn active" data-value="LIST"
data-id="cardinalityLIST">LIST</button>
+ </div>
+ </div>
+ </div>
</div>
</div>
<div class="form-group enumtype-container"
data-id="enumTypeSelectorContainer">
diff --git
a/dashboardv2/public/js/views/business_metadata/BusinessMetadataAttrTableLayoutView.js
b/dashboardv2/public/js/views/business_metadata/BusinessMetadataAttrTableLayoutView.js
index 37058bbe2..37e6e7a83 100644
---
a/dashboardv2/public/js/views/business_metadata/BusinessMetadataAttrTableLayoutView.js
+++
b/dashboardv2/public/js/views/business_metadata/BusinessMetadataAttrTableLayoutView.js
@@ -108,6 +108,15 @@ define(['require',
attrDetails.typeName =
attrObj.typeName.replace("array<", "").replace(">", "");
attrDetails.multiValued = true;
}
+ // Set cardinalityToggle based on existing
cardinality (SET or LIST), default to SET
+ if (attrObj.cardinality) {
+ attrDetails.cardinalityToggle =
(attrObj.cardinality === "LIST") ? "LIST" : "SET";
+ } else {
+ // If cardinality is not set but it's an array
type, default to SET
+ if (attrObj.typeName.includes('array')) {
+ attrDetails.cardinalityToggle = "SET";
+ }
+ }
}
});
this.showDetails = false;
@@ -197,6 +206,27 @@ define(['require',
}
})
},
+ cardinality: {
+ label: "Cardinality",
+ cell: "html",
+ editable: false,
+ sortable: true,
+ formatter: _.extend({},
Backgrid.CellFormatter.prototype, {
+ fromRaw: function(rawValue, model) {
+ var cardinality = model.get('cardinality');
+ if (!cardinality) {
+ // If cardinality is not set, determine
from typeName
+ if
(model.get('typeName').indexOf('array<') > -1) {
+ // Default to SET for array types if
cardinality is not specified
+ cardinality = "SET";
+ } else {
+ cardinality = "SINGLE";
+ }
+ }
+ return _.escape(cardinality);
+ }
+ })
+ },
maxStrLength: {
label: "Max Length",
cell: "html",
diff --git
a/dashboardv2/public/js/views/business_metadata/BusinessMetadataAttributeItemView.js
b/dashboardv2/public/js/views/business_metadata/BusinessMetadataAttributeItemView.js
index 5190db031..6dddb8f65 100644
---
a/dashboardv2/public/js/views/business_metadata/BusinessMetadataAttributeItemView.js
+++
b/dashboardv2/public/js/views/business_metadata/BusinessMetadataAttributeItemView.js
@@ -48,6 +48,10 @@ define(['require',
enumValueSelector: "[data-id='enumValueSelector']",
multiValueSelect: "[data-id='multiValueSelect']",
multiValueSelectStatus: "[data-id='multiValueSelectStatus']",
+ cardinalityToggleContainer:
"[data-id='cardinalityToggleContainer']",
+ cardinalityToggle: "[data-id='cardinalityToggle']",
+ cardinalitySET: "[data-id='cardinalitySET']",
+ cardinalityLIST: "[data-id='cardinalityLIST']",
stringLengthContainer: "[data-id='stringLengthContainer']",
stringLengthValue: "[data-id='stringLength']",
createNewEnum: "[data-id='createNewEnum']"
@@ -122,15 +126,72 @@ define(['require',
this.model.set({ "enumValues":
this.ui.enumValueSelector.val() });
};
events["change " + this.ui.multiValueSelectStatus] =
function(e) {
+ var that = this;
this.model.set({ "multiValueSelect": e.target.checked });
var typename = this.model.get('typeName');
if (e.target.checked) {
typename = "array<" + typename + ">";
+ // Show cardinality toggle when multivalues is enabled
+ that.ui.cardinalityToggleContainer.show();
+ // Disable cardinality toggle buttons in edit mode
+ if (that.isAttrEdit) {
+ that.ui.cardinalitySET.attr('disabled',
'disabled');
+ that.ui.cardinalityLIST.attr('disabled',
'disabled');
+ that.ui.cardinalitySET.addClass('disabled');
+ that.ui.cardinalityLIST.addClass('disabled');
+ } else {
+ that.ui.cardinalitySET.removeAttr('disabled');
+ that.ui.cardinalityLIST.removeAttr('disabled');
+ that.ui.cardinalitySET.removeClass('disabled');
+ that.ui.cardinalityLIST.removeClass('disabled');
+ }
+ // Set default cardinality to SET if not already set
+ if (!that.model.get('cardinalityToggle')) {
+ that.model.set({ "cardinalityToggle": "SET" });
+ that.ui.cardinalitySET.addClass('active');
+ that.ui.cardinalityLIST.removeClass('active');
+ } else {
+ // Set active button based on current value
+ var currentCardinality =
that.model.get('cardinalityToggle');
+ if (currentCardinality === "LIST") {
+ that.ui.cardinalityLIST.addClass('active');
+ that.ui.cardinalitySET.removeClass('active');
+ } else {
+ that.ui.cardinalitySET.addClass('active');
+ that.ui.cardinalityLIST.removeClass('active');
+ }
+ }
} else {
typename = typename.replace('array<', '').replace('>',
'');
+ // Hide cardinality toggle when multivalues is disabled
+ that.ui.cardinalityToggleContainer.hide();
+ // Reset cardinality toggle
+ that.model.set({ "cardinalityToggle": "SET" });
}
this.model.set({ "typeName": typename });
};
+ events["click " + this.ui.cardinalitySET] = function(e) {
+ e.preventDefault();
+ var that = this;
+ // Disable cardinality toggle in edit mode
+ if (that.isAttrEdit) {
+ return;
+ }
+ that.model.set({ "cardinalityToggle": "SET" });
+ that.ui.cardinalitySET.addClass('active');
+ that.ui.cardinalityLIST.removeClass('active');
+ };
+ events["click " + this.ui.cardinalityLIST] = function(e) {
+ e.preventDefault();
+ var that = this;
+ // Disable cardinality toggle in edit mode
+ if (that.isAttrEdit) {
+ return;
+ }
+ that.model.set({ "cardinalityToggle": "LIST" });
+ that.ui.cardinalityLIST.addClass('active');
+ that.ui.cardinalitySET.removeClass('active');
+ };
events["change " + this.ui.entityTypeSelector] = function(e) {
var options = this.model.get('options') || {};
options.applicableEntityTypes =
JSON.stringify(this.ui.entityTypeSelector.val());
@@ -223,6 +284,24 @@ define(['require',
this.ui.multiValueSelect.show();
$(this.ui.multiValueSelectStatus).prop('checked',
true).trigger('change');
this.ui.multiValueSelectStatus.attr("disabled",
"false");
+ // Set cardinality toggle based on existing
cardinality (SET or LIST), default to SET
+ var existingCardinality =
this.model.get("cardinality");
+ var cardinalityToggle = (existingCardinality ===
"LIST") ? "LIST" : "SET";
+ this.model.set({ "cardinalityToggle":
cardinalityToggle });
+ if (cardinalityToggle === "LIST") {
+ this.ui.cardinalityLIST.addClass('active');
+ this.ui.cardinalitySET.removeClass('active');
+ } else {
+ this.ui.cardinalitySET.addClass('active');
+ this.ui.cardinalityLIST.removeClass('active');
+ }
+ // Disable cardinality toggle buttons in edit mode
+ if (this.isAttrEdit) {
+ this.ui.cardinalitySET.attr('disabled',
'disabled');
+ this.ui.cardinalityLIST.attr('disabled',
'disabled');
+ this.ui.cardinalitySET.addClass('disabled');
+ this.ui.cardinalityLIST.addClass('disabled');
+ }
}
}
},
diff --git
a/dashboardv2/public/js/views/business_metadata/BusinessMetadataTableLayoutView.js
b/dashboardv2/public/js/views/business_metadata/BusinessMetadataTableLayoutView.js
index c02236843..53e14b9c4 100644
---
a/dashboardv2/public/js/views/business_metadata/BusinessMetadataTableLayoutView.js
+++
b/dashboardv2/public/js/views/business_metadata/BusinessMetadataTableLayoutView.js
@@ -244,11 +244,11 @@ define(['require',
accordion: false,
alwaysVisible: true,
expand: function(el, model) {
- el.attr('colspan', '8');
+ el.attr('colspan', '9');
var attrValues = '',
attrTable = $('table'),
attrTableBody = $('tbody'),
- attrTableHeading = "<thead><td
style='display:table-cell'><b>Attribute</b></td><td
style='display:table-cell'><b>Type</b></td><td
style='display:table-cell'><b>Search Weight</b></td><td
style='display:table-cell'><b>Enable Multivalues</b></td><td
style='display:table-cell'><b>Max Length</b></td><td
style='display:table-cell'><b>Applicable Type(s)</b></td><td
style='display:table-cell'><b>Action</b></td></thead>",
+ attrTableHeading = "<thead><td
style='display:table-cell'><b>Attribute</b></td><td
style='display:table-cell'><b>Type</b></td><td
style='display:table-cell'><b>Search Weight</b></td><td
style='display:table-cell'><b>Enable Multivalues</b></td><td
style='display:table-cell'><b>Cardinality</b></td><td
style='display:table-cell'><b>Max Length</b></td><td
style='display:table-cell'><b>Applicable Type(s)</b></td><td
style='display:table-cell'><b>Action</b></td> [...]
attrRow = '',
attrTableDetails = '';
if (model.attributes &&
model.attributes.attributeDefs.length) {
@@ -256,7 +256,8 @@ define(['require',
var applicableEntityTypes = '',
typeName = attrObj.typeName,
multiSelect = '',
- maxString = 'NA';
+ maxString = 'NA',
+ cardinality = 'SINGLE';
if (attrObj.options &&
attrObj.options.applicableEntityTypes) {
var entityTypes =
JSON.parse(attrObj.options.applicableEntityTypes);
_.each(entityTypes, function(values) {
@@ -266,12 +267,21 @@ define(['require',
if (typeName.includes('array')) {
typeName = _.escape(typeName);
multiSelect = 'checked';
+ // Determine cardinality - use
existing cardinality value or default to SET
+ if (attrObj.cardinality) {
+ cardinality = attrObj.cardinality;
+ } else {
+ cardinality = 'SET';
+ }
+ } else {
+ // For non-array types, cardinality is
SINGLE
+ cardinality = 'SINGLE';
}
if (typeName.includes('string') &&
attrObj.options && attrObj.options.maxStrLength) {
maxString =
attrObj.options.maxStrLength;
}
- attrRow += "<tr> <td
style='display:table-cell'>" + _.escape(attrObj.name) + "</td><td
style='display:table-cell'>" + typeName + "</td><td
style='display:table-cell'>" + _.escape(attrObj.searchWeight) + "</td><td
style='display:table-cell'><input type='checkbox' class='form-check-input
multi-value-select' " + multiSelect + " disabled='disabled'> </td><td
style='display:table-cell'>" + maxString + "</td><td
style='display:table-cell'>" + applicableEntit [...]
+ attrRow += "<tr> <td
style='display:table-cell'>" + _.escape(attrObj.name) + "</td><td
style='display:table-cell'>" + typeName + "</td><td
style='display:table-cell'>" + _.escape(attrObj.searchWeight) + "</td><td
style='display:table-cell'><input type='checkbox' class='form-check-input
multi-value-select' " + multiSelect + " disabled='disabled'> </td><td
style='display:table-cell'>" + _.escape(cardinality) + "</td><td
style='display:table-cell'>" + max [...]
});
var adminText = '<div class="row"><div
class="col-sm-12 attr-details"><table style="padding: 50px;">' +
attrTableHeading + attrRow + '</table></div></div>';
$(el).append($('<div>').html(adminText));
diff --git
a/dashboardv2/public/js/views/business_metadata/CreateBusinessMetadataLayoutView.js
b/dashboardv2/public/js/views/business_metadata/CreateBusinessMetadataLayoutView.js
index 165b99500..200e4ad16 100644
---
a/dashboardv2/public/js/views/business_metadata/CreateBusinessMetadataLayoutView.js
+++
b/dashboardv2/public/js/views/business_metadata/CreateBusinessMetadataLayoutView.js
@@ -216,6 +216,27 @@ define(['require',
}
},
+ processAttributes: function(attributes) {
+ var that = this;
+ return attributes.map(function(item) {
+ var multiValueSelect = item.multiValueSelect || false;
+ var cardinalityToggle = item.cardinalityToggle || "SET";
+
+ // Determine cardinality based on multiValueSelect and
cardinality toggle
+ var finalCardinality = "SINGLE";
+ if (multiValueSelect) {
+ // If multivalues is enabled, use the
cardinalityToggle (SET or LIST)
+ // Default to SET if not specified
+ finalCardinality = cardinalityToggle === "LIST" ?
"LIST" : (item.cardinality === "LIST" ? "LIST" : "SET");
+ }
+
+ // Remove cardinalityToggle from the final object as it's
not part of API
+ var processedItem = _.omit(item, 'cardinalityToggle');
+ processedItem.cardinality = finalCardinality;
+
+ return processedItem;
+ });
+ },
onCreateBusinessMetadata: function() {
var that = this;
if (this.validateValues()) {
@@ -227,6 +248,9 @@ define(['require',
var attributeObj = this.collection.toJSON();
if (this.collection.length === 1 &&
this.collection.first().get("name") === "") {
attributeObj = [];
+ } else {
+ // Process attributes to include cardinality
+ attributeObj = this.processAttributes(attributeObj);
}
this.json = {
"enumDefs": [],
@@ -279,7 +303,9 @@ define(['require',
if (selectedBusinessMetadataClone.attributeDefs ===
undefined) {
selectedBusinessMetadataClone.attributeDefs = [];
}
- selectedBusinessMetadataClone.attributeDefs =
selectedBusinessMetadataClone.attributeDefs.concat(this.collection.toJSON());
+ // Process new attributes to include cardinality
+ var newAttributes =
this.processAttributes(this.collection.toJSON());
+ selectedBusinessMetadataClone.attributeDefs =
selectedBusinessMetadataClone.attributeDefs.concat(newAttributes);
this.json = {
"enumDefs": [],
"structDefs": [],
diff --git a/dashboardv3/public/css/scss/business-metadata.scss
b/dashboardv3/public/css/scss/business-metadata.scss
index efed616e1..2a593bee8 100644
--- a/dashboardv3/public/css/scss/business-metadata.scss
+++ b/dashboardv3/public/css/scss/business-metadata.scss
@@ -240,4 +240,12 @@
.business-metadata-detail-attr-key {
width: 30%;
}
+}
+
+// Disabled state for cardinality toggle buttons
+.cardinality-btn.disabled,
+.cardinality-btn[disabled] {
+ opacity: 0.6;
+ cursor: not-allowed;
+ pointer-events: none;
}
\ No newline at end of file
diff --git
a/dashboardv3/public/js/templates/business_metadata/BusinessMetadataAttributeItemView_tmpl.html
b/dashboardv3/public/js/templates/business_metadata/BusinessMetadataAttributeItemView_tmpl.html
index aedb6dabe..8ffb689b4 100644
---
a/dashboardv3/public/js/templates/business_metadata/BusinessMetadataAttributeItemView_tmpl.html
+++
b/dashboardv3/public/js/templates/business_metadata/BusinessMetadataAttributeItemView_tmpl.html
@@ -68,7 +68,17 @@
<div class="form-group" data-id="multiValueSelect">
<label class="control-label col-sm-3" for="multiValSelect">Enable
Multivalues</label>
<div class="col-sm-8">
- <input type="checkbox" class="form-check-input multi-value-select"
data-id="multiValueSelectStatus">
+ <div class="row">
+ <div class="col-sm-6">
+ <input type="checkbox" class="form-check-input
multi-value-select" data-id="multiValueSelectStatus">
+ </div>
+ <div class="col-sm-6" data-id="cardinalityToggleContainer"
style="display: none;">
+ <div class="btn-group" role="group"
data-id="cardinalityToggle">
+ <button type="button" class="btn btn-sm btn-default
cardinality-btn" data-value="SET" data-id="cardinalitySET">SET</button>
+ <button type="button" class="btn btn-sm btn-default
cardinality-btn active" data-value="LIST"
data-id="cardinalityLIST">LIST</button>
+ </div>
+ </div>
+ </div>
</div>
</div>
<div class="form-group enumtype-container"
data-id="enumTypeSelectorContainer">
diff --git
a/dashboardv3/public/js/views/business_metadata/BusinessMetadataAttrTableLayoutView.js
b/dashboardv3/public/js/views/business_metadata/BusinessMetadataAttrTableLayoutView.js
index dd1e470e2..4646abbb1 100644
---
a/dashboardv3/public/js/views/business_metadata/BusinessMetadataAttrTableLayoutView.js
+++
b/dashboardv3/public/js/views/business_metadata/BusinessMetadataAttrTableLayoutView.js
@@ -108,6 +108,15 @@ define(['require',
attrDetails.typeName =
attrObj.typeName.replace("array<", "").replace(">", "");
attrDetails.multiValued = true;
}
+ // Set cardinalityToggle based on existing
cardinality (SET or LIST), default to SET
+ if (attrObj.cardinality) {
+ attrDetails.cardinalityToggle =
(attrObj.cardinality === "LIST") ? "LIST" : "SET";
+ } else {
+ // If cardinality is not set but it's an array
type, default to SET
+ if (attrObj.typeName.includes('array')) {
+ attrDetails.cardinalityToggle = "SET";
+ }
+ }
}
});
this.showDetails = false;
@@ -197,6 +206,27 @@ define(['require',
}
})
},
+ cardinality: {
+ label: "Cardinality",
+ cell: "html",
+ editable: false,
+ sortable: true,
+ formatter: _.extend({},
Backgrid.CellFormatter.prototype, {
+ fromRaw: function(rawValue, model) {
+ var cardinality = model.get('cardinality');
+ if (!cardinality) {
+ // If cardinality is not set, determine
from typeName
+ if
(model.get('typeName').indexOf('array<') > -1) {
+ // Default to SET for array types if
cardinality is not specified
+ cardinality = "SET";
+ } else {
+ cardinality = "SINGLE";
+ }
+ }
+ return _.escape(cardinality);
+ }
+ })
+ },
maxStrLength: {
label: "Max Length",
cell: "html",
diff --git
a/dashboardv3/public/js/views/business_metadata/BusinessMetadataAttributeItemView.js
b/dashboardv3/public/js/views/business_metadata/BusinessMetadataAttributeItemView.js
index 5190db031..6dddb8f65 100644
---
a/dashboardv3/public/js/views/business_metadata/BusinessMetadataAttributeItemView.js
+++
b/dashboardv3/public/js/views/business_metadata/BusinessMetadataAttributeItemView.js
@@ -48,6 +48,10 @@ define(['require',
enumValueSelector: "[data-id='enumValueSelector']",
multiValueSelect: "[data-id='multiValueSelect']",
multiValueSelectStatus: "[data-id='multiValueSelectStatus']",
+ cardinalityToggleContainer:
"[data-id='cardinalityToggleContainer']",
+ cardinalityToggle: "[data-id='cardinalityToggle']",
+ cardinalitySET: "[data-id='cardinalitySET']",
+ cardinalityLIST: "[data-id='cardinalityLIST']",
stringLengthContainer: "[data-id='stringLengthContainer']",
stringLengthValue: "[data-id='stringLength']",
createNewEnum: "[data-id='createNewEnum']"
@@ -122,15 +126,72 @@ define(['require',
this.model.set({ "enumValues":
this.ui.enumValueSelector.val() });
};
events["change " + this.ui.multiValueSelectStatus] =
function(e) {
+ var that = this;
this.model.set({ "multiValueSelect": e.target.checked });
var typename = this.model.get('typeName');
if (e.target.checked) {
typename = "array<" + typename + ">";
+ // Show cardinality toggle when multivalues is enabled
+ that.ui.cardinalityToggleContainer.show();
+ // Disable cardinality toggle buttons in edit mode
+ if (that.isAttrEdit) {
+ that.ui.cardinalitySET.attr('disabled',
'disabled');
+ that.ui.cardinalityLIST.attr('disabled',
'disabled');
+ that.ui.cardinalitySET.addClass('disabled');
+ that.ui.cardinalityLIST.addClass('disabled');
+ } else {
+ that.ui.cardinalitySET.removeAttr('disabled');
+ that.ui.cardinalityLIST.removeAttr('disabled');
+ that.ui.cardinalitySET.removeClass('disabled');
+ that.ui.cardinalityLIST.removeClass('disabled');
+ }
+ // Set default cardinality to SET if not already set
+ if (!that.model.get('cardinalityToggle')) {
+ that.model.set({ "cardinalityToggle": "SET" });
+ that.ui.cardinalitySET.addClass('active');
+ that.ui.cardinalityLIST.removeClass('active');
+ } else {
+ // Set active button based on current value
+ var currentCardinality =
that.model.get('cardinalityToggle');
+ if (currentCardinality === "LIST") {
+ that.ui.cardinalityLIST.addClass('active');
+ that.ui.cardinalitySET.removeClass('active');
+ } else {
+ that.ui.cardinalitySET.addClass('active');
+ that.ui.cardinalityLIST.removeClass('active');
+ }
+ }
} else {
typename = typename.replace('array<', '').replace('>',
'');
+ // Hide cardinality toggle when multivalues is disabled
+ that.ui.cardinalityToggleContainer.hide();
+ // Reset cardinality toggle
+ that.model.set({ "cardinalityToggle": "SET" });
}
this.model.set({ "typeName": typename });
};
+ events["click " + this.ui.cardinalitySET] = function(e) {
+ e.preventDefault();
+ var that = this;
+ // Disable cardinality toggle in edit mode
+ if (that.isAttrEdit) {
+ return;
+ }
+ that.model.set({ "cardinalityToggle": "SET" });
+ that.ui.cardinalitySET.addClass('active');
+ that.ui.cardinalityLIST.removeClass('active');
+ };
+ events["click " + this.ui.cardinalityLIST] = function(e) {
+ e.preventDefault();
+ var that = this;
+ // Disable cardinality toggle in edit mode
+ if (that.isAttrEdit) {
+ return;
+ }
+ that.model.set({ "cardinalityToggle": "LIST" });
+ that.ui.cardinalityLIST.addClass('active');
+ that.ui.cardinalitySET.removeClass('active');
+ };
events["change " + this.ui.entityTypeSelector] = function(e) {
var options = this.model.get('options') || {};
options.applicableEntityTypes =
JSON.stringify(this.ui.entityTypeSelector.val());
@@ -223,6 +284,24 @@ define(['require',
this.ui.multiValueSelect.show();
$(this.ui.multiValueSelectStatus).prop('checked',
true).trigger('change');
this.ui.multiValueSelectStatus.attr("disabled",
"false");
+ // Set cardinality toggle based on existing
cardinality (SET or LIST), default to SET
+ var existingCardinality =
this.model.get("cardinality");
+ var cardinalityToggle = (existingCardinality ===
"LIST") ? "LIST" : "SET";
+ this.model.set({ "cardinalityToggle":
cardinalityToggle });
+ if (cardinalityToggle === "LIST") {
+ this.ui.cardinalityLIST.addClass('active');
+ this.ui.cardinalitySET.removeClass('active');
+ } else {
+ this.ui.cardinalitySET.addClass('active');
+ this.ui.cardinalityLIST.removeClass('active');
+ }
+ // Disable cardinality toggle buttons in edit mode
+ if (this.isAttrEdit) {
+ this.ui.cardinalitySET.attr('disabled',
'disabled');
+ this.ui.cardinalityLIST.attr('disabled',
'disabled');
+ this.ui.cardinalitySET.addClass('disabled');
+ this.ui.cardinalityLIST.addClass('disabled');
+ }
}
}
},
diff --git
a/dashboardv3/public/js/views/business_metadata/BusinessMetadataTableLayoutView.js
b/dashboardv3/public/js/views/business_metadata/BusinessMetadataTableLayoutView.js
index 274630656..099a220af 100644
---
a/dashboardv3/public/js/views/business_metadata/BusinessMetadataTableLayoutView.js
+++
b/dashboardv3/public/js/views/business_metadata/BusinessMetadataTableLayoutView.js
@@ -244,11 +244,11 @@ define(['require',
accordion: false,
alwaysVisible: true,
expand: function(el, model) {
- el.attr('colspan', '8');
+ el.attr('colspan', '9');
var attrValues = '',
attrTable = $('table'),
attrTableBody = $('tbody'),
- attrTableHeading = "<thead><td
style='display:table-cell'><b>Attribute</b></td><td
style='display:table-cell'><b>Type</b></td><td
style='display:table-cell'><b>Search Weight</b></td><td
style='display:table-cell'><b>Enable Multivalues</b></td><td
style='display:table-cell'><b>Max Length</b></td><td
style='display:table-cell'><b>Applicable Type(s)</b></td><td
style='display:table-cell'><b>Action</b></td></thead>",
+ attrTableHeading = "<thead><td
style='display:table-cell'><b>Attribute</b></td><td
style='display:table-cell'><b>Type</b></td><td
style='display:table-cell'><b>Search Weight</b></td><td
style='display:table-cell'><b>Enable Multivalues</b></td><td
style='display:table-cell'><b>Cardinality</b></td><td
style='display:table-cell'><b>Max Length</b></td><td
style='display:table-cell'><b>Applicable Type(s)</b></td><td
style='display:table-cell'><b>Action</b></td> [...]
attrRow = '',
attrTableDetails = '';
if (model.attributes &&
model.attributes.attributeDefs.length) {
@@ -256,7 +256,8 @@ define(['require',
var applicableEntityTypes = '',
typeName = attrObj.typeName,
multiSelect = '',
- maxString = 'NA';
+ maxString = 'NA',
+ cardinality = 'SINGLE';
if (attrObj.options &&
attrObj.options.applicableEntityTypes) {
var entityTypes =
JSON.parse(attrObj.options.applicableEntityTypes);
_.each(entityTypes, function(values) {
@@ -266,12 +267,21 @@ define(['require',
if (typeName.includes('array')) {
typeName = _.escape(typeName);
multiSelect = 'checked';
+ // Determine cardinality - use
existing cardinality value or default to SET
+ if (attrObj.cardinality) {
+ cardinality = attrObj.cardinality;
+ } else {
+ cardinality = 'SET';
+ }
+ } else {
+ // For non-array types, cardinality is
SINGLE
+ cardinality = 'SINGLE';
}
if (typeName.includes('string') &&
attrObj.options && attrObj.options.maxStrLength) {
maxString =
attrObj.options.maxStrLength;
}
- attrRow += "<tr> <td
style='display:table-cell'>" + _.escape(attrObj.name) + "</td><td
style='display:table-cell'>" + typeName + "</td><td
style='display:table-cell'>" + _.escape(attrObj.searchWeight) + "</td><td
style='display:table-cell'><input type='checkbox' class='form-check-input
multi-value-select' " + multiSelect + " disabled='disabled'> </td><td
style='display:table-cell'>" + maxString + "</td><td
style='display:table-cell'>" + applicableEntit [...]
+ attrRow += "<tr> <td
style='display:table-cell'>" + _.escape(attrObj.name) + "</td><td
style='display:table-cell'>" + typeName + "</td><td
style='display:table-cell'>" + _.escape(attrObj.searchWeight) + "</td><td
style='display:table-cell'><input type='checkbox' class='form-check-input
multi-value-select' " + multiSelect + " disabled='disabled'> </td><td
style='display:table-cell'>" + _.escape(cardinality) + "</td><td
style='display:table-cell'>" + max [...]
});
var adminText = '<div class="row"><div
class="col-sm-12 attr-details"><table style="padding: 50px;">' +
attrTableHeading + attrRow + '</table></div></div>';
$(el).append($('<div>').html(adminText));
diff --git
a/dashboardv3/public/js/views/business_metadata/CreateBusinessMetadataLayoutView.js
b/dashboardv3/public/js/views/business_metadata/CreateBusinessMetadataLayoutView.js
index 165b99500..200e4ad16 100644
---
a/dashboardv3/public/js/views/business_metadata/CreateBusinessMetadataLayoutView.js
+++
b/dashboardv3/public/js/views/business_metadata/CreateBusinessMetadataLayoutView.js
@@ -216,6 +216,27 @@ define(['require',
}
},
+ processAttributes: function(attributes) {
+ var that = this;
+ return attributes.map(function(item) {
+ var multiValueSelect = item.multiValueSelect || false;
+ var cardinalityToggle = item.cardinalityToggle || "SET";
+
+ // Determine cardinality based on multiValueSelect and
cardinality toggle
+ var finalCardinality = "SINGLE";
+ if (multiValueSelect) {
+ // If multivalues is enabled, use the
cardinalityToggle (SET or LIST)
+ // Default to SET if not specified
+ finalCardinality = cardinalityToggle === "LIST" ?
"LIST" : (item.cardinality === "LIST" ? "LIST" : "SET");
+ }
+
+ // Remove cardinalityToggle from the final object as it's
not part of API
+ var processedItem = _.omit(item, 'cardinalityToggle');
+ processedItem.cardinality = finalCardinality;
+
+ return processedItem;
+ });
+ },
onCreateBusinessMetadata: function() {
var that = this;
if (this.validateValues()) {
@@ -227,6 +248,9 @@ define(['require',
var attributeObj = this.collection.toJSON();
if (this.collection.length === 1 &&
this.collection.first().get("name") === "") {
attributeObj = [];
+ } else {
+ // Process attributes to include cardinality
+ attributeObj = this.processAttributes(attributeObj);
}
this.json = {
"enumDefs": [],
@@ -279,7 +303,9 @@ define(['require',
if (selectedBusinessMetadataClone.attributeDefs ===
undefined) {
selectedBusinessMetadataClone.attributeDefs = [];
}
- selectedBusinessMetadataClone.attributeDefs =
selectedBusinessMetadataClone.attributeDefs.concat(this.collection.toJSON());
+ // Process new attributes to include cardinality
+ var newAttributes =
this.processAttributes(this.collection.toJSON());
+ selectedBusinessMetadataClone.attributeDefs =
selectedBusinessMetadataClone.attributeDefs.concat(newAttributes);
this.json = {
"enumDefs": [],
"structDefs": [],