mirror of
				https://github.com/open-metadata/OpenMetadata.git
				synced 2025-11-04 04:29:13 +00:00 
			
		
		
		
	* fix(#15683): backend validation error for the following property types * feat: Update supported date and datetime formats * test: add e2e playwright test for custom properties * chore: remove cypress test for custom properties * Add date, dateTime, time backend support for custom property * fix: update custom property display names in AddCustomProperty component * feat: Update supported date and time formats * chore: Update supported date and time formats * feat: Update date and time formats in CustomProperty * test: add playwright test * Update supported date and time formats * feat: Add tests for custom properties in Playwright in 2 parts * Update supported date and time formats * Update supported date and time formats * chore: Update date format to uppercase in PropertyValue component * feat: Add support for custom date format in PropertyValue component * Update supported date and time formats * test: add playwright test for time, dateTime, timeInterval and date properties * Update supported time formats * chore: Add focus to time and date pickers before filling values * chore: Add focus to time and date pickers before filling values * Remove date, dateTime, time from type_entity --------- Co-authored-by: sonikashah <sonikashah94@gmail.com>
This commit is contained in:
		
							parent
							
								
									d27f518f26
								
							
						
					
					
						commit
						6e99fe7bda
					
				@ -75,3 +75,7 @@ CREATE TABLE IF NOT EXISTS api_endpoint_entity (
 | 
			
		||||
    UNIQUE (fqnHash),
 | 
			
		||||
    INDEX (name)
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
-- Remove date, dateTime, time from type_entity, as they are no more om-field-types, instead we have date-cp, time-cp, dateTime-cp as om-field-types
 | 
			
		||||
DELETE FROM type_entity
 | 
			
		||||
WHERE name IN ('date', 'dateTime', 'time');
 | 
			
		||||
 | 
			
		||||
@ -69,3 +69,7 @@ CREATE TABLE IF NOT EXISTS api_endpoint_entity (
 | 
			
		||||
    PRIMARY KEY (id),
 | 
			
		||||
    UNIQUE (fqnHash)
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
-- Remove date, dateTime, time from type_entity, as they are no more om-field-types, instead we have date-cp, time-cp, dateTime-cp as om-field-types
 | 
			
		||||
DELETE FROM type_entity
 | 
			
		||||
WHERE name IN ('date', 'dateTime', 'time');
 | 
			
		||||
 | 
			
		||||
@ -102,4 +102,30 @@ public class TypeRegistry {
 | 
			
		||||
  public static String getPropertyName(String propertyFQN) {
 | 
			
		||||
    return FullyQualifiedName.split(propertyFQN)[2];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public static String getCustomPropertyType(String entityType, String propertyName) {
 | 
			
		||||
    Type type = TypeRegistry.TYPES.get(entityType);
 | 
			
		||||
    if (type != null && type.getCustomProperties() != null) {
 | 
			
		||||
      for (CustomProperty property : type.getCustomProperties()) {
 | 
			
		||||
        if (property.getName().equals(propertyName)) {
 | 
			
		||||
          return property.getPropertyType().getName();
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public static String getCustomPropertyConfig(String entityType, String propertyName) {
 | 
			
		||||
    Type type = TypeRegistry.TYPES.get(entityType);
 | 
			
		||||
    if (type != null && type.getCustomProperties() != null) {
 | 
			
		||||
      for (CustomProperty property : type.getCustomProperties()) {
 | 
			
		||||
        if (property.getName().equals(propertyName)
 | 
			
		||||
            && property.getCustomPropertyConfig() != null
 | 
			
		||||
            && property.getCustomPropertyConfig().getConfig() != null) {
 | 
			
		||||
          return property.getCustomPropertyConfig().getConfig().toString();
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -237,6 +237,11 @@ public final class CatalogExceptionMessage {
 | 
			
		||||
    return String.format("Unknown custom field %s", fieldName);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public static String dateTimeValidationError(String fieldName, String format) {
 | 
			
		||||
    return String.format(
 | 
			
		||||
        "Custom field %s value is not as per defined format %s", fieldName, format);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public static String jsonValidationError(String fieldName, String validationMessages) {
 | 
			
		||||
    return String.format("Custom field %s has invalid JSON %s", fieldName, validationMessages);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -77,11 +77,17 @@ import com.networknt.schema.JsonSchema;
 | 
			
		||||
import com.networknt.schema.ValidationMessage;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.net.URI;
 | 
			
		||||
import java.time.LocalDateTime;
 | 
			
		||||
import java.time.LocalTime;
 | 
			
		||||
import java.time.format.DateTimeFormatter;
 | 
			
		||||
import java.time.format.DateTimeParseException;
 | 
			
		||||
import java.time.temporal.TemporalAccessor;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
import java.util.Iterator;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Locale;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Map.Entry;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
@ -1340,6 +1346,35 @@ public abstract class EntityRepository<T extends EntityInterface> {
 | 
			
		||||
      if (jsonSchema == null) {
 | 
			
		||||
        throw new IllegalArgumentException(CatalogExceptionMessage.unknownCustomField(fieldName));
 | 
			
		||||
      }
 | 
			
		||||
      String customPropertyType = TypeRegistry.getCustomPropertyType(entityType, fieldName);
 | 
			
		||||
      String propertyConfig = TypeRegistry.getCustomPropertyConfig(entityType, fieldName);
 | 
			
		||||
      DateTimeFormatter formatter = null;
 | 
			
		||||
      try {
 | 
			
		||||
        if ("date-cp".equals(customPropertyType)) {
 | 
			
		||||
          DateTimeFormatter inputFormatter =
 | 
			
		||||
              DateTimeFormatter.ofPattern(Objects.requireNonNull(propertyConfig), Locale.ENGLISH);
 | 
			
		||||
 | 
			
		||||
          // Parse the input string into a TemporalAccessor
 | 
			
		||||
          TemporalAccessor date = inputFormatter.parse(fieldValue.textValue());
 | 
			
		||||
 | 
			
		||||
          // Create a formatter for the desired output format
 | 
			
		||||
          DateTimeFormatter outputFormatter =
 | 
			
		||||
              DateTimeFormatter.ofPattern(propertyConfig, Locale.ENGLISH);
 | 
			
		||||
          ((ObjectNode) jsonNode).put(fieldName, outputFormatter.format(date));
 | 
			
		||||
        } else if ("dateTime-cp".equals(customPropertyType)) {
 | 
			
		||||
          formatter = DateTimeFormatter.ofPattern(Objects.requireNonNull(propertyConfig));
 | 
			
		||||
          LocalDateTime dateTime = LocalDateTime.parse(fieldValue.textValue(), formatter);
 | 
			
		||||
          ((ObjectNode) jsonNode).put(fieldName, dateTime.format(formatter));
 | 
			
		||||
        } else if ("time-cp".equals(customPropertyType)) {
 | 
			
		||||
          formatter = DateTimeFormatter.ofPattern(Objects.requireNonNull(propertyConfig));
 | 
			
		||||
          LocalTime time = LocalTime.parse(fieldValue.textValue(), formatter);
 | 
			
		||||
          ((ObjectNode) jsonNode).put(fieldName, time.format(formatter));
 | 
			
		||||
        }
 | 
			
		||||
      } catch (DateTimeParseException e) {
 | 
			
		||||
        throw new IllegalArgumentException(
 | 
			
		||||
            CatalogExceptionMessage.dateTimeValidationError(
 | 
			
		||||
                fieldName, TypeRegistry.getCustomPropertyConfig(entityType, fieldName)));
 | 
			
		||||
      }
 | 
			
		||||
      Set<ValidationMessage> validationMessages = jsonSchema.validate(fieldValue);
 | 
			
		||||
      if (!validationMessages.isEmpty()) {
 | 
			
		||||
        throw new IllegalArgumentException(
 | 
			
		||||
 | 
			
		||||
@ -22,9 +22,11 @@ import static org.openmetadata.service.Entity.FIELD_DESCRIPTION;
 | 
			
		||||
import static org.openmetadata.service.util.EntityUtil.customFieldMatch;
 | 
			
		||||
import static org.openmetadata.service.util.EntityUtil.getCustomField;
 | 
			
		||||
 | 
			
		||||
import java.time.format.DateTimeFormatter;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
import javax.ws.rs.core.UriInfo;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
@ -185,10 +187,48 @@ public class TypeRepository extends EntityRepository<Type> {
 | 
			
		||||
          throw new IllegalArgumentException("Enum Custom Property Type must have EnumConfig.");
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      case "date-cp" -> validateDateFormat(
 | 
			
		||||
          customProperty.getCustomPropertyConfig(), getDateTokens(), "Invalid date format");
 | 
			
		||||
      case "dateTime-cp" -> validateDateFormat(
 | 
			
		||||
          customProperty.getCustomPropertyConfig(), getDateTimeTokens(), "Invalid dateTime format");
 | 
			
		||||
      case "time-cp" -> validateDateFormat(
 | 
			
		||||
          customProperty.getCustomPropertyConfig(), getTimeTokens(), "Invalid time format");
 | 
			
		||||
      case "int", "string" -> {}
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private void validateDateFormat(
 | 
			
		||||
      CustomPropertyConfig config, Set<Character> validTokens, String errorMessage) {
 | 
			
		||||
    if (config != null) {
 | 
			
		||||
      String format = String.valueOf(config.getConfig());
 | 
			
		||||
      for (char c : format.toCharArray()) {
 | 
			
		||||
        if (Character.isLetter(c) && !validTokens.contains(c)) {
 | 
			
		||||
          throw new IllegalArgumentException(errorMessage + ": " + format);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      try {
 | 
			
		||||
        DateTimeFormatter.ofPattern(format);
 | 
			
		||||
      } catch (IllegalArgumentException e) {
 | 
			
		||||
        throw new IllegalArgumentException(errorMessage + ": " + format, e);
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      throw new IllegalArgumentException(errorMessage + " must have Config populated with format.");
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private Set<Character> getDateTokens() {
 | 
			
		||||
    return Set.of('y', 'M', 'd', 'E', 'D', 'W', 'w');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private Set<Character> getDateTimeTokens() {
 | 
			
		||||
    return Set.of(
 | 
			
		||||
        'y', 'M', 'd', 'E', 'D', 'W', 'w', 'H', 'h', 'm', 's', 'a', 'T', 'X', 'Z', '+', '-', 'S');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private Set<Character> getTimeTokens() {
 | 
			
		||||
    return Set.of('H', 'h', 'm', 's', 'a', 'S');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** Handles entity updated from PUT and POST operation. */
 | 
			
		||||
  public class TypeUpdater extends EntityUpdater {
 | 
			
		||||
    public TypeUpdater(Type original, Type updated, Operation operation) {
 | 
			
		||||
 | 
			
		||||
@ -68,23 +68,35 @@
 | 
			
		||||
      "type": "string"
 | 
			
		||||
    },
 | 
			
		||||
    "date": {
 | 
			
		||||
      "$comment" : "@om-field-type",
 | 
			
		||||
      "description": "Date in ISO 8601 format in UTC. Example - '2018-11-13'.",
 | 
			
		||||
      "type": "string",
 | 
			
		||||
      "format": "date"
 | 
			
		||||
    },
 | 
			
		||||
    "dateTime": {
 | 
			
		||||
      "$comment" : "@om-field-type",
 | 
			
		||||
      "description": "Date and time in ISO 8601 format. Example - '2018-11-13T20:20:39+00:00'.",
 | 
			
		||||
      "type": "string",
 | 
			
		||||
      "format": "date-time"
 | 
			
		||||
    },
 | 
			
		||||
    "time": {
 | 
			
		||||
      "$comment" : "@om-field-type",
 | 
			
		||||
      "description": "time in ISO 8601 format. Example - '20:20:39+00:00'.",
 | 
			
		||||
      "type": "string",
 | 
			
		||||
      "format": "time"
 | 
			
		||||
    },
 | 
			
		||||
    "date-cp": {
 | 
			
		||||
      "$comment" : "@om-field-type",
 | 
			
		||||
      "description": "Date as defined in custom property.",
 | 
			
		||||
      "type": "string"
 | 
			
		||||
    },
 | 
			
		||||
    "dateTime-cp": {
 | 
			
		||||
      "$comment" : "@om-field-type",
 | 
			
		||||
      "description": "Date and time as defined in custom property.",
 | 
			
		||||
      "type": "string"
 | 
			
		||||
    },
 | 
			
		||||
    "time-cp": {
 | 
			
		||||
      "$comment" : "@om-field-type",
 | 
			
		||||
      "description": "Time as defined in custom property.",
 | 
			
		||||
      "type": "string"
 | 
			
		||||
    },
 | 
			
		||||
    "enum": {
 | 
			
		||||
      "$comment" : "@om-field-type",
 | 
			
		||||
      "description": "List of values in Enum.",
 | 
			
		||||
 | 
			
		||||
@ -1,654 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  Copyright 2023 Collate.
 | 
			
		||||
 *  Licensed 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 {
 | 
			
		||||
  CUSTOM_PROPERTY_INVALID_NAMES,
 | 
			
		||||
  CUSTOM_PROPERTY_NAME_VALIDATION_ERROR,
 | 
			
		||||
} from '../../constants/constants';
 | 
			
		||||
import { ENTITY_REFERENCE_PROPERTIES } from '../../constants/CustomProperty.constant';
 | 
			
		||||
import { EntityType, ENTITY_PATH } from '../../constants/Entity.interface';
 | 
			
		||||
import {
 | 
			
		||||
  descriptionBox,
 | 
			
		||||
  interceptURL,
 | 
			
		||||
  uuid,
 | 
			
		||||
  verifyResponseStatusCode,
 | 
			
		||||
} from '../common';
 | 
			
		||||
import { getToken } from './LocalStorage';
 | 
			
		||||
 | 
			
		||||
export enum CustomPropertyType {
 | 
			
		||||
  STRING = 'String',
 | 
			
		||||
  INTEGER = 'Integer',
 | 
			
		||||
  MARKDOWN = 'Markdown',
 | 
			
		||||
}
 | 
			
		||||
export enum CustomPropertyTypeByName {
 | 
			
		||||
  STRING = 'string',
 | 
			
		||||
  INTEGER = 'integer',
 | 
			
		||||
  MARKDOWN = 'markdown',
 | 
			
		||||
  NUMBER = 'number',
 | 
			
		||||
  DURATION = 'duration',
 | 
			
		||||
  EMAIL = 'email',
 | 
			
		||||
  ENUM = 'enum',
 | 
			
		||||
  SQL_QUERY = 'sqlQuery',
 | 
			
		||||
  TIMESTAMP = 'timestamp',
 | 
			
		||||
  ENTITY_REFERENCE = 'entityReference',
 | 
			
		||||
  ENTITY_REFERENCE_LIST = 'entityReferenceList',
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface CustomProperty {
 | 
			
		||||
  name: string;
 | 
			
		||||
  type: CustomPropertyType;
 | 
			
		||||
  description: string;
 | 
			
		||||
  propertyType: {
 | 
			
		||||
    name: string;
 | 
			
		||||
    type: string;
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const generateCustomProperty = (type: CustomPropertyType) => ({
 | 
			
		||||
  name: `cypress${type.toLowerCase()}${Date.now()}`,
 | 
			
		||||
  type,
 | 
			
		||||
  description: `${type} cypress Property`,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const getPropertyValues = (type: string) => {
 | 
			
		||||
  switch (type) {
 | 
			
		||||
    case 'integer':
 | 
			
		||||
      return {
 | 
			
		||||
        value: '123',
 | 
			
		||||
        newValue: '456',
 | 
			
		||||
      };
 | 
			
		||||
    case 'string':
 | 
			
		||||
      return {
 | 
			
		||||
        value: 'string value',
 | 
			
		||||
        newValue: 'new string value',
 | 
			
		||||
      };
 | 
			
		||||
    case 'markdown':
 | 
			
		||||
      return {
 | 
			
		||||
        value: '**Bold statement**',
 | 
			
		||||
        newValue: '__Italic statement__',
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
    case 'number':
 | 
			
		||||
      return {
 | 
			
		||||
        value: '123',
 | 
			
		||||
        newValue: '456',
 | 
			
		||||
      };
 | 
			
		||||
    case 'duration':
 | 
			
		||||
      return {
 | 
			
		||||
        value: 'PT1H',
 | 
			
		||||
        newValue: 'PT2H',
 | 
			
		||||
      };
 | 
			
		||||
    case 'email':
 | 
			
		||||
      return {
 | 
			
		||||
        value: 'john@gamil.com',
 | 
			
		||||
        newValue: 'user@getcollate.io',
 | 
			
		||||
      };
 | 
			
		||||
    case 'enum':
 | 
			
		||||
      return {
 | 
			
		||||
        value: 'small',
 | 
			
		||||
        newValue: 'medium',
 | 
			
		||||
      };
 | 
			
		||||
    case 'sqlQuery':
 | 
			
		||||
      return {
 | 
			
		||||
        value: 'Select * from table',
 | 
			
		||||
        newValue: 'Select * from table where id = 1',
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
    case 'timestamp':
 | 
			
		||||
      return {
 | 
			
		||||
        value: '1710831125922',
 | 
			
		||||
        newValue: '1710831125923',
 | 
			
		||||
      };
 | 
			
		||||
    case 'entityReference':
 | 
			
		||||
      return {
 | 
			
		||||
        value: 'Adam Matthews',
 | 
			
		||||
        newValue: 'Amber Green',
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
    case 'entityReferenceList':
 | 
			
		||||
      return {
 | 
			
		||||
        value: 'Aaron Johnson,Organization',
 | 
			
		||||
        newValue: 'Aaron Warren',
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
      return {
 | 
			
		||||
        value: '',
 | 
			
		||||
        newValue: '',
 | 
			
		||||
      };
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const deleteCustomPropertyForEntity = ({
 | 
			
		||||
  property,
 | 
			
		||||
  type,
 | 
			
		||||
}: {
 | 
			
		||||
  property: CustomProperty;
 | 
			
		||||
  type: EntityType;
 | 
			
		||||
}) => {
 | 
			
		||||
  interceptURL('GET', `/api/v1/metadata/types/name/*`, 'getEntity');
 | 
			
		||||
  interceptURL('PATCH', `/api/v1/metadata/types/*`, 'patchEntity');
 | 
			
		||||
  // Selecting the entity
 | 
			
		||||
  cy.settingClick(type, true);
 | 
			
		||||
 | 
			
		||||
  verifyResponseStatusCode('@getEntity', 200);
 | 
			
		||||
 | 
			
		||||
  cy.get(
 | 
			
		||||
    `[data-row-key="${property.name}"] [data-testid="delete-button"]`
 | 
			
		||||
  ).click();
 | 
			
		||||
 | 
			
		||||
  cy.get('[data-testid="modal-header"]').should('contain', property.name);
 | 
			
		||||
 | 
			
		||||
  cy.get('[data-testid="save-button"]').click();
 | 
			
		||||
 | 
			
		||||
  verifyResponseStatusCode('@patchEntity', 200);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const setValueForProperty = (
 | 
			
		||||
  propertyName: string,
 | 
			
		||||
  value: string,
 | 
			
		||||
  propertyType: string
 | 
			
		||||
) => {
 | 
			
		||||
  cy.get('[data-testid="custom_properties"]').click();
 | 
			
		||||
 | 
			
		||||
  cy.get('tbody').should('contain', propertyName);
 | 
			
		||||
 | 
			
		||||
  // Adding value for the custom property
 | 
			
		||||
 | 
			
		||||
  // Navigating through the created custom property for adding value
 | 
			
		||||
  cy.get(`[data-row-key="${propertyName}"]`)
 | 
			
		||||
    .find('[data-testid="edit-icon"]')
 | 
			
		||||
    .scrollIntoView()
 | 
			
		||||
    .as('editbutton');
 | 
			
		||||
 | 
			
		||||
  cy.get('@editbutton').should('be.visible').click({ force: true });
 | 
			
		||||
 | 
			
		||||
  interceptURL('PATCH', `/api/v1/*/*`, 'patchEntity');
 | 
			
		||||
  // Checking for value text box or markdown box
 | 
			
		||||
 | 
			
		||||
  switch (propertyType) {
 | 
			
		||||
    case 'markdown':
 | 
			
		||||
      cy.get('.toastui-editor-md-container > .toastui-editor > .ProseMirror')
 | 
			
		||||
        .should('be.visible')
 | 
			
		||||
        .clear()
 | 
			
		||||
        .type(value);
 | 
			
		||||
      cy.get('[data-testid="save"]').click();
 | 
			
		||||
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case 'email':
 | 
			
		||||
      cy.get('[data-testid="email-input"]')
 | 
			
		||||
        .should('be.visible')
 | 
			
		||||
        .clear()
 | 
			
		||||
        .type(value);
 | 
			
		||||
      cy.get('[data-testid="inline-save-btn"]').click();
 | 
			
		||||
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case 'duration':
 | 
			
		||||
      cy.get('[data-testid="duration-input"]')
 | 
			
		||||
        .should('be.visible')
 | 
			
		||||
        .clear()
 | 
			
		||||
        .type(value);
 | 
			
		||||
      cy.get('[data-testid="inline-save-btn"]').click();
 | 
			
		||||
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case 'enum':
 | 
			
		||||
      cy.get('#enumValues').click().type(`${value}{enter}`);
 | 
			
		||||
      cy.clickOutside();
 | 
			
		||||
      cy.get('[data-testid="inline-save-btn"]').click();
 | 
			
		||||
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case 'sqlQuery':
 | 
			
		||||
      cy.get("pre[role='presentation']").last().click().type(value);
 | 
			
		||||
      cy.get('[data-testid="inline-save-btn"]').click();
 | 
			
		||||
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case 'timestamp':
 | 
			
		||||
      cy.get('[data-testid="timestamp-input"]')
 | 
			
		||||
        .should('be.visible')
 | 
			
		||||
        .clear()
 | 
			
		||||
        .type(value);
 | 
			
		||||
      cy.get('[data-testid="inline-save-btn"]').click();
 | 
			
		||||
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case 'timeInterval': {
 | 
			
		||||
      const [startValue, endValue] = value.split(',');
 | 
			
		||||
      cy.get('[data-testid="start-input"]')
 | 
			
		||||
        .should('be.visible')
 | 
			
		||||
        .clear()
 | 
			
		||||
        .type(startValue);
 | 
			
		||||
      cy.get('[data-testid="end-input"]')
 | 
			
		||||
        .should('be.visible')
 | 
			
		||||
        .clear()
 | 
			
		||||
        .type(endValue);
 | 
			
		||||
      cy.get('[data-testid="inline-save-btn"]').click();
 | 
			
		||||
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    case 'string':
 | 
			
		||||
    case 'integer':
 | 
			
		||||
    case 'number':
 | 
			
		||||
      cy.get('[data-testid="value-input"]')
 | 
			
		||||
        .should('be.visible')
 | 
			
		||||
        .clear()
 | 
			
		||||
        .type(value);
 | 
			
		||||
      cy.get('[data-testid="inline-save-btn"]').click();
 | 
			
		||||
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case 'entityReference':
 | 
			
		||||
    case 'entityReferenceList': {
 | 
			
		||||
      const refValues = value.split(',');
 | 
			
		||||
 | 
			
		||||
      refValues.forEach((val) => {
 | 
			
		||||
        interceptURL(
 | 
			
		||||
          'GET',
 | 
			
		||||
          `/api/v1/search/query?q=*${encodeURIComponent(val)}*`,
 | 
			
		||||
          'searchEntityReference'
 | 
			
		||||
        );
 | 
			
		||||
        cy.get('#entityReference').clear().type(`${val}`);
 | 
			
		||||
        cy.wait('@searchEntityReference');
 | 
			
		||||
        cy.get(`[data-testid="${val}"]`).click();
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      cy.get('[data-testid="inline-save-btn"]').click();
 | 
			
		||||
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  verifyResponseStatusCode('@patchEntity', 200);
 | 
			
		||||
 | 
			
		||||
  if (propertyType === 'enum') {
 | 
			
		||||
    cy.get('[data-testid="enum-value"]').should('contain', value);
 | 
			
		||||
  } else if (propertyType === 'timeInterval') {
 | 
			
		||||
    const [startValue, endValue] = value.split(',');
 | 
			
		||||
    cy.get('[data-testid="time-interval-value"]').should('contain', startValue);
 | 
			
		||||
    cy.get('[data-testid="time-interval-value"]').should('contain', endValue);
 | 
			
		||||
  } else if (propertyType === 'sqlQuery') {
 | 
			
		||||
    cy.get('.CodeMirror-scroll').should('contain', value);
 | 
			
		||||
  } else if (
 | 
			
		||||
    ['entityReference', 'entityReferenceList'].includes(propertyType)
 | 
			
		||||
  ) {
 | 
			
		||||
    // do nothing
 | 
			
		||||
  } else {
 | 
			
		||||
    cy.get(`[data-row-key="${propertyName}"]`).should(
 | 
			
		||||
      'contain',
 | 
			
		||||
      value.replace(/\*|_/gi, '')
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
export const validateValueForProperty = (
 | 
			
		||||
  propertyName: string,
 | 
			
		||||
  value: string,
 | 
			
		||||
  propertyType: string
 | 
			
		||||
) => {
 | 
			
		||||
  cy.get('.ant-tabs-tab').first().click();
 | 
			
		||||
  cy.get(
 | 
			
		||||
    '[data-testid="entity-right-panel"] [data-testid="custom-properties-table"]',
 | 
			
		||||
    {
 | 
			
		||||
      timeout: 10000,
 | 
			
		||||
    }
 | 
			
		||||
  ).scrollIntoView();
 | 
			
		||||
 | 
			
		||||
  if (propertyType === 'enum') {
 | 
			
		||||
    cy.get('[data-testid="enum-value"]').should('contain', value);
 | 
			
		||||
  } else if (propertyType === 'timeInterval') {
 | 
			
		||||
    const [startValue, endValue] = value.split(',');
 | 
			
		||||
    cy.get('[data-testid="time-interval-value"]').should('contain', startValue);
 | 
			
		||||
    cy.get('[data-testid="time-interval-value"]').should('contain', endValue);
 | 
			
		||||
  } else if (propertyType === 'sqlQuery') {
 | 
			
		||||
    cy.get('.CodeMirror-scroll').should('contain', value);
 | 
			
		||||
  } else if (
 | 
			
		||||
    ['entityReference', 'entityReferenceList'].includes(propertyType)
 | 
			
		||||
  ) {
 | 
			
		||||
    // do nothing
 | 
			
		||||
  } else {
 | 
			
		||||
    cy.get(`[data-row-key="${propertyName}"]`).should(
 | 
			
		||||
      'contain',
 | 
			
		||||
      value.replace(/\*|_/gi, '')
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
export const generateCustomProperties = () => {
 | 
			
		||||
  return {
 | 
			
		||||
    name: `cyCustomProperty${uuid()}`,
 | 
			
		||||
    description: `cyCustomProperty${uuid()}`,
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
export const verifyCustomPropertyRows = () => {
 | 
			
		||||
  cy.get('[data-testid="custom_properties"]').click();
 | 
			
		||||
  cy.get('.ant-table-row').should('have.length.gte', 10);
 | 
			
		||||
  cy.get('.ant-tabs-tab').first().click();
 | 
			
		||||
  cy.get(
 | 
			
		||||
    '[data-testid="entity-right-panel"] [data-testid="custom-properties-table"]',
 | 
			
		||||
    {
 | 
			
		||||
      timeout: 10000,
 | 
			
		||||
    }
 | 
			
		||||
  ).scrollIntoView();
 | 
			
		||||
  cy.get(
 | 
			
		||||
    '[data-testid="entity-right-panel"] [data-testid="custom-properties-table"] tbody tr'
 | 
			
		||||
  ).should('have.length', 6);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const deleteCustomProperties = (
 | 
			
		||||
  tableSchemaId: string,
 | 
			
		||||
  token: string
 | 
			
		||||
) => {
 | 
			
		||||
  cy.request({
 | 
			
		||||
    method: 'PATCH',
 | 
			
		||||
    url: `/api/v1/metadata/types/${tableSchemaId}`,
 | 
			
		||||
    headers: {
 | 
			
		||||
      Authorization: `Bearer ${token}`,
 | 
			
		||||
      'Content-Type': 'application/json-patch+json',
 | 
			
		||||
    },
 | 
			
		||||
    body: [
 | 
			
		||||
      {
 | 
			
		||||
        op: 'remove',
 | 
			
		||||
        path: '/customProperties',
 | 
			
		||||
      },
 | 
			
		||||
    ],
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const customPropertiesArray = Array(10)
 | 
			
		||||
  .fill(null)
 | 
			
		||||
  .map(() => generateCustomProperties());
 | 
			
		||||
 | 
			
		||||
export const addCustomPropertiesForEntity = ({
 | 
			
		||||
  propertyName,
 | 
			
		||||
  customPropertyData,
 | 
			
		||||
  customType,
 | 
			
		||||
  enumConfig,
 | 
			
		||||
  formatConfig,
 | 
			
		||||
  entityReferenceConfig,
 | 
			
		||||
}: {
 | 
			
		||||
  propertyName: string;
 | 
			
		||||
  customPropertyData: { description: string };
 | 
			
		||||
  customType: string;
 | 
			
		||||
  enumConfig?: { values: string[]; multiSelect: boolean };
 | 
			
		||||
  formatConfig?: string;
 | 
			
		||||
  entityReferenceConfig?: string[];
 | 
			
		||||
}) => {
 | 
			
		||||
  // eslint-disable-next-line max-len
 | 
			
		||||
  const longDescription = `Lorem ipsum dolor sit amet consectetur adipisicing elit. Dolore neque fuga reprehenderit placeat, sint doloremque quo expedita consequatur fugiat maxime maiores voluptate eum quis quas dignissimos cumque perspiciatis optio dolorem blanditiis iure natus commodi dolor quam. Voluptatem excepturi aut, at ullam aliquid repudiandae distinctio ipsam voluptates tenetur a. Sit, illum.`;
 | 
			
		||||
  // Add Custom property for selected entity
 | 
			
		||||
  cy.get('[data-testid="add-field-button"]').click();
 | 
			
		||||
 | 
			
		||||
  // validation should work
 | 
			
		||||
  cy.get('[data-testid="create-button"]').scrollIntoView().click();
 | 
			
		||||
 | 
			
		||||
  cy.get('#name_help').should('contain', 'Name is required');
 | 
			
		||||
  cy.get('#propertyType_help').should('contain', 'Property Type is required');
 | 
			
		||||
 | 
			
		||||
  cy.get('#description_help').should('contain', 'Description is required');
 | 
			
		||||
 | 
			
		||||
  // capital case validation
 | 
			
		||||
  cy.get('[data-testid="name"]')
 | 
			
		||||
    .scrollIntoView()
 | 
			
		||||
    .type(CUSTOM_PROPERTY_INVALID_NAMES.CAPITAL_CASE);
 | 
			
		||||
  cy.get('[role="alert"]').should(
 | 
			
		||||
    'contain',
 | 
			
		||||
    CUSTOM_PROPERTY_NAME_VALIDATION_ERROR
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  // with underscore validation
 | 
			
		||||
  cy.get('[data-testid="name"]')
 | 
			
		||||
    .clear()
 | 
			
		||||
    .type(CUSTOM_PROPERTY_INVALID_NAMES.WITH_UNDERSCORE);
 | 
			
		||||
  cy.get('[role="alert"]').should(
 | 
			
		||||
    'contain',
 | 
			
		||||
    CUSTOM_PROPERTY_NAME_VALIDATION_ERROR
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  // with space validation
 | 
			
		||||
  cy.get('[data-testid="name"]')
 | 
			
		||||
    .clear()
 | 
			
		||||
    .type(CUSTOM_PROPERTY_INVALID_NAMES.WITH_SPACE);
 | 
			
		||||
  cy.get('[role="alert"]').should(
 | 
			
		||||
    'contain',
 | 
			
		||||
    CUSTOM_PROPERTY_NAME_VALIDATION_ERROR
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  // with dots validation
 | 
			
		||||
  cy.get('[data-testid="name"]')
 | 
			
		||||
    .clear()
 | 
			
		||||
    .type(CUSTOM_PROPERTY_INVALID_NAMES.WITH_DOTS);
 | 
			
		||||
  cy.get('[role="alert"]').should(
 | 
			
		||||
    'contain',
 | 
			
		||||
    CUSTOM_PROPERTY_NAME_VALIDATION_ERROR
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  // should allow name in another languages
 | 
			
		||||
  cy.get('[data-testid="name"]').clear().type('汝らヴェディア');
 | 
			
		||||
  // should not throw the validation error
 | 
			
		||||
  cy.get('#name_help').should('not.exist');
 | 
			
		||||
 | 
			
		||||
  cy.get('[data-testid="name"]').clear().type(propertyName);
 | 
			
		||||
 | 
			
		||||
  cy.get(`#root\\/propertyType`).clear().type(customType);
 | 
			
		||||
  cy.get(`[title="${customType}"]`).click();
 | 
			
		||||
 | 
			
		||||
  if (customType === 'Enum') {
 | 
			
		||||
    enumConfig.values.forEach((val) => {
 | 
			
		||||
      cy.get('#root\\/enumConfig').type(`${val}{enter}`);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    cy.clickOutside();
 | 
			
		||||
 | 
			
		||||
    if (enumConfig.multiSelect) {
 | 
			
		||||
      cy.get('#root\\/multiSelect').scrollIntoView().click();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (ENTITY_REFERENCE_PROPERTIES.includes(customType)) {
 | 
			
		||||
    entityReferenceConfig.forEach((val) => {
 | 
			
		||||
      cy.get('#root\\/entityReferenceConfig').click().type(`${val}`);
 | 
			
		||||
      cy.get(`[title="${val}"]`).click();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    cy.clickOutside();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (['Date', 'Date Time'].includes(customType)) {
 | 
			
		||||
    cy.get('#root\\/formatConfig').clear().type('invalid-format');
 | 
			
		||||
    cy.get('[role="alert"]').should('contain', 'Format is invalid');
 | 
			
		||||
 | 
			
		||||
    cy.get('#root\\/formatConfig').clear().type(formatConfig);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  cy.get(descriptionBox)
 | 
			
		||||
    .clear()
 | 
			
		||||
    .type(`${customPropertyData.description} ${longDescription}`);
 | 
			
		||||
 | 
			
		||||
  // Check if the property got added
 | 
			
		||||
  cy.intercept('/api/v1/metadata/types/name/*?fields=customProperties').as(
 | 
			
		||||
    'customProperties'
 | 
			
		||||
  );
 | 
			
		||||
  cy.get('[data-testid="create-button"]').scrollIntoView().click();
 | 
			
		||||
 | 
			
		||||
  cy.wait('@customProperties');
 | 
			
		||||
  cy.get('.ant-table-row').should('contain', propertyName);
 | 
			
		||||
 | 
			
		||||
  // Navigating to home page
 | 
			
		||||
  cy.clickOnLogo();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const editCreatedProperty = (propertyName: string, type?: string) => {
 | 
			
		||||
  // Fetching for edit button
 | 
			
		||||
  cy.get(`[data-row-key="${propertyName}"]`)
 | 
			
		||||
    .find('[data-testid="edit-button"]')
 | 
			
		||||
    .as('editButton');
 | 
			
		||||
 | 
			
		||||
  if (type === 'Enum') {
 | 
			
		||||
    cy.get(`[data-row-key="${propertyName}"]`)
 | 
			
		||||
      .find('[data-testid="enum-config"]')
 | 
			
		||||
      .should('contain', '["enum1","enum2","enum3"]');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  cy.get('@editButton').click();
 | 
			
		||||
 | 
			
		||||
  cy.get(descriptionBox).clear().type('This is new description');
 | 
			
		||||
 | 
			
		||||
  if (type === 'Enum') {
 | 
			
		||||
    cy.get('#root\\/customPropertyConfig').type(`updatedValue{enter}`);
 | 
			
		||||
 | 
			
		||||
    cy.clickOutside();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (ENTITY_REFERENCE_PROPERTIES.includes(type)) {
 | 
			
		||||
    cy.get('#root\\/customPropertyConfig').click().type(`Table{enter}`);
 | 
			
		||||
 | 
			
		||||
    cy.clickOutside();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  interceptURL('PATCH', '/api/v1/metadata/types/*', 'checkPatchForDescription');
 | 
			
		||||
 | 
			
		||||
  cy.get('button[type="submit"]').scrollIntoView().click();
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @link https://docs.cypress.io/guides/references/configuration#Timeouts
 | 
			
		||||
   * default responseTimeout is 30000ms which is not enough for the patch request
 | 
			
		||||
   * so we need to increase the responseTimeout to 70000ms for AUT environment in PATCH request
 | 
			
		||||
   */
 | 
			
		||||
  cy.wait('@checkPatchForDescription', { responseTimeout: 70000 });
 | 
			
		||||
 | 
			
		||||
  cy.get('.ant-modal-wrap').should('not.exist');
 | 
			
		||||
 | 
			
		||||
  // Fetching for updated descriptions for the created custom property
 | 
			
		||||
  cy.get(`[data-row-key="${propertyName}"]`)
 | 
			
		||||
    .find('[data-testid="viewer-container"]')
 | 
			
		||||
    .should('contain', 'This is new description');
 | 
			
		||||
 | 
			
		||||
  if (type === 'Enum') {
 | 
			
		||||
    cy.get(`[data-row-key="${propertyName}"]`)
 | 
			
		||||
      .find('[data-testid="enum-config"]')
 | 
			
		||||
      .should('contain', '["enum1","enum2","enum3","updatedValue"]');
 | 
			
		||||
  }
 | 
			
		||||
  if (ENTITY_REFERENCE_PROPERTIES.includes(type)) {
 | 
			
		||||
    cy.get(`[data-row-key="${propertyName}"]`)
 | 
			
		||||
      .find(`[data-testid="${propertyName}-config"]`)
 | 
			
		||||
      .should('contain', '["user","team","table"]');
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const deleteCreatedProperty = (propertyName: string) => {
 | 
			
		||||
  // Fetching for delete button
 | 
			
		||||
  cy.get(`[data-row-key="${propertyName}"]`)
 | 
			
		||||
    .scrollIntoView()
 | 
			
		||||
    .find('[data-testid="delete-button"]')
 | 
			
		||||
    .click();
 | 
			
		||||
 | 
			
		||||
  // Checking property name is present on the delete pop-up
 | 
			
		||||
  cy.get('[data-testid="body-text"]').should('contain', propertyName);
 | 
			
		||||
 | 
			
		||||
  cy.get('[data-testid="save-button"]').should('be.visible').click();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const createCustomPropertyForEntity = (prop: string) => {
 | 
			
		||||
  return cy.getAllLocalStorage().then((data) => {
 | 
			
		||||
    const token = getToken(data);
 | 
			
		||||
 | 
			
		||||
    // fetch the available property types
 | 
			
		||||
    return cy
 | 
			
		||||
      .request({
 | 
			
		||||
        method: 'GET',
 | 
			
		||||
        url: `/api/v1/metadata/types?category=field&limit=20`,
 | 
			
		||||
        headers: { Authorization: `Bearer ${token}` },
 | 
			
		||||
      })
 | 
			
		||||
      .then(({ body }) => {
 | 
			
		||||
        const propertyList = body.data.filter((item) =>
 | 
			
		||||
          Object.values(CustomPropertyTypeByName).includes(item.name)
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // fetch the entity details for which the custom property needs to be added
 | 
			
		||||
        return cy
 | 
			
		||||
          .request({
 | 
			
		||||
            method: 'GET',
 | 
			
		||||
            url: `/api/v1/metadata/types/name/${ENTITY_PATH[prop]}`,
 | 
			
		||||
            headers: { Authorization: `Bearer ${token}` },
 | 
			
		||||
          })
 | 
			
		||||
          .then(({ body }) => {
 | 
			
		||||
            const entityId = body.id;
 | 
			
		||||
 | 
			
		||||
            // Add the custom property for the entity
 | 
			
		||||
            propertyList.forEach((item) => {
 | 
			
		||||
              return cy
 | 
			
		||||
                .request({
 | 
			
		||||
                  method: 'PUT',
 | 
			
		||||
                  url: `/api/v1/metadata/types/${entityId}`,
 | 
			
		||||
                  headers: { Authorization: `Bearer ${token}` },
 | 
			
		||||
                  body: {
 | 
			
		||||
                    name: `cyCustomProperty${uuid()}`,
 | 
			
		||||
                    description: `cyCustomProperty${uuid()}`,
 | 
			
		||||
                    propertyType: {
 | 
			
		||||
                      id: item.id ?? '',
 | 
			
		||||
                      type: 'type',
 | 
			
		||||
                    },
 | 
			
		||||
                    ...(item.name === 'enum'
 | 
			
		||||
                      ? {
 | 
			
		||||
                          customPropertyConfig: {
 | 
			
		||||
                            config: {
 | 
			
		||||
                              multiSelect: true,
 | 
			
		||||
                              values: ['small', 'medium', 'large'],
 | 
			
		||||
                            },
 | 
			
		||||
                          },
 | 
			
		||||
                        }
 | 
			
		||||
                      : {}),
 | 
			
		||||
                    ...(['entityReference', 'entityReferenceList'].includes(
 | 
			
		||||
                      item.name
 | 
			
		||||
                    )
 | 
			
		||||
                      ? {
 | 
			
		||||
                          customPropertyConfig: {
 | 
			
		||||
                            config: ['user', 'team'],
 | 
			
		||||
                          },
 | 
			
		||||
                        }
 | 
			
		||||
                      : {}),
 | 
			
		||||
                  },
 | 
			
		||||
                })
 | 
			
		||||
                .then(({ body }) => {
 | 
			
		||||
                  return body.customProperties.reduce(
 | 
			
		||||
                    (prev, curr) => {
 | 
			
		||||
                      const propertyTypeName = curr.propertyType.name;
 | 
			
		||||
 | 
			
		||||
                      return {
 | 
			
		||||
                        ...prev,
 | 
			
		||||
                        [propertyTypeName]: {
 | 
			
		||||
                          ...getPropertyValues(propertyTypeName),
 | 
			
		||||
                          property: curr,
 | 
			
		||||
                        },
 | 
			
		||||
                      };
 | 
			
		||||
                    },
 | 
			
		||||
                    {} as Record<
 | 
			
		||||
                      string,
 | 
			
		||||
                      {
 | 
			
		||||
                        value: string;
 | 
			
		||||
                        newValue: string;
 | 
			
		||||
                        property: CustomProperty;
 | 
			
		||||
                      }
 | 
			
		||||
                    >
 | 
			
		||||
                  );
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
          });
 | 
			
		||||
      });
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
@ -1,32 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  Copyright 2024 Collate.
 | 
			
		||||
 *  Licensed 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 { EntityType } from './Entity.interface';
 | 
			
		||||
 | 
			
		||||
export const CustomPropertySupportedEntityList = [
 | 
			
		||||
  EntityType.Database,
 | 
			
		||||
  EntityType.DatabaseSchema,
 | 
			
		||||
  EntityType.Table,
 | 
			
		||||
  EntityType.StoreProcedure,
 | 
			
		||||
  EntityType.Topic,
 | 
			
		||||
  EntityType.Dashboard,
 | 
			
		||||
  EntityType.Pipeline,
 | 
			
		||||
  EntityType.Container,
 | 
			
		||||
  EntityType.MlModel,
 | 
			
		||||
  EntityType.GlossaryTerm,
 | 
			
		||||
  EntityType.SearchIndex,
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
export const ENTITY_REFERENCE_PROPERTIES = [
 | 
			
		||||
  'Entity Reference',
 | 
			
		||||
  'Entity Reference List',
 | 
			
		||||
];
 | 
			
		||||
@ -337,195 +337,6 @@ export const ENTITY_SERVICE_TYPE = {
 | 
			
		||||
  Search: 'Search',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const ENTITIES = {
 | 
			
		||||
  entity_container: {
 | 
			
		||||
    name: 'container',
 | 
			
		||||
    description: 'This is Container custom property',
 | 
			
		||||
    integerValue: '14',
 | 
			
		||||
    stringValue: 'This is string propery',
 | 
			
		||||
    markdownValue: 'This is markdown value',
 | 
			
		||||
    enumConfig: {
 | 
			
		||||
      values: ['enum1', 'enum2', 'enum3'],
 | 
			
		||||
      multiSelect: false,
 | 
			
		||||
    },
 | 
			
		||||
    dateFormatConfig: 'yyyy-mm-dd',
 | 
			
		||||
    dateTimeFormatConfig: 'yyyy-mm-dd hh:mm:ss',
 | 
			
		||||
    entityReferenceConfig: ['User', 'Team'],
 | 
			
		||||
    entityObj: {},
 | 
			
		||||
    entityApiType: 'containers',
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  entity_dashboard: {
 | 
			
		||||
    name: 'dashboard',
 | 
			
		||||
    description: 'This is Dashboard custom property',
 | 
			
		||||
    integerValue: '14',
 | 
			
		||||
    stringValue: 'This is string propery',
 | 
			
		||||
    markdownValue: 'This is markdown value',
 | 
			
		||||
    enumConfig: {
 | 
			
		||||
      values: ['enum1', 'enum2', 'enum3'],
 | 
			
		||||
      multiSelect: false,
 | 
			
		||||
    },
 | 
			
		||||
    dateFormatConfig: 'yyyy-mm-dd',
 | 
			
		||||
    dateTimeFormatConfig: 'yyyy-mm-dd hh:mm:ss',
 | 
			
		||||
    entityReferenceConfig: ['User', 'Team'],
 | 
			
		||||
    entityObj: SEARCH_ENTITY_DASHBOARD.dashboard_1,
 | 
			
		||||
    entityApiType: 'dashboards',
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  entity_database: {
 | 
			
		||||
    name: 'database',
 | 
			
		||||
    description: 'This is Database custom property',
 | 
			
		||||
    integerValue: '14',
 | 
			
		||||
    stringValue: 'This is string propery',
 | 
			
		||||
    markdownValue: 'This is markdown value',
 | 
			
		||||
    enumConfig: {
 | 
			
		||||
      values: ['enum1', 'enum2', 'enum3'],
 | 
			
		||||
      multiSelect: false,
 | 
			
		||||
    },
 | 
			
		||||
    dateFormatConfig: 'yyyy-mm-dd',
 | 
			
		||||
    dateTimeFormatConfig: 'yyyy-mm-dd hh:mm:ss',
 | 
			
		||||
    entityReferenceConfig: ['User', 'Team'],
 | 
			
		||||
    entityObj: {},
 | 
			
		||||
    entityApiType: 'databases',
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  entity_databaseSchema: {
 | 
			
		||||
    name: 'databaseSchema',
 | 
			
		||||
    description: 'This is Database Schema custom property',
 | 
			
		||||
    integerValue: '14',
 | 
			
		||||
    stringValue: 'This is string propery',
 | 
			
		||||
    markdownValue: 'This is markdown value',
 | 
			
		||||
    enumConfig: {
 | 
			
		||||
      values: ['enum1', 'enum2', 'enum3'],
 | 
			
		||||
      multiSelect: false,
 | 
			
		||||
    },
 | 
			
		||||
    dateFormatConfig: 'yyyy-mm-dd',
 | 
			
		||||
    dateTimeFormatConfig: 'yyyy-mm-dd hh:mm:ss',
 | 
			
		||||
    entityReferenceConfig: ['User', 'Team'],
 | 
			
		||||
    entityObj: {},
 | 
			
		||||
    entityApiType: 'databaseSchemas',
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  entity_glossaryTerm: {
 | 
			
		||||
    name: 'glossaryTerm',
 | 
			
		||||
    description: 'This is Glossary Term custom property',
 | 
			
		||||
    integerValue: '14',
 | 
			
		||||
    stringValue: 'This is string propery',
 | 
			
		||||
    markdownValue: 'This is markdown value',
 | 
			
		||||
    enumConfig: {
 | 
			
		||||
      values: ['enum1', 'enum2', 'enum3'],
 | 
			
		||||
      multiSelect: false,
 | 
			
		||||
    },
 | 
			
		||||
    dateFormatConfig: 'yyyy-mm-dd',
 | 
			
		||||
    dateTimeFormatConfig: 'yyyy-mm-dd hh:mm:ss',
 | 
			
		||||
    entityReferenceConfig: ['User', 'Team'],
 | 
			
		||||
    entityObj: {},
 | 
			
		||||
    entityApiType: 'glossaryTerm',
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  entity_mlmodel: {
 | 
			
		||||
    name: 'mlmodel',
 | 
			
		||||
    description: 'This is ML Model custom property',
 | 
			
		||||
    integerValue: '14',
 | 
			
		||||
    stringValue: 'This is string propery',
 | 
			
		||||
    markdownValue: 'This is markdown value',
 | 
			
		||||
    enumConfig: {
 | 
			
		||||
      values: ['enum1', 'enum2', 'enum3'],
 | 
			
		||||
      multiSelect: false,
 | 
			
		||||
    },
 | 
			
		||||
    dateFormatConfig: 'yyyy-mm-dd',
 | 
			
		||||
    dateTimeFormatConfig: 'yyyy-mm-dd hh:mm:ss',
 | 
			
		||||
    entityReferenceConfig: ['User', 'Team'],
 | 
			
		||||
    entityObj: {},
 | 
			
		||||
    entityApiType: 'mlmodels',
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  entity_pipeline: {
 | 
			
		||||
    name: 'pipeline',
 | 
			
		||||
    description: 'This is Pipeline custom property',
 | 
			
		||||
    integerValue: '78',
 | 
			
		||||
    stringValue: 'This is string propery',
 | 
			
		||||
    markdownValue: 'This is markdown value',
 | 
			
		||||
    enumConfig: {
 | 
			
		||||
      values: ['enum1', 'enum2', 'enum3'],
 | 
			
		||||
      multiSelect: true,
 | 
			
		||||
    },
 | 
			
		||||
    dateFormatConfig: 'yyyy-mm-dd',
 | 
			
		||||
    dateTimeFormatConfig: 'yyyy-mm-dd hh:mm:ss',
 | 
			
		||||
    entityReferenceConfig: ['User', 'Team'],
 | 
			
		||||
    entityObj: SEARCH_ENTITY_PIPELINE.pipeline_1,
 | 
			
		||||
    entityApiType: 'pipelines',
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  entity_searchIndex: {
 | 
			
		||||
    name: 'searchIndex',
 | 
			
		||||
    description: 'This is Search Index custom property',
 | 
			
		||||
    integerValue: '14',
 | 
			
		||||
    stringValue: 'This is string propery',
 | 
			
		||||
    markdownValue: 'This is markdown value',
 | 
			
		||||
    enumConfig: {
 | 
			
		||||
      values: ['enum1', 'enum2', 'enum3'],
 | 
			
		||||
      multiSelect: false,
 | 
			
		||||
    },
 | 
			
		||||
    dateFormatConfig: 'yyyy-mm-dd',
 | 
			
		||||
    dateTimeFormatConfig: 'yyyy-mm-dd hh:mm:ss',
 | 
			
		||||
    entityReferenceConfig: ['User', 'Team'],
 | 
			
		||||
    entityObj: {},
 | 
			
		||||
    entityApiType: 'searchIndexes',
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  entity_storedProcedure: {
 | 
			
		||||
    name: 'storedProcedure',
 | 
			
		||||
    description: 'This is Stored Procedure custom property',
 | 
			
		||||
    integerValue: '14',
 | 
			
		||||
    stringValue: 'This is string propery',
 | 
			
		||||
    markdownValue: 'This is markdown value',
 | 
			
		||||
    enumConfig: {
 | 
			
		||||
      values: ['enum1', 'enum2', 'enum3'],
 | 
			
		||||
      multiSelect: false,
 | 
			
		||||
    },
 | 
			
		||||
    dateFormatConfig: 'yyyy-mm-dd',
 | 
			
		||||
    dateTimeFormatConfig: 'yyyy-mm-dd hh:mm:ss',
 | 
			
		||||
    entityReferenceConfig: ['User', 'Team'],
 | 
			
		||||
    entityObj: {},
 | 
			
		||||
    entityApiType: 'storedProcedures',
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  entity_table: {
 | 
			
		||||
    name: 'table',
 | 
			
		||||
    description: 'This is Table custom property',
 | 
			
		||||
    integerValue: '45',
 | 
			
		||||
    stringValue: 'This is string propery',
 | 
			
		||||
    markdownValue: 'This is markdown value',
 | 
			
		||||
    enumConfig: {
 | 
			
		||||
      values: ['enum1', 'enum2', 'enum3'],
 | 
			
		||||
      multiSelect: false,
 | 
			
		||||
    },
 | 
			
		||||
    dateFormatConfig: 'yyyy-mm-dd',
 | 
			
		||||
    dateTimeFormatConfig: 'yyyy-mm-dd hh:mm:ss',
 | 
			
		||||
    entityReferenceConfig: ['User', 'Team'],
 | 
			
		||||
    entityObj: SEARCH_ENTITY_TABLE.table_1,
 | 
			
		||||
    entityApiType: 'tables',
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  entity_topic: {
 | 
			
		||||
    name: 'topic',
 | 
			
		||||
    description: 'This is Topic custom property',
 | 
			
		||||
    integerValue: '23',
 | 
			
		||||
    stringValue: 'This is string propery',
 | 
			
		||||
    markdownValue: 'This is markdown value',
 | 
			
		||||
    enumConfig: {
 | 
			
		||||
      values: ['enum1', 'enum2', 'enum3'],
 | 
			
		||||
      multiSelect: false,
 | 
			
		||||
    },
 | 
			
		||||
    dateFormatConfig: 'yyyy-mm-dd',
 | 
			
		||||
    dateTimeFormatConfig: 'yyyy-mm-dd hh:mm:ss',
 | 
			
		||||
    entityReferenceConfig: ['User', 'Team'],
 | 
			
		||||
    entityObj: SEARCH_ENTITY_TOPIC.topic_1,
 | 
			
		||||
    entityApiType: 'topics',
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const LOGIN = {
 | 
			
		||||
  username: 'admin@openmetadata.org',
 | 
			
		||||
  password: 'admin',
 | 
			
		||||
@ -594,16 +405,6 @@ export const DESTINATION = {
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const CUSTOM_PROPERTY_INVALID_NAMES = {
 | 
			
		||||
  CAPITAL_CASE: 'CapitalCase',
 | 
			
		||||
  WITH_UNDERSCORE: 'with_underscore',
 | 
			
		||||
  WITH_DOTS: 'with.',
 | 
			
		||||
  WITH_SPACE: 'with ',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const CUSTOM_PROPERTY_NAME_VALIDATION_ERROR =
 | 
			
		||||
  'Name must start with lower case with no space, underscore, or dots.';
 | 
			
		||||
 | 
			
		||||
export const TAG_INVALID_NAMES = {
 | 
			
		||||
  MIN_LENGTH: 'c',
 | 
			
		||||
  MAX_LENGTH: 'a87439625b1c2d3e4f5061728394a5b6c7d8e90a1b2c3d4e5f67890ab',
 | 
			
		||||
 | 
			
		||||
@ -1,577 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  Copyright 2022 Collate.
 | 
			
		||||
 *  Licensed 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 { lowerCase } from 'lodash';
 | 
			
		||||
import { interceptURL, verifyResponseStatusCode } from '../../common/common';
 | 
			
		||||
import {
 | 
			
		||||
  createGlossary,
 | 
			
		||||
  createGlossaryTerms,
 | 
			
		||||
  deleteGlossary,
 | 
			
		||||
} from '../../common/GlossaryUtils';
 | 
			
		||||
import {
 | 
			
		||||
  addCustomPropertiesForEntity,
 | 
			
		||||
  CustomProperty,
 | 
			
		||||
  CustomPropertyType,
 | 
			
		||||
  deleteCreatedProperty,
 | 
			
		||||
  deleteCustomPropertyForEntity,
 | 
			
		||||
  editCreatedProperty,
 | 
			
		||||
  generateCustomProperty,
 | 
			
		||||
  setValueForProperty,
 | 
			
		||||
  validateValueForProperty,
 | 
			
		||||
} from '../../common/Utils/CustomProperty';
 | 
			
		||||
import { visitEntityDetailsPage } from '../../common/Utils/Entity';
 | 
			
		||||
import { updateJWTTokenExpiryTime } from '../../common/Utils/Login';
 | 
			
		||||
import { ENTITIES, JWT_EXPIRY_TIME_MAP, uuid } from '../../constants/constants';
 | 
			
		||||
import { EntityType, SidebarItem } from '../../constants/Entity.interface';
 | 
			
		||||
import { GLOSSARY_1 } from '../../constants/glossary.constant';
 | 
			
		||||
 | 
			
		||||
const glossaryTerm = {
 | 
			
		||||
  name: 'glossaryTerm',
 | 
			
		||||
  description: 'This is Glossary Term custom property',
 | 
			
		||||
  integerValue: '45',
 | 
			
		||||
  stringValue: 'This is string property',
 | 
			
		||||
  markdownValue: 'This is markdown value',
 | 
			
		||||
  entityApiType: 'glossaryTerm',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const customPropertyValue = {
 | 
			
		||||
  Integer: {
 | 
			
		||||
    value: '123',
 | 
			
		||||
    newValue: '456',
 | 
			
		||||
    property: generateCustomProperty(CustomPropertyType.INTEGER),
 | 
			
		||||
  },
 | 
			
		||||
  String: {
 | 
			
		||||
    value: '123',
 | 
			
		||||
    newValue: '456',
 | 
			
		||||
    property: generateCustomProperty(CustomPropertyType.STRING),
 | 
			
		||||
  },
 | 
			
		||||
  Markdown: {
 | 
			
		||||
    value: '**Bold statement**',
 | 
			
		||||
    newValue: '__Italic statement__',
 | 
			
		||||
    property: generateCustomProperty(CustomPropertyType.MARKDOWN),
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
describe('Custom Properties should work properly', { tags: 'Settings' }, () => {
 | 
			
		||||
  before(() => {
 | 
			
		||||
    cy.login();
 | 
			
		||||
    updateJWTTokenExpiryTime(JWT_EXPIRY_TIME_MAP['2 hours']);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  after(() => {
 | 
			
		||||
    cy.login();
 | 
			
		||||
    updateJWTTokenExpiryTime(JWT_EXPIRY_TIME_MAP['1 hour']);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  beforeEach(() => {
 | 
			
		||||
    cy.login();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  afterEach(() => {
 | 
			
		||||
    cy.logout();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  [
 | 
			
		||||
    'Integer',
 | 
			
		||||
    'String',
 | 
			
		||||
    'Markdown',
 | 
			
		||||
    'Duration',
 | 
			
		||||
    'Email',
 | 
			
		||||
    'Number',
 | 
			
		||||
    'Sql Query',
 | 
			
		||||
    // 'Time',
 | 
			
		||||
    // 'Time Interval',
 | 
			
		||||
    'Timestamp',
 | 
			
		||||
  ].forEach((type) => {
 | 
			
		||||
    describe(`Add update and delete ${type} custom properties`, () => {
 | 
			
		||||
      Object.values(ENTITIES).forEach((entity) => {
 | 
			
		||||
        const propertyName = `addcyentity${entity.name}test${uuid()}`;
 | 
			
		||||
 | 
			
		||||
        it(`Add/Update/Delete ${type} custom property for ${entity.name} Entities`, () => {
 | 
			
		||||
          interceptURL(
 | 
			
		||||
            'GET',
 | 
			
		||||
            `/api/v1/metadata/types/name/${entity.name}*`,
 | 
			
		||||
            'getEntity'
 | 
			
		||||
          );
 | 
			
		||||
          cy.settingClick(entity.entityApiType, true);
 | 
			
		||||
 | 
			
		||||
          verifyResponseStatusCode('@getEntity', 200);
 | 
			
		||||
 | 
			
		||||
          // Getting the property
 | 
			
		||||
          addCustomPropertiesForEntity({
 | 
			
		||||
            propertyName,
 | 
			
		||||
            customPropertyData: entity,
 | 
			
		||||
            customType: type,
 | 
			
		||||
          });
 | 
			
		||||
 | 
			
		||||
          // Navigating back to custom properties page
 | 
			
		||||
          cy.settingClick(entity.entityApiType, true);
 | 
			
		||||
          verifyResponseStatusCode('@getEntity', 200);
 | 
			
		||||
 | 
			
		||||
          // `Edit created property for ${entity.name} entity`
 | 
			
		||||
          interceptURL(
 | 
			
		||||
            'GET',
 | 
			
		||||
            `/api/v1/metadata/types/name/${entity.name}*`,
 | 
			
		||||
            'getEntity'
 | 
			
		||||
          );
 | 
			
		||||
 | 
			
		||||
          // Selecting the entity
 | 
			
		||||
          cy.settingClick(entity.entityApiType, true);
 | 
			
		||||
 | 
			
		||||
          verifyResponseStatusCode('@getEntity', 200);
 | 
			
		||||
          editCreatedProperty(propertyName);
 | 
			
		||||
 | 
			
		||||
          // `Delete created property for ${entity.name} entity`
 | 
			
		||||
          interceptURL(
 | 
			
		||||
            'GET',
 | 
			
		||||
            `/api/v1/metadata/types/name/${entity.name}*`,
 | 
			
		||||
            'getEntity'
 | 
			
		||||
          );
 | 
			
		||||
 | 
			
		||||
          // Selecting the entity
 | 
			
		||||
          cy.settingClick(entity.entityApiType, true);
 | 
			
		||||
 | 
			
		||||
          verifyResponseStatusCode('@getEntity', 200);
 | 
			
		||||
          deleteCreatedProperty(propertyName);
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  describe('Add update and delete Enum custom properties', () => {
 | 
			
		||||
    Object.values(ENTITIES).forEach((entity) => {
 | 
			
		||||
      const propertyName = `addcyentity${entity.name}test${uuid()}`;
 | 
			
		||||
 | 
			
		||||
      it(`Add/Update/Delete Enum custom property for ${entity.name} Entities`, () => {
 | 
			
		||||
        interceptURL(
 | 
			
		||||
          'GET',
 | 
			
		||||
          `/api/v1/metadata/types/name/${entity.name}*`,
 | 
			
		||||
          'getEntity'
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Selecting the entity
 | 
			
		||||
        cy.settingClick(entity.entityApiType, true);
 | 
			
		||||
 | 
			
		||||
        verifyResponseStatusCode('@getEntity', 200);
 | 
			
		||||
 | 
			
		||||
        addCustomPropertiesForEntity({
 | 
			
		||||
          propertyName,
 | 
			
		||||
          customPropertyData: entity,
 | 
			
		||||
          customType: 'Enum',
 | 
			
		||||
          enumConfig: entity.enumConfig,
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // Navigating back to custom properties page
 | 
			
		||||
        cy.settingClick(entity.entityApiType, true);
 | 
			
		||||
 | 
			
		||||
        verifyResponseStatusCode('@getEntity', 200);
 | 
			
		||||
 | 
			
		||||
        // `Edit created property for ${entity.name} entity`
 | 
			
		||||
        interceptURL(
 | 
			
		||||
          'GET',
 | 
			
		||||
          `/api/v1/metadata/types/name/${entity.name}*`,
 | 
			
		||||
          'getEntity'
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Selecting the entity
 | 
			
		||||
        cy.settingClick(entity.entityApiType, true);
 | 
			
		||||
 | 
			
		||||
        verifyResponseStatusCode('@getEntity', 200);
 | 
			
		||||
        editCreatedProperty(propertyName, 'Enum');
 | 
			
		||||
 | 
			
		||||
        // `Delete created property for ${entity.name} entity`
 | 
			
		||||
        interceptURL(
 | 
			
		||||
          'GET',
 | 
			
		||||
          `/api/v1/metadata/types/name/${entity.name}*`,
 | 
			
		||||
          'getEntity'
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Selecting the entity
 | 
			
		||||
        cy.settingClick(entity.entityApiType, true);
 | 
			
		||||
 | 
			
		||||
        verifyResponseStatusCode('@getEntity', 200);
 | 
			
		||||
        deleteCreatedProperty(propertyName);
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  describe('Add update and delete Entity Reference custom properties', () => {
 | 
			
		||||
    Object.values(ENTITIES).forEach((entity) => {
 | 
			
		||||
      const propertyName = `addcyentity${entity.name}test${uuid()}`;
 | 
			
		||||
 | 
			
		||||
      it(`Add/Update/Delete Entity Reference custom property for ${entity.name} Entities`, () => {
 | 
			
		||||
        interceptURL(
 | 
			
		||||
          'GET',
 | 
			
		||||
          `/api/v1/metadata/types/name/${entity.name}*`,
 | 
			
		||||
          'getEntity'
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Selecting the entity
 | 
			
		||||
        cy.settingClick(entity.entityApiType, true);
 | 
			
		||||
 | 
			
		||||
        verifyResponseStatusCode('@getEntity', 200);
 | 
			
		||||
 | 
			
		||||
        addCustomPropertiesForEntity({
 | 
			
		||||
          propertyName,
 | 
			
		||||
          customPropertyData: entity,
 | 
			
		||||
          customType: 'Entity Reference',
 | 
			
		||||
          entityReferenceConfig: entity.entityReferenceConfig,
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // Navigating back to custom properties page
 | 
			
		||||
        cy.settingClick(entity.entityApiType, true);
 | 
			
		||||
 | 
			
		||||
        verifyResponseStatusCode('@getEntity', 200);
 | 
			
		||||
 | 
			
		||||
        // `Edit created property for ${entity.name} entity`
 | 
			
		||||
        interceptURL(
 | 
			
		||||
          'GET',
 | 
			
		||||
          `/api/v1/metadata/types/name/${entity.name}*`,
 | 
			
		||||
          'getEntity'
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Selecting the entity
 | 
			
		||||
        cy.settingClick(entity.entityApiType, true);
 | 
			
		||||
 | 
			
		||||
        verifyResponseStatusCode('@getEntity', 200);
 | 
			
		||||
        editCreatedProperty(propertyName, 'Entity Reference');
 | 
			
		||||
 | 
			
		||||
        // `Delete created property for ${entity.name} entity`
 | 
			
		||||
        interceptURL(
 | 
			
		||||
          'GET',
 | 
			
		||||
          `/api/v1/metadata/types/name/${entity.name}*`,
 | 
			
		||||
          'getEntity'
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Selecting the entity
 | 
			
		||||
        cy.settingClick(entity.entityApiType, true);
 | 
			
		||||
 | 
			
		||||
        verifyResponseStatusCode('@getEntity', 200);
 | 
			
		||||
        deleteCreatedProperty(propertyName);
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  describe('Add update and delete Entity Reference List custom properties', () => {
 | 
			
		||||
    Object.values(ENTITIES).forEach((entity) => {
 | 
			
		||||
      const propertyName = `addcyentity${entity.name}test${uuid()}`;
 | 
			
		||||
 | 
			
		||||
      it(`Add/Update/Delete Entity Reference List custom property for ${entity.name} Entities`, () => {
 | 
			
		||||
        interceptURL(
 | 
			
		||||
          'GET',
 | 
			
		||||
          `/api/v1/metadata/types/name/${entity.name}*`,
 | 
			
		||||
          'getEntity'
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Selecting the entity
 | 
			
		||||
        cy.settingClick(entity.entityApiType, true);
 | 
			
		||||
 | 
			
		||||
        verifyResponseStatusCode('@getEntity', 200);
 | 
			
		||||
 | 
			
		||||
        addCustomPropertiesForEntity({
 | 
			
		||||
          propertyName,
 | 
			
		||||
          customPropertyData: entity,
 | 
			
		||||
          customType: 'Entity Reference List',
 | 
			
		||||
          entityReferenceConfig: entity.entityReferenceConfig,
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // Navigating back to custom properties page
 | 
			
		||||
        cy.settingClick(entity.entityApiType, true);
 | 
			
		||||
 | 
			
		||||
        verifyResponseStatusCode('@getEntity', 200);
 | 
			
		||||
 | 
			
		||||
        // `Edit created property for ${entity.name} entity`
 | 
			
		||||
        interceptURL(
 | 
			
		||||
          'GET',
 | 
			
		||||
          `/api/v1/metadata/types/name/${entity.name}*`,
 | 
			
		||||
          'getEntity'
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Selecting the entity
 | 
			
		||||
        cy.settingClick(entity.entityApiType, true);
 | 
			
		||||
 | 
			
		||||
        verifyResponseStatusCode('@getEntity', 200);
 | 
			
		||||
        editCreatedProperty(propertyName, 'Entity Reference List');
 | 
			
		||||
 | 
			
		||||
        // `Delete created property for ${entity.name} entity`
 | 
			
		||||
        interceptURL(
 | 
			
		||||
          'GET',
 | 
			
		||||
          `/api/v1/metadata/types/name/${entity.name}*`,
 | 
			
		||||
          'getEntity'
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Selecting the entity
 | 
			
		||||
        cy.settingClick(entity.entityApiType, true);
 | 
			
		||||
 | 
			
		||||
        verifyResponseStatusCode('@getEntity', 200);
 | 
			
		||||
        deleteCreatedProperty(propertyName);
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  // eslint-disable-next-line jest/no-disabled-tests
 | 
			
		||||
  describe.skip('Add update and delete Date custom properties', () => {
 | 
			
		||||
    Object.values(ENTITIES).forEach((entity) => {
 | 
			
		||||
      const propertyName = `addcyentity${entity.name}test${uuid()}`;
 | 
			
		||||
 | 
			
		||||
      it(`Add Date custom property for ${entity.name} Entities`, () => {
 | 
			
		||||
        interceptURL(
 | 
			
		||||
          'GET',
 | 
			
		||||
          `/api/v1/metadata/types/name/${entity.name}*`,
 | 
			
		||||
          'getEntity'
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Selecting the entity
 | 
			
		||||
        cy.settingClick(entity.entityApiType, true);
 | 
			
		||||
 | 
			
		||||
        verifyResponseStatusCode('@getEntity', 200);
 | 
			
		||||
 | 
			
		||||
        addCustomPropertiesForEntity({
 | 
			
		||||
          propertyName,
 | 
			
		||||
          customPropertyData: entity,
 | 
			
		||||
          customType: 'Date',
 | 
			
		||||
          formatConfig: entity.dateFormatConfig,
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // Navigating back to custom properties page
 | 
			
		||||
        cy.settingClick(entity.entityApiType, true);
 | 
			
		||||
 | 
			
		||||
        verifyResponseStatusCode('@getEntity', 200);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      it(`Edit created property for ${entity.name} entity`, () => {
 | 
			
		||||
        interceptURL(
 | 
			
		||||
          'GET',
 | 
			
		||||
          `/api/v1/metadata/types/name/${entity.name}*`,
 | 
			
		||||
          'getEntity'
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Selecting the entity
 | 
			
		||||
        cy.settingClick(entity.entityApiType, true);
 | 
			
		||||
 | 
			
		||||
        verifyResponseStatusCode('@getEntity', 200);
 | 
			
		||||
        editCreatedProperty(propertyName);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      it(`Delete created property for ${entity.name} entity`, () => {
 | 
			
		||||
        interceptURL(
 | 
			
		||||
          'GET',
 | 
			
		||||
          `/api/v1/metadata/types/name/${entity.name}*`,
 | 
			
		||||
          'getEntity'
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Selecting the entity
 | 
			
		||||
        cy.settingClick(entity.entityApiType, true);
 | 
			
		||||
 | 
			
		||||
        verifyResponseStatusCode('@getEntity', 200);
 | 
			
		||||
        deleteCreatedProperty(propertyName);
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  // eslint-disable-next-line jest/no-disabled-tests
 | 
			
		||||
  describe.skip('Add update and delete DateTime custom properties', () => {
 | 
			
		||||
    Object.values(ENTITIES).forEach((entity) => {
 | 
			
		||||
      const propertyName = `addcyentity${entity.name}test${uuid()}`;
 | 
			
		||||
 | 
			
		||||
      it(`Add DateTime custom property for ${entity.name} Entities`, () => {
 | 
			
		||||
        interceptURL(
 | 
			
		||||
          'GET',
 | 
			
		||||
          `/api/v1/metadata/types/name/${entity.name}*`,
 | 
			
		||||
          'getEntity'
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Selecting the entity
 | 
			
		||||
        cy.settingClick(entity.entityApiType, true);
 | 
			
		||||
 | 
			
		||||
        verifyResponseStatusCode('@getEntity', 200);
 | 
			
		||||
 | 
			
		||||
        addCustomPropertiesForEntity({
 | 
			
		||||
          propertyName,
 | 
			
		||||
          customPropertyData: entity,
 | 
			
		||||
          customType: 'Date Time',
 | 
			
		||||
          formatConfig: entity.dateTimeFormatConfig,
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // Navigating back to custom properties page
 | 
			
		||||
        cy.settingClick(entity.entityApiType, true);
 | 
			
		||||
 | 
			
		||||
        verifyResponseStatusCode('@getEntity', 200);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      it(`Edit created property for ${entity.name} entity`, () => {
 | 
			
		||||
        interceptURL(
 | 
			
		||||
          'GET',
 | 
			
		||||
          `/api/v1/metadata/types/name/${entity.name}*`,
 | 
			
		||||
          'getEntity'
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Selecting the entity
 | 
			
		||||
        cy.settingClick(entity.entityApiType, true);
 | 
			
		||||
 | 
			
		||||
        verifyResponseStatusCode('@getEntity', 200);
 | 
			
		||||
        editCreatedProperty(propertyName);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      it(`Delete created property for ${entity.name} entity`, () => {
 | 
			
		||||
        interceptURL(
 | 
			
		||||
          'GET',
 | 
			
		||||
          `/api/v1/metadata/types/name/${entity.name}*`,
 | 
			
		||||
          'getEntity'
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        // Selecting the entity
 | 
			
		||||
        cy.settingClick(entity.entityApiType, true);
 | 
			
		||||
 | 
			
		||||
        verifyResponseStatusCode('@getEntity', 200);
 | 
			
		||||
        deleteCreatedProperty(propertyName);
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  describe('Custom properties for glossary and glossary terms', () => {
 | 
			
		||||
    const propertyName = `addcyentity${glossaryTerm.name}test${uuid()}`;
 | 
			
		||||
    const properties = Object.values(CustomPropertyType).join(', ');
 | 
			
		||||
 | 
			
		||||
    it('test custom properties in advanced search modal', () => {
 | 
			
		||||
      cy.settingClick(glossaryTerm.entityApiType, true);
 | 
			
		||||
 | 
			
		||||
      addCustomPropertiesForEntity({
 | 
			
		||||
        propertyName,
 | 
			
		||||
        customPropertyData: glossaryTerm,
 | 
			
		||||
        customType: 'Integer',
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      // Navigating to explore page
 | 
			
		||||
      cy.sidebarClick(SidebarItem.EXPLORE);
 | 
			
		||||
      interceptURL(
 | 
			
		||||
        'GET',
 | 
			
		||||
        `/api/v1/metadata/types/name/glossaryTerm*`,
 | 
			
		||||
        'getEntity'
 | 
			
		||||
      );
 | 
			
		||||
      cy.get(
 | 
			
		||||
        `[data-testid=${Cypress.$.escapeSelector('glossary terms-tab')}]`
 | 
			
		||||
      ).click();
 | 
			
		||||
 | 
			
		||||
      cy.get('[data-testid="advance-search-button"]').click();
 | 
			
		||||
      verifyResponseStatusCode('@getEntity', 200);
 | 
			
		||||
 | 
			
		||||
      // Click on field dropdown
 | 
			
		||||
      cy.get('.rule--field > .ant-select > .ant-select-selector').eq(0).click();
 | 
			
		||||
 | 
			
		||||
      // Select custom property fields
 | 
			
		||||
      cy.get(`[title="Custom Properties"]`).eq(0).click();
 | 
			
		||||
 | 
			
		||||
      // Click on field dropdown
 | 
			
		||||
      cy.get('.rule--field > .ant-select > .ant-select-selector').eq(0).click();
 | 
			
		||||
 | 
			
		||||
      // Verify field exists
 | 
			
		||||
      cy.get(`[title="${propertyName}"]`).should('be.visible');
 | 
			
		||||
 | 
			
		||||
      cy.get('[data-testid="cancel-btn"]').click();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it(`Delete created property for glossary term entity`, () => {
 | 
			
		||||
      interceptURL(
 | 
			
		||||
        'GET',
 | 
			
		||||
        `/api/v1/metadata/types/name/${glossaryTerm.name}*`,
 | 
			
		||||
        'getEntity'
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      // Selecting the entity
 | 
			
		||||
      cy.settingClick(glossaryTerm.entityApiType, true);
 | 
			
		||||
 | 
			
		||||
      verifyResponseStatusCode('@getEntity', 200);
 | 
			
		||||
      deleteCreatedProperty(propertyName);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // TODO: Need to fix this for mysql due to data issue @Sachin-chaurasiya
 | 
			
		||||
    // eslint-disable-next-line jest/no-disabled-tests
 | 
			
		||||
    it.skip(`Add update and delete ${properties} custom properties for glossary term `, () => {
 | 
			
		||||
      interceptURL('GET', '/api/v1/glossaryTerms*', 'getGlossaryTerms');
 | 
			
		||||
      interceptURL('GET', '/api/v1/glossaries?fields=*', 'fetchGlossaries');
 | 
			
		||||
 | 
			
		||||
      cy.sidebarClick(SidebarItem.GLOSSARY);
 | 
			
		||||
      const glossary = GLOSSARY_1;
 | 
			
		||||
      glossary.terms = [GLOSSARY_1.terms[0]];
 | 
			
		||||
 | 
			
		||||
      createGlossary(GLOSSARY_1, false);
 | 
			
		||||
      createGlossaryTerms(glossary);
 | 
			
		||||
 | 
			
		||||
      cy.settingClick(glossaryTerm.entityApiType, true);
 | 
			
		||||
 | 
			
		||||
      Object.values(CustomPropertyType).forEach((type) => {
 | 
			
		||||
        addCustomPropertiesForEntity({
 | 
			
		||||
          propertyName: lowerCase(type),
 | 
			
		||||
          customPropertyData: glossaryTerm,
 | 
			
		||||
          customType: type,
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        cy.settingClick(glossaryTerm.entityApiType, true);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      visitEntityDetailsPage({
 | 
			
		||||
        term: glossary.terms[0].name,
 | 
			
		||||
        serviceName: glossary.terms[0].fullyQualifiedName,
 | 
			
		||||
        entity: 'glossaryTerms' as EntityType,
 | 
			
		||||
        dataTestId: `${glossary.name}-${glossary.terms[0].name}`,
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      // set custom property value
 | 
			
		||||
      Object.values(CustomPropertyType).forEach((type) => {
 | 
			
		||||
        setValueForProperty(
 | 
			
		||||
          lowerCase(type),
 | 
			
		||||
          customPropertyValue[type].value,
 | 
			
		||||
          lowerCase(type)
 | 
			
		||||
        );
 | 
			
		||||
        validateValueForProperty(
 | 
			
		||||
          lowerCase(type),
 | 
			
		||||
          customPropertyValue[type].value,
 | 
			
		||||
          lowerCase(type)
 | 
			
		||||
        );
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      // update custom property value
 | 
			
		||||
      Object.values(CustomPropertyType).forEach((type) => {
 | 
			
		||||
        setValueForProperty(
 | 
			
		||||
          lowerCase(type),
 | 
			
		||||
          customPropertyValue[type].newValue,
 | 
			
		||||
          lowerCase(type)
 | 
			
		||||
        );
 | 
			
		||||
        validateValueForProperty(
 | 
			
		||||
          lowerCase(type),
 | 
			
		||||
          customPropertyValue[type].newValue,
 | 
			
		||||
          lowerCase(type)
 | 
			
		||||
        );
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      // delete custom properties
 | 
			
		||||
      Object.values(CustomPropertyType).forEach((customPropertyType) => {
 | 
			
		||||
        const type = glossaryTerm.entityApiType as EntityType;
 | 
			
		||||
        const property = customPropertyValue[customPropertyType].property ?? {};
 | 
			
		||||
 | 
			
		||||
        deleteCustomPropertyForEntity({
 | 
			
		||||
          property: {
 | 
			
		||||
            ...property,
 | 
			
		||||
            name: lowerCase(customPropertyType),
 | 
			
		||||
          } as CustomProperty,
 | 
			
		||||
          type,
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      // delete glossary and glossary term
 | 
			
		||||
      cy.sidebarClick(SidebarItem.GLOSSARY);
 | 
			
		||||
      deleteGlossary(glossary.name);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
@ -32,3 +32,213 @@ export const ENTITY_REFERENCE_PROPERTIES = [
 | 
			
		||||
  'Entity Reference',
 | 
			
		||||
  'Entity Reference List',
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
export const CUSTOM_PROPERTIES_ENTITIES = {
 | 
			
		||||
  entity_container: {
 | 
			
		||||
    name: 'container',
 | 
			
		||||
    description: 'This is Container custom property',
 | 
			
		||||
    integerValue: '14',
 | 
			
		||||
    stringValue: 'This is string propery',
 | 
			
		||||
    markdownValue: 'This is markdown value',
 | 
			
		||||
    enumConfig: {
 | 
			
		||||
      values: ['enum1', 'enum2', 'enum3'],
 | 
			
		||||
      multiSelect: false,
 | 
			
		||||
    },
 | 
			
		||||
    dateFormatConfig: 'yyyy-MM-dd',
 | 
			
		||||
    dateTimeFormatConfig: 'yyyy-MM-dd HH:mm:ss',
 | 
			
		||||
    timeFormatConfig: 'HH:mm:ss',
 | 
			
		||||
    entityReferenceConfig: ['User', 'Team'],
 | 
			
		||||
    entityObj: {},
 | 
			
		||||
    entityApiType: 'containers',
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  entity_dashboard: {
 | 
			
		||||
    name: 'dashboard',
 | 
			
		||||
    description: 'This is Dashboard custom property',
 | 
			
		||||
    integerValue: '14',
 | 
			
		||||
    stringValue: 'This is string propery',
 | 
			
		||||
    markdownValue: 'This is markdown value',
 | 
			
		||||
    enumConfig: {
 | 
			
		||||
      values: ['enum1', 'enum2', 'enum3'],
 | 
			
		||||
      multiSelect: false,
 | 
			
		||||
    },
 | 
			
		||||
    dateFormatConfig: 'yyyy-MM-dd',
 | 
			
		||||
    dateTimeFormatConfig: 'yyyy-MM-dd HH:mm:ss',
 | 
			
		||||
    timeFormatConfig: 'HH:mm:ss',
 | 
			
		||||
    entityReferenceConfig: ['User', 'Team'],
 | 
			
		||||
    entityObj: {},
 | 
			
		||||
    entityApiType: 'dashboards',
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  entity_database: {
 | 
			
		||||
    name: 'database',
 | 
			
		||||
    description: 'This is Database custom property',
 | 
			
		||||
    integerValue: '14',
 | 
			
		||||
    stringValue: 'This is string propery',
 | 
			
		||||
    markdownValue: 'This is markdown value',
 | 
			
		||||
    enumConfig: {
 | 
			
		||||
      values: ['enum1', 'enum2', 'enum3'],
 | 
			
		||||
      multiSelect: false,
 | 
			
		||||
    },
 | 
			
		||||
    dateFormatConfig: 'yyyy-MM-dd',
 | 
			
		||||
    dateTimeFormatConfig: 'yyyy-MM-dd HH:mm:ss',
 | 
			
		||||
    timeFormatConfig: 'HH:mm:ss',
 | 
			
		||||
    entityReferenceConfig: ['User', 'Team'],
 | 
			
		||||
    entityObj: {},
 | 
			
		||||
    entityApiType: 'databases',
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  entity_databaseSchema: {
 | 
			
		||||
    name: 'databaseSchema',
 | 
			
		||||
    description: 'This is Database Schema custom property',
 | 
			
		||||
    integerValue: '14',
 | 
			
		||||
    stringValue: 'This is string propery',
 | 
			
		||||
    markdownValue: 'This is markdown value',
 | 
			
		||||
    enumConfig: {
 | 
			
		||||
      values: ['enum1', 'enum2', 'enum3'],
 | 
			
		||||
      multiSelect: false,
 | 
			
		||||
    },
 | 
			
		||||
    dateFormatConfig: 'yyyy-MM-dd',
 | 
			
		||||
    dateTimeFormatConfig: 'yyyy-MM-dd HH:mm:ss',
 | 
			
		||||
    timeFormatConfig: 'HH:mm:ss',
 | 
			
		||||
    entityReferenceConfig: ['User', 'Team'],
 | 
			
		||||
    entityObj: {},
 | 
			
		||||
    entityApiType: 'databaseSchemas',
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  entity_glossaryTerm: {
 | 
			
		||||
    name: 'glossaryTerm',
 | 
			
		||||
    description: 'This is Glossary Term custom property',
 | 
			
		||||
    integerValue: '14',
 | 
			
		||||
    stringValue: 'This is string propery',
 | 
			
		||||
    markdownValue: 'This is markdown value',
 | 
			
		||||
    enumConfig: {
 | 
			
		||||
      values: ['enum1', 'enum2', 'enum3'],
 | 
			
		||||
      multiSelect: false,
 | 
			
		||||
    },
 | 
			
		||||
    dateFormatConfig: 'yyyy-MM-dd',
 | 
			
		||||
    dateTimeFormatConfig: 'yyyy-MM-dd HH:mm:ss',
 | 
			
		||||
    timeFormatConfig: 'HH:mm:ss',
 | 
			
		||||
    entityReferenceConfig: ['User', 'Team'],
 | 
			
		||||
    entityObj: {},
 | 
			
		||||
    entityApiType: 'glossaryTerm',
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  entity_mlmodel: {
 | 
			
		||||
    name: 'mlmodel',
 | 
			
		||||
    description: 'This is ML Model custom property',
 | 
			
		||||
    integerValue: '14',
 | 
			
		||||
    stringValue: 'This is string propery',
 | 
			
		||||
    markdownValue: 'This is markdown value',
 | 
			
		||||
    enumConfig: {
 | 
			
		||||
      values: ['enum1', 'enum2', 'enum3'],
 | 
			
		||||
      multiSelect: false,
 | 
			
		||||
    },
 | 
			
		||||
    dateFormatConfig: 'yyyy-MM-dd',
 | 
			
		||||
    dateTimeFormatConfig: 'yyyy-MM-dd HH:mm:ss',
 | 
			
		||||
    timeFormatConfig: 'HH:mm:ss',
 | 
			
		||||
    entityReferenceConfig: ['User', 'Team'],
 | 
			
		||||
    entityObj: {},
 | 
			
		||||
    entityApiType: 'mlmodels',
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  entity_pipeline: {
 | 
			
		||||
    name: 'pipeline',
 | 
			
		||||
    description: 'This is Pipeline custom property',
 | 
			
		||||
    integerValue: '78',
 | 
			
		||||
    stringValue: 'This is string propery',
 | 
			
		||||
    markdownValue: 'This is markdown value',
 | 
			
		||||
    enumConfig: {
 | 
			
		||||
      values: ['enum1', 'enum2', 'enum3'],
 | 
			
		||||
      multiSelect: true,
 | 
			
		||||
    },
 | 
			
		||||
    dateFormatConfig: 'yyyy-MM-dd',
 | 
			
		||||
    dateTimeFormatConfig: 'yyyy-MM-dd HH:mm:ss',
 | 
			
		||||
    timeFormatConfig: 'HH:mm:ss',
 | 
			
		||||
    entityReferenceConfig: ['User', 'Team'],
 | 
			
		||||
    entityObj: {},
 | 
			
		||||
    entityApiType: 'pipelines',
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  entity_searchIndex: {
 | 
			
		||||
    name: 'searchIndex',
 | 
			
		||||
    description: 'This is Search Index custom property',
 | 
			
		||||
    integerValue: '14',
 | 
			
		||||
    stringValue: 'This is string propery',
 | 
			
		||||
    markdownValue: 'This is markdown value',
 | 
			
		||||
    enumConfig: {
 | 
			
		||||
      values: ['enum1', 'enum2', 'enum3'],
 | 
			
		||||
      multiSelect: false,
 | 
			
		||||
    },
 | 
			
		||||
    dateFormatConfig: 'yyyy-MM-dd',
 | 
			
		||||
    dateTimeFormatConfig: 'yyyy-MM-dd HH:mm:ss',
 | 
			
		||||
    timeFormatConfig: 'HH:mm:ss',
 | 
			
		||||
    entityReferenceConfig: ['User', 'Team'],
 | 
			
		||||
    entityObj: {},
 | 
			
		||||
    entityApiType: 'searchIndexes',
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  entity_storedProcedure: {
 | 
			
		||||
    name: 'storedProcedure',
 | 
			
		||||
    description: 'This is Stored Procedure custom property',
 | 
			
		||||
    integerValue: '14',
 | 
			
		||||
    stringValue: 'This is string propery',
 | 
			
		||||
    markdownValue: 'This is markdown value',
 | 
			
		||||
    enumConfig: {
 | 
			
		||||
      values: ['enum1', 'enum2', 'enum3'],
 | 
			
		||||
      multiSelect: false,
 | 
			
		||||
    },
 | 
			
		||||
    dateFormatConfig: 'yyyy-MM-dd',
 | 
			
		||||
    dateTimeFormatConfig: 'yyyy-MM-dd HH:mm:ss',
 | 
			
		||||
    timeFormatConfig: 'HH:mm:ss',
 | 
			
		||||
    entityReferenceConfig: ['User', 'Team'],
 | 
			
		||||
    entityObj: {},
 | 
			
		||||
    entityApiType: 'storedProcedures',
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  entity_table: {
 | 
			
		||||
    name: 'table',
 | 
			
		||||
    description: 'This is Table custom property',
 | 
			
		||||
    integerValue: '45',
 | 
			
		||||
    stringValue: 'This is string propery',
 | 
			
		||||
    markdownValue: 'This is markdown value',
 | 
			
		||||
    enumConfig: {
 | 
			
		||||
      values: ['enum1', 'enum2', 'enum3'],
 | 
			
		||||
      multiSelect: false,
 | 
			
		||||
    },
 | 
			
		||||
    dateFormatConfig: 'yyyy-MM-dd',
 | 
			
		||||
    dateTimeFormatConfig: 'yyyy-MM-dd HH:mm:ss',
 | 
			
		||||
    timeFormatConfig: 'HH:mm:ss',
 | 
			
		||||
    entityReferenceConfig: ['User', 'Team'],
 | 
			
		||||
    entityObj: {},
 | 
			
		||||
    entityApiType: 'tables',
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  entity_topic: {
 | 
			
		||||
    name: 'topic',
 | 
			
		||||
    description: 'This is Topic custom property',
 | 
			
		||||
    integerValue: '23',
 | 
			
		||||
    stringValue: 'This is string propery',
 | 
			
		||||
    markdownValue: 'This is markdown value',
 | 
			
		||||
    enumConfig: {
 | 
			
		||||
      values: ['enum1', 'enum2', 'enum3'],
 | 
			
		||||
      multiSelect: false,
 | 
			
		||||
    },
 | 
			
		||||
    dateFormatConfig: 'yyyy-MM-dd',
 | 
			
		||||
    dateTimeFormatConfig: 'yyyy-MM-dd HH:mm:ss',
 | 
			
		||||
    timeFormatConfig: 'HH:mm:ss',
 | 
			
		||||
    entityReferenceConfig: ['User', 'Team'],
 | 
			
		||||
    entityObj: {},
 | 
			
		||||
    entityApiType: 'topics',
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const CUSTOM_PROPERTY_INVALID_NAMES = {
 | 
			
		||||
  CAPITAL_CASE: 'CapitalCase',
 | 
			
		||||
  WITH_UNDERSCORE: 'with_underscore',
 | 
			
		||||
  WITH_DOTS: 'with.',
 | 
			
		||||
  WITH_SPACE: 'with ',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const CUSTOM_PROPERTY_NAME_VALIDATION_ERROR =
 | 
			
		||||
  'Name must start with lower case with no space, underscore, or dots.';
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,69 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  Copyright 2024 Collate.
 | 
			
		||||
 *  Licensed 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 test from '@playwright/test';
 | 
			
		||||
import { CUSTOM_PROPERTIES_ENTITIES } from '../../constant/customProperty';
 | 
			
		||||
import { redirectToHomePage, uuid } from '../../utils/common';
 | 
			
		||||
import {
 | 
			
		||||
  addCustomPropertiesForEntity,
 | 
			
		||||
  deleteCreatedProperty,
 | 
			
		||||
  editCreatedProperty,
 | 
			
		||||
} from '../../utils/customProperty';
 | 
			
		||||
import { settingClick } from '../../utils/sidebar';
 | 
			
		||||
 | 
			
		||||
const propertiesList = [
 | 
			
		||||
  'Integer',
 | 
			
		||||
  'String',
 | 
			
		||||
  'Markdown',
 | 
			
		||||
  'Duration',
 | 
			
		||||
  'Email',
 | 
			
		||||
  'Number',
 | 
			
		||||
  'Sql Query',
 | 
			
		||||
  'Time Interval',
 | 
			
		||||
  'Timestamp',
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
// use the admin user to login
 | 
			
		||||
test.use({ storageState: 'playwright/.auth/admin.json' });
 | 
			
		||||
 | 
			
		||||
test.describe('Custom properties without custom property config', () => {
 | 
			
		||||
  test.beforeEach('Visit Home Page', async ({ page }) => {
 | 
			
		||||
    await redirectToHomePage(page);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  propertiesList.forEach((property) => {
 | 
			
		||||
    test.describe(`Add update and delete ${property} custom properties`, () => {
 | 
			
		||||
      Object.values(CUSTOM_PROPERTIES_ENTITIES).forEach(async (entity) => {
 | 
			
		||||
        const propertyName = `pwcustomproperty${entity.name}test${uuid()}`;
 | 
			
		||||
 | 
			
		||||
        test(`Add ${property} custom property for ${entity.name}`, async ({
 | 
			
		||||
          page,
 | 
			
		||||
        }) => {
 | 
			
		||||
          test.slow(true);
 | 
			
		||||
 | 
			
		||||
          await settingClick(page, entity.entityApiType, true);
 | 
			
		||||
 | 
			
		||||
          await addCustomPropertiesForEntity({
 | 
			
		||||
            page,
 | 
			
		||||
            propertyName,
 | 
			
		||||
            customPropertyData: entity,
 | 
			
		||||
            customType: property,
 | 
			
		||||
          });
 | 
			
		||||
 | 
			
		||||
          await editCreatedProperty(page, propertyName);
 | 
			
		||||
 | 
			
		||||
          await deleteCreatedProperty(page, propertyName);
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
@ -0,0 +1,186 @@
 | 
			
		||||
/*
 | 
			
		||||
 *  Copyright 2024 Collate.
 | 
			
		||||
 *  Licensed 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 test from '@playwright/test';
 | 
			
		||||
import { CUSTOM_PROPERTIES_ENTITIES } from '../../constant/customProperty';
 | 
			
		||||
import { redirectToHomePage, uuid } from '../../utils/common';
 | 
			
		||||
import {
 | 
			
		||||
  addCustomPropertiesForEntity,
 | 
			
		||||
  deleteCreatedProperty,
 | 
			
		||||
  editCreatedProperty,
 | 
			
		||||
} from '../../utils/customProperty';
 | 
			
		||||
import { settingClick } from '../../utils/sidebar';
 | 
			
		||||
 | 
			
		||||
// use the admin user to login
 | 
			
		||||
test.use({ storageState: 'playwright/.auth/admin.json' });
 | 
			
		||||
 | 
			
		||||
test.describe('Custom properties with custom property config', () => {
 | 
			
		||||
  test.beforeEach('Visit Home Page', async ({ page }) => {
 | 
			
		||||
    await redirectToHomePage(page);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  test.describe('Add update and delete Enum custom properties', () => {
 | 
			
		||||
    Object.values(CUSTOM_PROPERTIES_ENTITIES).forEach(async (entity) => {
 | 
			
		||||
      const propertyName = `pwcustomproperty${entity.name}test${uuid()}`;
 | 
			
		||||
 | 
			
		||||
      test(`Add Enum custom property for ${entity.name}`, async ({ page }) => {
 | 
			
		||||
        test.slow(true);
 | 
			
		||||
 | 
			
		||||
        await settingClick(page, entity.entityApiType, true);
 | 
			
		||||
 | 
			
		||||
        await addCustomPropertiesForEntity({
 | 
			
		||||
          page,
 | 
			
		||||
          propertyName,
 | 
			
		||||
          customPropertyData: entity,
 | 
			
		||||
          customType: 'Enum',
 | 
			
		||||
          enumConfig: entity.enumConfig,
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        await editCreatedProperty(page, propertyName);
 | 
			
		||||
 | 
			
		||||
        await deleteCreatedProperty(page, propertyName);
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  test.describe(
 | 
			
		||||
    'Add update and delete Entity Reference custom properties',
 | 
			
		||||
    () => {
 | 
			
		||||
      Object.values(CUSTOM_PROPERTIES_ENTITIES).forEach(async (entity) => {
 | 
			
		||||
        const propertyName = `pwcustomproperty${entity.name}test${uuid()}`;
 | 
			
		||||
 | 
			
		||||
        test(`Add Entity Reference custom property for ${entity.name}`, async ({
 | 
			
		||||
          page,
 | 
			
		||||
        }) => {
 | 
			
		||||
          test.slow(true);
 | 
			
		||||
 | 
			
		||||
          await settingClick(page, entity.entityApiType, true);
 | 
			
		||||
 | 
			
		||||
          await addCustomPropertiesForEntity({
 | 
			
		||||
            page,
 | 
			
		||||
            propertyName,
 | 
			
		||||
            customPropertyData: entity,
 | 
			
		||||
            customType: 'Entity Reference',
 | 
			
		||||
            entityReferenceConfig: entity.entityReferenceConfig,
 | 
			
		||||
          });
 | 
			
		||||
 | 
			
		||||
          await editCreatedProperty(page, propertyName);
 | 
			
		||||
 | 
			
		||||
          await deleteCreatedProperty(page, propertyName);
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  test.describe(
 | 
			
		||||
    'Add update and delete Entity Reference List custom properties',
 | 
			
		||||
    () => {
 | 
			
		||||
      Object.values(CUSTOM_PROPERTIES_ENTITIES).forEach(async (entity) => {
 | 
			
		||||
        const propertyName = `pwcustomproperty${entity.name}test${uuid()}`;
 | 
			
		||||
 | 
			
		||||
        test(`Add Entity Reference list custom property for ${entity.name}`, async ({
 | 
			
		||||
          page,
 | 
			
		||||
        }) => {
 | 
			
		||||
          test.slow(true);
 | 
			
		||||
 | 
			
		||||
          await settingClick(page, entity.entityApiType, true);
 | 
			
		||||
 | 
			
		||||
          await addCustomPropertiesForEntity({
 | 
			
		||||
            page,
 | 
			
		||||
            propertyName,
 | 
			
		||||
            customPropertyData: entity,
 | 
			
		||||
            customType: 'Entity Reference List',
 | 
			
		||||
            entityReferenceConfig: entity.entityReferenceConfig,
 | 
			
		||||
          });
 | 
			
		||||
 | 
			
		||||
          await editCreatedProperty(page, propertyName);
 | 
			
		||||
 | 
			
		||||
          await deleteCreatedProperty(page, propertyName);
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  test.describe('Add update and delete Date custom properties', () => {
 | 
			
		||||
    Object.values(CUSTOM_PROPERTIES_ENTITIES).forEach(async (entity) => {
 | 
			
		||||
      const propertyName = `pwcustomproperty${entity.name}test${uuid()}`;
 | 
			
		||||
 | 
			
		||||
      test(`Add Date custom property for ${entity.name}`, async ({ page }) => {
 | 
			
		||||
        test.slow(true);
 | 
			
		||||
 | 
			
		||||
        await settingClick(page, entity.entityApiType, true);
 | 
			
		||||
 | 
			
		||||
        await addCustomPropertiesForEntity({
 | 
			
		||||
          page,
 | 
			
		||||
          propertyName,
 | 
			
		||||
          customPropertyData: entity,
 | 
			
		||||
          customType: 'Date',
 | 
			
		||||
          formatConfig: entity.dateFormatConfig,
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        await editCreatedProperty(page, propertyName);
 | 
			
		||||
 | 
			
		||||
        await deleteCreatedProperty(page, propertyName);
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  test.describe('Add update and delete Time custom properties', () => {
 | 
			
		||||
    Object.values(CUSTOM_PROPERTIES_ENTITIES).forEach(async (entity) => {
 | 
			
		||||
      const propertyName = `pwcustomproperty${entity.name}test${uuid()}`;
 | 
			
		||||
 | 
			
		||||
      test(`Add Time custom property for ${entity.name}`, async ({ page }) => {
 | 
			
		||||
        test.slow(true);
 | 
			
		||||
 | 
			
		||||
        await settingClick(page, entity.entityApiType, true);
 | 
			
		||||
 | 
			
		||||
        await addCustomPropertiesForEntity({
 | 
			
		||||
          page,
 | 
			
		||||
          propertyName,
 | 
			
		||||
          customPropertyData: entity,
 | 
			
		||||
          customType: 'Time',
 | 
			
		||||
          formatConfig: entity.timeFormatConfig,
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        await editCreatedProperty(page, propertyName);
 | 
			
		||||
 | 
			
		||||
        await deleteCreatedProperty(page, propertyName);
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  test.describe('Add update and delete DateTime custom properties', () => {
 | 
			
		||||
    Object.values(CUSTOM_PROPERTIES_ENTITIES).forEach(async (entity) => {
 | 
			
		||||
      const propertyName = `pwcustomproperty${entity.name}test${uuid()}`;
 | 
			
		||||
 | 
			
		||||
      test(`Add DateTime custom property for ${entity.name}`, async ({
 | 
			
		||||
        page,
 | 
			
		||||
      }) => {
 | 
			
		||||
        test.slow(true);
 | 
			
		||||
 | 
			
		||||
        await settingClick(page, entity.entityApiType, true);
 | 
			
		||||
 | 
			
		||||
        await addCustomPropertiesForEntity({
 | 
			
		||||
          page,
 | 
			
		||||
          propertyName,
 | 
			
		||||
          customPropertyData: entity,
 | 
			
		||||
          customType: 'Date Time',
 | 
			
		||||
          formatConfig: entity.dateTimeFormatConfig,
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        await editCreatedProperty(page, propertyName);
 | 
			
		||||
 | 
			
		||||
        await deleteCreatedProperty(page, propertyName);
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
@ -11,12 +11,17 @@
 | 
			
		||||
 *  limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
import { APIRequestContext, expect, Page } from '@playwright/test';
 | 
			
		||||
import {
 | 
			
		||||
  CUSTOM_PROPERTY_INVALID_NAMES,
 | 
			
		||||
  CUSTOM_PROPERTY_NAME_VALIDATION_ERROR,
 | 
			
		||||
  ENTITY_REFERENCE_PROPERTIES,
 | 
			
		||||
} from '../constant/customProperty';
 | 
			
		||||
import {
 | 
			
		||||
  EntityTypeEndpoint,
 | 
			
		||||
  ENTITY_PATH,
 | 
			
		||||
} from '../support/entity/Entity.interface';
 | 
			
		||||
import { UserClass } from '../support/user/UserClass';
 | 
			
		||||
import { uuid } from './common';
 | 
			
		||||
import { descriptionBox, uuid } from './common';
 | 
			
		||||
 | 
			
		||||
export enum CustomPropertyType {
 | 
			
		||||
  STRING = 'String',
 | 
			
		||||
@ -35,6 +40,10 @@ export enum CustomPropertyTypeByName {
 | 
			
		||||
  TIMESTAMP = 'timestamp',
 | 
			
		||||
  ENTITY_REFERENCE = 'entityReference',
 | 
			
		||||
  ENTITY_REFERENCE_LIST = 'entityReferenceList',
 | 
			
		||||
  TIME_INTERVAL = 'timeInterval',
 | 
			
		||||
  TIME_CP = 'time-cp',
 | 
			
		||||
  DATE_CP = 'date-cp',
 | 
			
		||||
  DATE_TIME_CP = 'dateTime-cp',
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface CustomProperty {
 | 
			
		||||
@ -132,6 +141,31 @@ export const setValueForProperty = async (data: {
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    case 'time-cp': {
 | 
			
		||||
      await page.locator('[data-testid="time-picker"]').isVisible();
 | 
			
		||||
      await page.locator('[data-testid="time-picker"]').click();
 | 
			
		||||
      await page.locator('[data-testid="time-picker"]').fill(value);
 | 
			
		||||
      await page.getByRole('button', { name: 'OK', exact: true }).click();
 | 
			
		||||
      await page.locator('[data-testid="inline-save-btn"]').click();
 | 
			
		||||
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    case 'date-cp':
 | 
			
		||||
    case 'dateTime-cp': {
 | 
			
		||||
      await page.locator('[data-testid="date-time-picker"]').isVisible();
 | 
			
		||||
      await page.locator('[data-testid="date-time-picker"]').click();
 | 
			
		||||
      await page.locator('[data-testid="date-time-picker"]').fill(value);
 | 
			
		||||
      if (propertyType === 'dateTime-cp') {
 | 
			
		||||
        await page.getByText('Now', { exact: true }).click();
 | 
			
		||||
      } else {
 | 
			
		||||
        await page.getByText('Today', { exact: true }).click();
 | 
			
		||||
      }
 | 
			
		||||
      await page.locator('[data-testid="inline-save-btn"]').click();
 | 
			
		||||
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    case 'string':
 | 
			
		||||
    case 'integer':
 | 
			
		||||
    case 'number':
 | 
			
		||||
@ -191,7 +225,12 @@ export const validateValueForProperty = async (data: {
 | 
			
		||||
      page.getByLabel('Custom Properties').locator('.CodeMirror-scroll')
 | 
			
		||||
    ).toContainText(value);
 | 
			
		||||
  } else if (
 | 
			
		||||
    !['entityReference', 'entityReferenceList'].includes(propertyType)
 | 
			
		||||
    ![
 | 
			
		||||
      'entityReference',
 | 
			
		||||
      'entityReferenceList',
 | 
			
		||||
      'date-cp',
 | 
			
		||||
      'dateTime-cp',
 | 
			
		||||
    ].includes(propertyType)
 | 
			
		||||
  ) {
 | 
			
		||||
    await expect(page.getByRole('row', { name: propertyName })).toContainText(
 | 
			
		||||
      value.replace(/\*|_/gi, '')
 | 
			
		||||
@ -263,6 +302,30 @@ export const getPropertyValues = (
 | 
			
		||||
        newValue: users.user4,
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
    case 'timeInterval':
 | 
			
		||||
      return {
 | 
			
		||||
        value: '1710831125922,1710831125924',
 | 
			
		||||
        newValue: '1710831125924,1710831125922',
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
    case 'time-cp':
 | 
			
		||||
      return {
 | 
			
		||||
        value: '15:35:59',
 | 
			
		||||
        newValue: '17:35:59',
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
    case 'date-cp':
 | 
			
		||||
      return {
 | 
			
		||||
        value: '2024-07-09',
 | 
			
		||||
        newValue: '2025-07-09',
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
    case 'dateTime-cp':
 | 
			
		||||
      return {
 | 
			
		||||
        value: '2024-07-09 15:07:59',
 | 
			
		||||
        newValue: '2025-07-09 15:07:59',
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
    default:
 | 
			
		||||
      return {
 | 
			
		||||
        value: '',
 | 
			
		||||
@ -346,6 +409,30 @@ export const createCustomPropertyForEntity = async (
 | 
			
		||||
                },
 | 
			
		||||
              }
 | 
			
		||||
            : {}),
 | 
			
		||||
 | 
			
		||||
          ...(item.name === 'time-cp'
 | 
			
		||||
            ? {
 | 
			
		||||
                customPropertyConfig: {
 | 
			
		||||
                  config: 'HH:mm:ss',
 | 
			
		||||
                },
 | 
			
		||||
              }
 | 
			
		||||
            : {}),
 | 
			
		||||
 | 
			
		||||
          ...(item.name === 'date-cp'
 | 
			
		||||
            ? {
 | 
			
		||||
                customPropertyConfig: {
 | 
			
		||||
                  config: 'yyyy-MM-dd',
 | 
			
		||||
                },
 | 
			
		||||
              }
 | 
			
		||||
            : {}),
 | 
			
		||||
 | 
			
		||||
          ...(item.name === 'dateTime-cp'
 | 
			
		||||
            ? {
 | 
			
		||||
                customPropertyConfig: {
 | 
			
		||||
                  config: 'yyyy-MM-dd HH:mm:ss',
 | 
			
		||||
                },
 | 
			
		||||
              }
 | 
			
		||||
            : {}),
 | 
			
		||||
        },
 | 
			
		||||
      }
 | 
			
		||||
    );
 | 
			
		||||
@ -368,3 +455,229 @@ export const createCustomPropertyForEntity = async (
 | 
			
		||||
 | 
			
		||||
  return { customProperties, cleanupUser };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const addCustomPropertiesForEntity = async ({
 | 
			
		||||
  page,
 | 
			
		||||
  propertyName,
 | 
			
		||||
  customPropertyData,
 | 
			
		||||
  customType,
 | 
			
		||||
  enumConfig,
 | 
			
		||||
  formatConfig,
 | 
			
		||||
  entityReferenceConfig,
 | 
			
		||||
}: {
 | 
			
		||||
  page: Page;
 | 
			
		||||
  propertyName: string;
 | 
			
		||||
  customPropertyData: { description: string };
 | 
			
		||||
  customType: string;
 | 
			
		||||
  enumConfig?: { values: string[]; multiSelect: boolean };
 | 
			
		||||
  formatConfig?: string;
 | 
			
		||||
  entityReferenceConfig?: string[];
 | 
			
		||||
}) => {
 | 
			
		||||
  // Add Custom property for selected entity
 | 
			
		||||
  await page.click('[data-testid="add-field-button"]');
 | 
			
		||||
 | 
			
		||||
  // Trigger validation
 | 
			
		||||
  await page.click('[data-testid="create-button"]');
 | 
			
		||||
 | 
			
		||||
  await expect(page.locator('#name_help')).toContainText('Name is required');
 | 
			
		||||
  await expect(page.locator('#propertyType_help')).toContainText(
 | 
			
		||||
    'Property Type is required'
 | 
			
		||||
  );
 | 
			
		||||
  await expect(page.locator('#description_help')).toContainText(
 | 
			
		||||
    'Description is required'
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  // Validation checks
 | 
			
		||||
  await page.fill(
 | 
			
		||||
    '[data-testid="name"]',
 | 
			
		||||
    CUSTOM_PROPERTY_INVALID_NAMES.CAPITAL_CASE
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  await expect(page.locator('#name_help')).toContainText(
 | 
			
		||||
    CUSTOM_PROPERTY_NAME_VALIDATION_ERROR
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  await page.fill(
 | 
			
		||||
    '[data-testid="name"]',
 | 
			
		||||
    CUSTOM_PROPERTY_INVALID_NAMES.WITH_UNDERSCORE
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  await expect(page.locator('#name_help')).toContainText(
 | 
			
		||||
    CUSTOM_PROPERTY_NAME_VALIDATION_ERROR
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  await page.fill(
 | 
			
		||||
    '[data-testid="name"]',
 | 
			
		||||
    CUSTOM_PROPERTY_INVALID_NAMES.WITH_SPACE
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  await expect(page.locator('#name_help')).toContainText(
 | 
			
		||||
    CUSTOM_PROPERTY_NAME_VALIDATION_ERROR
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  await page.fill(
 | 
			
		||||
    '[data-testid="name"]',
 | 
			
		||||
    CUSTOM_PROPERTY_INVALID_NAMES.WITH_DOTS
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  await expect(page.locator('#name_help')).toContainText(
 | 
			
		||||
    CUSTOM_PROPERTY_NAME_VALIDATION_ERROR
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  // Name in another language
 | 
			
		||||
  await page.fill('[data-testid="name"]', '汝らヴェディア');
 | 
			
		||||
 | 
			
		||||
  await expect(page.locator('#name_help')).not.toBeVisible();
 | 
			
		||||
 | 
			
		||||
  // Correct name
 | 
			
		||||
  await page.fill('[data-testid="name"]', propertyName);
 | 
			
		||||
 | 
			
		||||
  // Select custom type
 | 
			
		||||
  await page.locator('[id="root\\/propertyType"]').fill(customType);
 | 
			
		||||
  await page.getByTitle(`${customType}`, { exact: true }).click();
 | 
			
		||||
 | 
			
		||||
  // Enum configuration
 | 
			
		||||
  if (customType === 'Enum' && enumConfig) {
 | 
			
		||||
    for (const val of enumConfig.values) {
 | 
			
		||||
      await page.fill('#root\\/enumConfig', `${val}{Enter}`);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (enumConfig.multiSelect) {
 | 
			
		||||
      await page.click('#root\\/multiSelect');
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Entity reference configuration
 | 
			
		||||
  if (
 | 
			
		||||
    ENTITY_REFERENCE_PROPERTIES.includes(customType) &&
 | 
			
		||||
    entityReferenceConfig
 | 
			
		||||
  ) {
 | 
			
		||||
    for (const val of entityReferenceConfig) {
 | 
			
		||||
      await page.click('#root\\/entityReferenceConfig');
 | 
			
		||||
      await page.fill('#root\\/entityReferenceConfig', val);
 | 
			
		||||
      await page.click(`[title="${val}"]`);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Format configuration
 | 
			
		||||
  if (['Date', 'Date Time', 'Time'].includes(customType)) {
 | 
			
		||||
    await page.fill('#root\\/formatConfig', 'invalid-format');
 | 
			
		||||
 | 
			
		||||
    await expect(page.locator('#formatConfig_help')).toContainText(
 | 
			
		||||
      'Format is invalid'
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    if (formatConfig) {
 | 
			
		||||
      await page.fill('#root\\/formatConfig', formatConfig);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Description
 | 
			
		||||
  await page.fill(descriptionBox, customPropertyData.description);
 | 
			
		||||
 | 
			
		||||
  const createPropertyPromise = page.waitForResponse(
 | 
			
		||||
    '/api/v1/metadata/types/name/*?fields=customProperties'
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  await page.click('[data-testid="create-button"]');
 | 
			
		||||
 | 
			
		||||
  const response = await createPropertyPromise;
 | 
			
		||||
 | 
			
		||||
  expect(response.status()).toBe(200);
 | 
			
		||||
 | 
			
		||||
  await expect(
 | 
			
		||||
    page.getByRole('row', { name: new RegExp(propertyName, 'i') })
 | 
			
		||||
  ).toBeVisible();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const editCreatedProperty = async (
 | 
			
		||||
  page: Page,
 | 
			
		||||
  propertyName: string,
 | 
			
		||||
  type?: string
 | 
			
		||||
) => {
 | 
			
		||||
  // Fetching for edit button
 | 
			
		||||
  const editButton = page.locator(
 | 
			
		||||
    `[data-row-key="${propertyName}"] [data-testid="edit-button"]`
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  if (type === 'Enum') {
 | 
			
		||||
    await expect(
 | 
			
		||||
      page.locator(
 | 
			
		||||
        `[data-row-key="${propertyName}"] [data-testid="enum-config"]`
 | 
			
		||||
      )
 | 
			
		||||
    ).toContainText('["enum1","enum2","enum3"]');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  await editButton.click();
 | 
			
		||||
 | 
			
		||||
  await page.locator(descriptionBox).fill('');
 | 
			
		||||
  await page.locator(descriptionBox).fill('This is new description');
 | 
			
		||||
 | 
			
		||||
  if (type === 'Enum') {
 | 
			
		||||
    await page
 | 
			
		||||
      .locator('#root\\/customPropertyConfig')
 | 
			
		||||
      .fill(`updatedValue{Enter}`);
 | 
			
		||||
    await page.click('body'); // Equivalent to clicking outside
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (ENTITY_REFERENCE_PROPERTIES.includes(type ?? '')) {
 | 
			
		||||
    await page.locator('#root\\/customPropertyConfig').click();
 | 
			
		||||
    await page.locator('#root\\/customPropertyConfig').fill(`Table{Enter}`);
 | 
			
		||||
    await page.click('body'); // Equivalent to clicking outside
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const patchRequest = page.waitForResponse('/api/v1/metadata/types/*');
 | 
			
		||||
 | 
			
		||||
  await page.locator('button[type="submit"]').click();
 | 
			
		||||
 | 
			
		||||
  const response = await patchRequest;
 | 
			
		||||
 | 
			
		||||
  expect(response.status()).toBe(200);
 | 
			
		||||
 | 
			
		||||
  await expect(page.locator('.ant-modal-wrap')).not.toBeVisible();
 | 
			
		||||
 | 
			
		||||
  // Fetching for updated descriptions for the created custom property
 | 
			
		||||
  await expect(
 | 
			
		||||
    page.locator(
 | 
			
		||||
      `[data-row-key="${propertyName}"] [data-testid="viewer-container"]`
 | 
			
		||||
    )
 | 
			
		||||
  ).toContainText('This is new description');
 | 
			
		||||
 | 
			
		||||
  if (type === 'Enum') {
 | 
			
		||||
    await expect(
 | 
			
		||||
      page.locator(
 | 
			
		||||
        `[data-row-key="${propertyName}"] [data-testid="enum-config"]`
 | 
			
		||||
      )
 | 
			
		||||
    ).toContainText('["enum1","enum2","enum3","updatedValue"]');
 | 
			
		||||
  }
 | 
			
		||||
  if (ENTITY_REFERENCE_PROPERTIES.includes(type ?? '')) {
 | 
			
		||||
    await expect(
 | 
			
		||||
      page.locator(
 | 
			
		||||
        `[data-row-key="${propertyName}"] [data-testid="${propertyName}-config"]`
 | 
			
		||||
      )
 | 
			
		||||
    ).toContainText('["user","team","table"]');
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const deleteCreatedProperty = async (
 | 
			
		||||
  page: Page,
 | 
			
		||||
  propertyName: string
 | 
			
		||||
) => {
 | 
			
		||||
  // Fetching for delete button
 | 
			
		||||
  await page
 | 
			
		||||
    .locator(`[data-row-key="${propertyName}"]`)
 | 
			
		||||
    .scrollIntoViewIfNeeded();
 | 
			
		||||
  await page
 | 
			
		||||
    .locator(`[data-row-key="${propertyName}"] [data-testid="delete-button"]`)
 | 
			
		||||
    .click();
 | 
			
		||||
 | 
			
		||||
  // Checking property name is present on the delete pop-up
 | 
			
		||||
  await expect(page.locator('[data-testid="body-text"]')).toContainText(
 | 
			
		||||
    propertyName
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  // Ensure the save button is visible before clicking
 | 
			
		||||
  await expect(page.locator('[data-testid="save-button"]')).toBeVisible();
 | 
			
		||||
 | 
			
		||||
  await page.locator('[data-testid="save-button"]').click();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -35,9 +35,35 @@ $$
 | 
			
		||||
$$section
 | 
			
		||||
### Format $(id="formatConfig")
 | 
			
		||||
 | 
			
		||||
To specify a format for the `date` or `dateTime` type, example you can use the following pattern: `dd-mm-yyyy` or `dd-mm-yyyy HH:mm:ss`.
 | 
			
		||||
To specify a format for the `date` or `dateTime` type, example you can use the following pattern: `dd-MM-yyyy` or `dd-MM-yyyy HH:mm:ss`.
 | 
			
		||||
$$
 | 
			
		||||
 | 
			
		||||
**Supported Date formats**
 | 
			
		||||
 | 
			
		||||
- `yyyy-MM-dd`
 | 
			
		||||
- `dd MMM yyyy`
 | 
			
		||||
- `MM/dd/yyyy`
 | 
			
		||||
- `dd/MM/yyyy`
 | 
			
		||||
- `dd-MM-yyyy`
 | 
			
		||||
- `yyyyDDD`
 | 
			
		||||
- `d MMMM yyyy`
 | 
			
		||||
 | 
			
		||||
**Supported DateTime formats**
 | 
			
		||||
 | 
			
		||||
- `MMM dd HH:mm:ss yyyy`
 | 
			
		||||
- `yyyy-MM-dd HH:mm:ss`
 | 
			
		||||
- `MM/dd/yyyy HH:mm:ss`
 | 
			
		||||
- `dd/MM/yyyy HH:mm:ss`
 | 
			
		||||
- `dd-MM-yyyy HH:mm:ss`
 | 
			
		||||
- `yyyy-MM-dd HH:mm:ss.SSS`
 | 
			
		||||
- `yyyy-MM-dd HH:mm:ss.SSSSSS`
 | 
			
		||||
- `dd MMMM yyyy HH:mm:ss`
 | 
			
		||||
 | 
			
		||||
**Supported Time formats**
 | 
			
		||||
 | 
			
		||||
- `HH:mm:ss`
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
$$section
 | 
			
		||||
### Entity Reference type $(id="entityReferenceConfig")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -21,9 +21,9 @@ const mockGoBack = jest.fn();
 | 
			
		||||
const mockPropertyTypes = [
 | 
			
		||||
  {
 | 
			
		||||
    id: '153a0c07-6480-404e-990b-555a42c8a7b5',
 | 
			
		||||
    name: 'date',
 | 
			
		||||
    fullyQualifiedName: 'date',
 | 
			
		||||
    displayName: 'date',
 | 
			
		||||
    name: 'date-cp',
 | 
			
		||||
    fullyQualifiedName: 'date-cp',
 | 
			
		||||
    displayName: 'date-cp',
 | 
			
		||||
    description: '"Date in ISO 8601 format in UTC. Example - \'2018-11-13\'."',
 | 
			
		||||
    category: 'field',
 | 
			
		||||
    nameSpace: 'basic',
 | 
			
		||||
@ -35,9 +35,9 @@ const mockPropertyTypes = [
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    id: '6ce245d8-80c0-4641-9b60-32cf03ca79a2',
 | 
			
		||||
    name: 'dateTime',
 | 
			
		||||
    fullyQualifiedName: 'dateTime',
 | 
			
		||||
    displayName: 'dateTime',
 | 
			
		||||
    name: 'dateTime-cp',
 | 
			
		||||
    fullyQualifiedName: 'dateTime-cp',
 | 
			
		||||
    displayName: 'dateTime-cp',
 | 
			
		||||
    description:
 | 
			
		||||
      '"Date and time in ISO 8601 format. Example - \'2018-11-13T20:20:39+00:00\'."',
 | 
			
		||||
    category: 'field',
 | 
			
		||||
 | 
			
		||||
@ -14,7 +14,7 @@
 | 
			
		||||
import { Button, Col, Form, Row } from 'antd';
 | 
			
		||||
import { AxiosError } from 'axios';
 | 
			
		||||
import { t } from 'i18next';
 | 
			
		||||
import { isUndefined, omit, omitBy, startCase } from 'lodash';
 | 
			
		||||
import { isUndefined, map, omit, omitBy, startCase } from 'lodash';
 | 
			
		||||
import React, {
 | 
			
		||||
  FocusEvent,
 | 
			
		||||
  useCallback,
 | 
			
		||||
@ -24,10 +24,10 @@ import React, {
 | 
			
		||||
} from 'react';
 | 
			
		||||
import { useHistory, useParams } from 'react-router-dom';
 | 
			
		||||
import {
 | 
			
		||||
  DISABLED_PROPERTY_TYPES,
 | 
			
		||||
  ENTITY_REFERENCE_OPTIONS,
 | 
			
		||||
  PROPERTY_TYPES_WITH_ENTITY_REFERENCE,
 | 
			
		||||
  PROPERTY_TYPES_WITH_FORMAT,
 | 
			
		||||
  SUPPORTED_FORMAT_MAP,
 | 
			
		||||
} from '../../../../constants/CustomProperty.constants';
 | 
			
		||||
import { GlobalSettingsMenuCategory } from '../../../../constants/GlobalSettings.constants';
 | 
			
		||||
import { CUSTOM_PROPERTY_NAME_REGEX } from '../../../../constants/regex.constants';
 | 
			
		||||
@ -49,7 +49,6 @@ import {
 | 
			
		||||
  getTypeByFQN,
 | 
			
		||||
  getTypeListByCategory,
 | 
			
		||||
} from '../../../../rest/metadataTypeAPI';
 | 
			
		||||
import { isValidDateFormat } from '../../../../utils/date-time/DateTimeUtils';
 | 
			
		||||
import { generateFormFields } from '../../../../utils/formUtils';
 | 
			
		||||
import { getSettingOptionByEntityType } from '../../../../utils/GlobalSettingsUtils';
 | 
			
		||||
import { getSettingPath } from '../../../../utils/RouterUtils';
 | 
			
		||||
@ -58,8 +57,6 @@ import ResizablePanels from '../../../common/ResizablePanels/ResizablePanels';
 | 
			
		||||
import ServiceDocPanel from '../../../common/ServiceDocPanel/ServiceDocPanel';
 | 
			
		||||
import TitleBreadcrumb from '../../../common/TitleBreadcrumb/TitleBreadcrumb.component';
 | 
			
		||||
 | 
			
		||||
type PropertyType = { key: string; label: string; value: string | undefined };
 | 
			
		||||
 | 
			
		||||
const AddCustomProperty = () => {
 | 
			
		||||
  const [form] = Form.useForm();
 | 
			
		||||
  const { entityType } = useParams<{ entityType: EntityType }>();
 | 
			
		||||
@ -97,43 +94,40 @@ const AddCustomProperty = () => {
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const propertyTypeOptions = useMemo(() => {
 | 
			
		||||
    return propertyTypes.reduce((acc: PropertyType[], type) => {
 | 
			
		||||
      if (DISABLED_PROPERTY_TYPES.includes(type.name)) {
 | 
			
		||||
        return acc;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return [
 | 
			
		||||
        ...acc,
 | 
			
		||||
        {
 | 
			
		||||
          key: type.name,
 | 
			
		||||
          label: startCase(type.displayName ?? type.name),
 | 
			
		||||
          value: type.id,
 | 
			
		||||
        },
 | 
			
		||||
      ];
 | 
			
		||||
    }, []);
 | 
			
		||||
    return map(propertyTypes, (type) => ({
 | 
			
		||||
      key: type.name,
 | 
			
		||||
      // Remove -cp from the name and convert to start case
 | 
			
		||||
      label: startCase((type.displayName ?? type.name).replace(/-cp/g, '')),
 | 
			
		||||
      value: type.id,
 | 
			
		||||
    }));
 | 
			
		||||
  }, [propertyTypes]);
 | 
			
		||||
 | 
			
		||||
  const { hasEnumConfig, hasFormatConfig, hasEntityReferenceConfig } =
 | 
			
		||||
    useMemo(() => {
 | 
			
		||||
      const watchedOption = propertyTypeOptions.find(
 | 
			
		||||
        (option) => option.value === watchedPropertyType
 | 
			
		||||
      );
 | 
			
		||||
      const watchedOptionKey = watchedOption?.key ?? '';
 | 
			
		||||
  const {
 | 
			
		||||
    hasEnumConfig,
 | 
			
		||||
    hasFormatConfig,
 | 
			
		||||
    hasEntityReferenceConfig,
 | 
			
		||||
    watchedOption,
 | 
			
		||||
  } = useMemo(() => {
 | 
			
		||||
    const watchedOption = propertyTypeOptions.find(
 | 
			
		||||
      (option) => option.value === watchedPropertyType
 | 
			
		||||
    );
 | 
			
		||||
    const watchedOptionKey = watchedOption?.key ?? '';
 | 
			
		||||
 | 
			
		||||
      const hasEnumConfig = watchedOptionKey === 'enum';
 | 
			
		||||
    const hasEnumConfig = watchedOptionKey === 'enum';
 | 
			
		||||
 | 
			
		||||
      const hasFormatConfig =
 | 
			
		||||
        PROPERTY_TYPES_WITH_FORMAT.includes(watchedOptionKey);
 | 
			
		||||
    const hasFormatConfig =
 | 
			
		||||
      PROPERTY_TYPES_WITH_FORMAT.includes(watchedOptionKey);
 | 
			
		||||
 | 
			
		||||
      const hasEntityReferenceConfig =
 | 
			
		||||
        PROPERTY_TYPES_WITH_ENTITY_REFERENCE.includes(watchedOptionKey);
 | 
			
		||||
    const hasEntityReferenceConfig =
 | 
			
		||||
      PROPERTY_TYPES_WITH_ENTITY_REFERENCE.includes(watchedOptionKey);
 | 
			
		||||
 | 
			
		||||
      return {
 | 
			
		||||
        hasEnumConfig,
 | 
			
		||||
        hasFormatConfig,
 | 
			
		||||
        hasEntityReferenceConfig,
 | 
			
		||||
      };
 | 
			
		||||
    }, [watchedPropertyType, propertyTypeOptions]);
 | 
			
		||||
    return {
 | 
			
		||||
      hasEnumConfig,
 | 
			
		||||
      hasFormatConfig,
 | 
			
		||||
      hasEntityReferenceConfig,
 | 
			
		||||
      watchedOption,
 | 
			
		||||
    };
 | 
			
		||||
  }, [watchedPropertyType, propertyTypeOptions]);
 | 
			
		||||
 | 
			
		||||
  const fetchPropertyType = async () => {
 | 
			
		||||
    try {
 | 
			
		||||
@ -337,7 +331,13 @@ const AddCustomProperty = () => {
 | 
			
		||||
    rules: [
 | 
			
		||||
      {
 | 
			
		||||
        validator: (_, value) => {
 | 
			
		||||
          if (!isValidDateFormat(value)) {
 | 
			
		||||
          const propertyName = watchedOption?.key ?? '';
 | 
			
		||||
          const supportedFormats =
 | 
			
		||||
            SUPPORTED_FORMAT_MAP[
 | 
			
		||||
              propertyName as keyof typeof SUPPORTED_FORMAT_MAP
 | 
			
		||||
            ];
 | 
			
		||||
 | 
			
		||||
          if (!supportedFormats.includes(value)) {
 | 
			
		||||
            return Promise.reject(
 | 
			
		||||
              t('label.field-invalid', {
 | 
			
		||||
                field: t('label.format'),
 | 
			
		||||
 | 
			
		||||
@ -160,7 +160,7 @@ describe('Test PropertyValue Component', () => {
 | 
			
		||||
    const extension = { yNumber: '20-03-2024' };
 | 
			
		||||
    const propertyType = {
 | 
			
		||||
      ...mockData.property.propertyType,
 | 
			
		||||
      name: 'date',
 | 
			
		||||
      name: 'date-cp',
 | 
			
		||||
    };
 | 
			
		||||
    render(
 | 
			
		||||
      <PropertyValue
 | 
			
		||||
@ -187,7 +187,7 @@ describe('Test PropertyValue Component', () => {
 | 
			
		||||
    };
 | 
			
		||||
    const propertyType = {
 | 
			
		||||
      ...mockData.property.propertyType,
 | 
			
		||||
      name: 'dateTime',
 | 
			
		||||
      name: 'dateTime-cp',
 | 
			
		||||
    };
 | 
			
		||||
    render(
 | 
			
		||||
      <PropertyValue
 | 
			
		||||
@ -216,7 +216,7 @@ describe('Test PropertyValue Component', () => {
 | 
			
		||||
    };
 | 
			
		||||
    const propertyType = {
 | 
			
		||||
      ...mockData.property.propertyType,
 | 
			
		||||
      name: 'time',
 | 
			
		||||
      name: 'time-cp',
 | 
			
		||||
    };
 | 
			
		||||
    render(
 | 
			
		||||
      <PropertyValue
 | 
			
		||||
 | 
			
		||||
@ -225,12 +225,12 @@ export const PropertyValue: FC<PropertyValueProps> = ({
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case 'date':
 | 
			
		||||
      case 'dateTime': {
 | 
			
		||||
      case 'date-cp':
 | 
			
		||||
      case 'dateTime-cp': {
 | 
			
		||||
        // Default format is 'yyyy-mm-dd'
 | 
			
		||||
        const format =
 | 
			
		||||
          toUpper(property.customPropertyConfig?.config as string) ??
 | 
			
		||||
          'yyyy-mm-dd';
 | 
			
		||||
        const format = toUpper(
 | 
			
		||||
          (property.customPropertyConfig?.config as string) ?? 'yyyy-mm-dd'
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        const initialValues = {
 | 
			
		||||
          dateTimeValue: value ? moment(value, format) : undefined,
 | 
			
		||||
@ -263,7 +263,7 @@ export const PropertyValue: FC<PropertyValueProps> = ({
 | 
			
		||||
                  data-testid="date-time-picker"
 | 
			
		||||
                  disabled={isLoading}
 | 
			
		||||
                  format={format}
 | 
			
		||||
                  showTime={propertyType.name === 'dateTime'}
 | 
			
		||||
                  showTime={propertyType.name === 'dateTime-cp'}
 | 
			
		||||
                  style={{ width: '250px' }}
 | 
			
		||||
                />
 | 
			
		||||
              </Form.Item>
 | 
			
		||||
@ -272,8 +272,9 @@ export const PropertyValue: FC<PropertyValueProps> = ({
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      case 'time': {
 | 
			
		||||
        const format = 'HH:mm:ss';
 | 
			
		||||
      case 'time-cp': {
 | 
			
		||||
        const format =
 | 
			
		||||
          (property.customPropertyConfig?.config as string) ?? 'HH:mm:ss';
 | 
			
		||||
        const initialValues = {
 | 
			
		||||
          time: value ? moment(value, format) : undefined,
 | 
			
		||||
        };
 | 
			
		||||
@ -303,6 +304,7 @@ export const PropertyValue: FC<PropertyValueProps> = ({
 | 
			
		||||
                  allowClear
 | 
			
		||||
                  data-testid="time-picker"
 | 
			
		||||
                  disabled={isLoading}
 | 
			
		||||
                  format={format}
 | 
			
		||||
                  style={{ width: '250px' }}
 | 
			
		||||
                />
 | 
			
		||||
              </Form.Item>
 | 
			
		||||
@ -788,9 +790,9 @@ export const PropertyValue: FC<PropertyValueProps> = ({
 | 
			
		||||
      case 'string':
 | 
			
		||||
      case 'integer':
 | 
			
		||||
      case 'number':
 | 
			
		||||
      case 'date':
 | 
			
		||||
      case 'dateTime':
 | 
			
		||||
      case 'time':
 | 
			
		||||
      case 'date-cp':
 | 
			
		||||
      case 'dateTime-cp':
 | 
			
		||||
      case 'time-cp':
 | 
			
		||||
      case 'email':
 | 
			
		||||
      case 'timestamp':
 | 
			
		||||
      case 'duration':
 | 
			
		||||
 | 
			
		||||
@ -10,14 +10,7 @@
 | 
			
		||||
 *  See the License for the specific language governing permissions and
 | 
			
		||||
 *  limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
export const PROPERTY_TYPES_WITH_FORMAT = ['date', 'dateTime'];
 | 
			
		||||
 | 
			
		||||
export const DISABLED_PROPERTY_TYPES = [
 | 
			
		||||
  'time',
 | 
			
		||||
  'dateTime',
 | 
			
		||||
  'timeInterval',
 | 
			
		||||
  'date',
 | 
			
		||||
];
 | 
			
		||||
export const PROPERTY_TYPES_WITH_FORMAT = ['date-cp', 'dateTime-cp', 'time-cp'];
 | 
			
		||||
 | 
			
		||||
export const PROPERTY_TYPES_WITH_ENTITY_REFERENCE = [
 | 
			
		||||
  'entityReference',
 | 
			
		||||
@ -101,3 +94,35 @@ export const ENTITY_REFERENCE_OPTIONS = [
 | 
			
		||||
    label: 'Team',
 | 
			
		||||
  },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
// supported date formats on backend
 | 
			
		||||
export const SUPPORTED_DATE_FORMATS = [
 | 
			
		||||
  'yyyy-MM-dd',
 | 
			
		||||
  'dd MMM yyyy',
 | 
			
		||||
  'MM/dd/yyyy',
 | 
			
		||||
  'dd/MM/yyyy',
 | 
			
		||||
  'dd-MM-yyyy',
 | 
			
		||||
  'yyyyDDD',
 | 
			
		||||
  'd MMMM yyyy',
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
// supported date time formats on backend
 | 
			
		||||
export const SUPPORTED_DATE_TIME_FORMATS = [
 | 
			
		||||
  'MMM dd HH:mm:ss yyyy',
 | 
			
		||||
  'yyyy-MM-dd HH:mm:ss',
 | 
			
		||||
  'MM/dd/yyyy HH:mm:ss',
 | 
			
		||||
  'dd/MM/yyyy HH:mm:ss',
 | 
			
		||||
  'dd-MM-yyyy HH:mm:ss',
 | 
			
		||||
  'yyyy-MM-dd HH:mm:ss.SSS',
 | 
			
		||||
  'yyyy-MM-dd HH:mm:ss.SSSSSS',
 | 
			
		||||
  'dd MMMM yyyy HH:mm:ss',
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
// supported time formats on backend
 | 
			
		||||
export const SUPPORTED_TIME_FORMATS = ['HH:mm:ss'];
 | 
			
		||||
 | 
			
		||||
export const SUPPORTED_FORMAT_MAP = {
 | 
			
		||||
  'date-cp': SUPPORTED_DATE_FORMATS,
 | 
			
		||||
  'dateTime-cp': SUPPORTED_DATE_TIME_FORMATS,
 | 
			
		||||
  'time-cp': SUPPORTED_TIME_FORMATS,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user