From 859c9f0bed4a679b4c9f4e90d710b933a59fa29a Mon Sep 17 00:00:00 2001
From: pkupczyk <piotr.kupczyk@id.ethz.ch>
Date: Sun, 22 Dec 2019 15:44:29 +0100
Subject: [PATCH] SSDM-7583 : ObjectTypeForm - improve 'save' logic (e.g. allow
 usage of legacy/common property types and turn them into type specific
 property types only if needed)

---
 .../types/objectType/ObjectTypeFacade.js      |  13 -
 .../types/objectType/ObjectTypeHandlerLoad.js |   9 +-
 .../types/objectType/ObjectTypeHandlerSave.js | 341 +++++++++---------
 .../ObjectTypeParametersProperty.jsx          |  40 +-
 .../objectType/ObjectTypeParametersType.jsx   |   4 +-
 ...UsageWarning.jsx => ObjectTypeWarning.jsx} |  11 +-
 .../objectType/ObjectTypeWarningLegacy.jsx    |  17 +
 .../objectType/ObjectTypeWarningUsage.jsx     |  20 +
 8 files changed, 258 insertions(+), 197 deletions(-)
 rename openbis_ng_ui/src/js/components/types/objectType/{ObjectTypeUsageWarning.jsx => ObjectTypeWarning.jsx} (63%)
 create mode 100644 openbis_ng_ui/src/js/components/types/objectType/ObjectTypeWarningLegacy.jsx
 create mode 100644 openbis_ng_ui/src/js/components/types/objectType/ObjectTypeWarningUsage.jsx

diff --git a/openbis_ng_ui/src/js/components/types/objectType/ObjectTypeFacade.js b/openbis_ng_ui/src/js/components/types/objectType/ObjectTypeFacade.js
index 21062db9eaa..e23dbd7b452 100644
--- a/openbis_ng_ui/src/js/components/types/objectType/ObjectTypeFacade.js
+++ b/openbis_ng_ui/src/js/components/types/objectType/ObjectTypeFacade.js
@@ -21,19 +21,6 @@ export default class ObjectTypeFacade {
     })
   }
 
-  loadPropertyTypes(typeId) {
-    const criteria = new dto.PropertyTypeSearchCriteria()
-    criteria.withCode().thatStartsWith(typeId + '.')
-
-    const fo = new dto.PropertyTypeFetchOptions()
-    fo.withVocabulary()
-    fo.withMaterialType()
-
-    return facade.searchPropertyTypes(criteria, fo).then(result => {
-      return result.objects
-    })
-  }
-
   loadUsages(typeId) {
     function createTypeUsedOperation(typeId) {
       const criteria = new dto.SampleSearchCriteria()
diff --git a/openbis_ng_ui/src/js/components/types/objectType/ObjectTypeHandlerLoad.js b/openbis_ng_ui/src/js/components/types/objectType/ObjectTypeHandlerLoad.js
index ea17fd2ac74..e6b3954679d 100644
--- a/openbis_ng_ui/src/js/components/types/objectType/ObjectTypeHandlerLoad.js
+++ b/openbis_ng_ui/src/js/components/types/objectType/ObjectTypeHandlerLoad.js
@@ -33,10 +33,6 @@ export default class ObjectTypeHandlerLoad {
           usages: loadedUsages.type
         }
 
-        type.original = {
-          ...type
-        }
-
         const sections = []
         const properties = []
         let currentSection = null
@@ -86,6 +82,11 @@ export default class ObjectTypeHandlerLoad {
           properties.push(currentProperty)
         })
 
+        type.original = {
+          ...type,
+          properties
+        }
+
         this.setState(() => ({
           type,
           properties,
diff --git a/openbis_ng_ui/src/js/components/types/objectType/ObjectTypeHandlerSave.js b/openbis_ng_ui/src/js/components/types/objectType/ObjectTypeHandlerSave.js
index 5fccc4714f9..efd7d1c416f 100644
--- a/openbis_ng_ui/src/js/components/types/objectType/ObjectTypeHandlerSave.js
+++ b/openbis_ng_ui/src/js/components/types/objectType/ObjectTypeHandlerSave.js
@@ -44,8 +44,6 @@ export default class ObjectTypeHandlerSave {
   }
 
   prepareProperties(type, properties, sections) {
-    const propertyCodePrefix = type.code + '.'
-
     const propertiesMap = properties.reduce((map, property) => {
       map[property.id] = property
       return map
@@ -59,10 +57,6 @@ export default class ObjectTypeHandlerSave {
         let code = property.code
         if (code) {
           code = code.toUpperCase()
-
-          if (!code.startsWith(propertyCodePrefix)) {
-            code = propertyCodePrefix + code
-          }
         }
 
         const newProperty = _.mapValues(
@@ -81,162 +75,204 @@ export default class ObjectTypeHandlerSave {
     return results
   }
 
-  preparePropertyChanges(loadedPropertyTypes) {
-    const toDelete = {}
-    const toCreate = {}
-    const toUpdate = {}
-
-    function isUpdatePossible(type, property) {
-      const typeVocabulary = type.vocabulary ? type.vocabulary.code : null
-      const typeMaterialType = type.materialType ? type.materialType.code : null
-      const originalPlugin = property.original ? property.original.plugin : null
-      return (
-        type.dataType === property.dataType &&
-        typeVocabulary === property.vocabulary &&
-        typeMaterialType === property.materialType &&
-        originalPlugin === property.plugin
-      )
+  getTypePrefix() {
+    return this.type.code + '.'
+  }
+
+  hasTypePrefix(property) {
+    return property.code && property.code.startsWith(this.getTypePrefix())
+  }
+
+  addTypePrefix(property) {
+    if (property.code && !this.hasTypePrefix(property)) {
+      return {
+        ...property,
+        code: this.getTypePrefix() + property.code
+      }
+    } else {
+      return property
     }
+  }
 
-    const propertiesMap = this.properties.reduce((map, property) => {
-      map[property.code] = property
-      return map
-    }, {})
+  hasPropertyChanged(property, path) {
+    const originalValue = property.original
+      ? _.get(property.original, path)
+      : null
+    const currentValue = _.get(property, path)
+    return originalValue !== currentValue
+  }
 
-    const loadedPropertyTypesMap = loadedPropertyTypes.reduce(
-      (map, loadedProperty) => {
-        map[loadedProperty.code] = loadedProperty
-        return map
-      },
-      {}
+  isPropertyTypeUpdateNeeded(property) {
+    return (
+      this.hasPropertyChanged(property, 'dataType') ||
+      this.hasPropertyChanged(property, 'vocabulary') ||
+      this.hasPropertyChanged(property, 'materialType') ||
+      this.hasPropertyChanged(property, 'plugin') ||
+      this.hasPropertyChanged(property, 'label') ||
+      this.hasPropertyChanged(property, 'description') ||
+      this.hasPropertyChanged(property, 'schema') ||
+      this.hasPropertyChanged(property, 'transformation')
     )
+  }
 
-    this.properties.forEach(property => {
-      const loadedProperty = loadedPropertyTypesMap[property.code]
-      if (!loadedProperty) {
-        toCreate[property.code] = property
-      }
-    })
+  isPropertyTypeUpdatePossible(property) {
+    if (
+      this.hasPropertyChanged(property, 'dataType') ||
+      this.hasPropertyChanged(property, 'vocabulary') ||
+      this.hasPropertyChanged(property, 'materialType') ||
+      this.hasPropertyChanged(property, 'plugin')
+    ) {
+      return false
+    }
+    return true
+  }
 
-    loadedPropertyTypes.forEach(loadedPropertyType => {
-      const property = propertiesMap[loadedPropertyType.code]
+  createOperations() {
+    const operations = []
+    const assignments = []
+
+    if (this.type.original) {
+      this.type.original.properties.forEach(originalProperty => {
+        const property = _.find(this.properties, ['id', originalProperty.id])
+        if (!property) {
+          if (this.hasTypePrefix(originalProperty)) {
+            operations.push(
+              this.deletePropertyAssignmentOperation(originalProperty)
+            )
+            operations.push(this.deletePropertyTypeOperation(originalProperty))
+          } else {
+            operations.push(
+              this.deletePropertyAssignmentOperation(originalProperty)
+            )
+          }
+        }
+      })
+    }
 
-      if (property) {
-        if (isUpdatePossible(loadedPropertyType, property)) {
-          toUpdate[property.code] = property
+    this.properties.forEach((property, index) => {
+      if (property.original) {
+        if (this.hasTypePrefix(property)) {
+          if (this.isPropertyTypeUpdateNeeded(property)) {
+            if (this.isPropertyTypeUpdatePossible(property)) {
+              operations.push(this.updatePropertyTypeOperation(property))
+            } else {
+              operations.push(this.deletePropertyAssignmentOperation(property))
+              operations.push(this.deletePropertyTypeOperation(property))
+              operations.push(this.createPropertyTypeOperation(property))
+            }
+          }
+          assignments.push(this.propertyAssignmentCreation(property, index))
         } else {
-          toDelete[property.code] = property.code
-          toCreate[property.code] = property
+          if (this.isPropertyTypeUpdateNeeded(property)) {
+            const propertyWithPrefix = this.addTypePrefix(property)
+            operations.push(this.deletePropertyAssignmentOperation(property))
+            operations.push(
+              this.createPropertyTypeOperation(propertyWithPrefix)
+            )
+            assignments.push(
+              this.propertyAssignmentCreation(propertyWithPrefix, index)
+            )
+          } else {
+            assignments.push(this.propertyAssignmentCreation(property, index))
+          }
         }
       } else {
-        toDelete[loadedPropertyType.code] = loadedPropertyType.code
+        const propertyWithPrefix = this.addTypePrefix(property)
+        operations.push(this.createPropertyTypeOperation(propertyWithPrefix))
+        assignments.push(
+          this.propertyAssignmentCreation(propertyWithPrefix, index)
+        )
       }
     })
 
-    return { toCreate, toUpdate, toDelete }
-  }
+    operations.push(this.updateTypeOperation(assignments))
 
-  deletePropertyAssignmentsOperations(toDelete) {
-    const assignmentIds = []
+    return operations
+  }
 
-    Object.values(toDelete).forEach(code => {
-      assignmentIds.push(
-        new dto.PropertyAssignmentPermId(
-          new dto.EntityTypePermId(this.type.code, dto.EntityKind.SAMPLE),
-          new dto.PropertyTypePermId(code)
-        )
-      )
-    })
+  deletePropertyAssignmentOperation(property) {
+    const assignmentId = new dto.PropertyAssignmentPermId(
+      new dto.EntityTypePermId(this.type.code, dto.EntityKind.SAMPLE),
+      new dto.PropertyTypePermId(property.code)
+    )
 
     const update = new dto.SampleTypeUpdate()
     update.setTypeId(
       new dto.EntityTypePermId(this.type.code, dto.EntityKind.SAMPLE)
     )
-    update.getPropertyAssignments().remove(assignmentIds)
-    update.getPropertyAssignments().setForceRemovingAssignments(true)
+    update.getPropertyAssignments().remove([assignmentId])
+    update
+      .getPropertyAssignments()
+      .setForceRemovingAssignments(property.usages > 0)
 
-    return [new dto.UpdateSampleTypesOperation([update])]
+    return new dto.UpdateSampleTypesOperation([update])
   }
 
-  createUpdateDeletePropertyTypesOperations(toCreate, toUpdate, toDelete) {
-    const operations = []
-
-    Object.values(toDelete).forEach(code => {
-      const ids = [new dto.PropertyTypePermId(code)]
-      const options = new dto.PropertyTypeDeletionOptions()
-      options.setReason('deleted via ng_ui')
-      operations.push(new dto.DeletePropertyTypesOperation(ids, options))
-    })
-
-    Object.values(toCreate).forEach(property => {
-      const creation = new dto.PropertyTypeCreation()
-      creation.setCode(property.code)
-      creation.setLabel(property.label)
-      creation.setDescription(property.description)
-      creation.setDataType(property.dataType)
-      creation.setSchema(property.schema)
-      creation.setTransformation(property.transformation)
-
-      if (
-        property.dataType === dto.DataType.CONTROLLEDVOCABULARY &&
-        property.vocabulary
-      ) {
-        creation.setVocabularyId(new dto.VocabularyPermId(property.vocabulary))
-      }
-      if (
-        property.dataType === dto.DataType.MATERIAL &&
-        property.materialType
-      ) {
-        creation.setMaterialTypeId(
-          new dto.EntityTypePermId(
-            property.materialType,
-            dto.EntityKind.MATERIAL
-          )
-        )
-      }
-      operations.push(new dto.CreatePropertyTypesOperation([creation]))
-    })
+  createPropertyTypeOperation(property) {
+    const creation = new dto.PropertyTypeCreation()
+    creation.setCode(property.code)
+    creation.setLabel(property.label)
+    creation.setDescription(property.description)
+    creation.setDataType(property.dataType)
+    creation.setSchema(property.schema)
+    creation.setTransformation(property.transformation)
+
+    if (
+      property.dataType === dto.DataType.CONTROLLEDVOCABULARY &&
+      property.vocabulary
+    ) {
+      creation.setVocabularyId(new dto.VocabularyPermId(property.vocabulary))
+    }
+    if (property.dataType === dto.DataType.MATERIAL && property.materialType) {
+      creation.setMaterialTypeId(
+        new dto.EntityTypePermId(property.materialType, dto.EntityKind.MATERIAL)
+      )
+    }
+    return new dto.CreatePropertyTypesOperation([creation])
+  }
 
-    Object.values(toUpdate).forEach(property => {
-      const update = new dto.PropertyTypeUpdate()
-      if (property.code) {
-        update.setTypeId(new dto.PropertyTypePermId(property.code))
-      }
-      update.setLabel(property.label)
-      update.setDescription(property.description)
-      update.setSchema(property.schema)
-      update.setTransformation(property.transformation)
-      operations.push(new dto.UpdatePropertyTypesOperation([update]))
-    })
+  updatePropertyTypeOperation(property) {
+    const update = new dto.PropertyTypeUpdate()
+    if (property.code) {
+      update.setTypeId(new dto.PropertyTypePermId(property.code))
+    }
+    update.setLabel(property.label)
+    update.setDescription(property.description)
+    update.setSchema(property.schema)
+    update.setTransformation(property.transformation)
+    return new dto.UpdatePropertyTypesOperation([update])
+  }
 
-    return operations
+  deletePropertyTypeOperation(property) {
+    const id = new dto.PropertyTypePermId(property.code)
+    const options = new dto.PropertyTypeDeletionOptions()
+    options.setReason('deleted via ng_ui')
+    return new dto.DeletePropertyTypesOperation([id], options)
   }
 
-  updateTypeAndAssignmentsOperations() {
-    const updateProperties = this.properties.map((property, index) => {
-      let updateProperty = new dto.PropertyAssignmentCreation()
-      updateProperty.setOrdinal(index + 1)
-      updateProperty.setMandatory(property.mandatory)
-      updateProperty.setInitialValueForExistingEntities(
-        property.initialValueForExistingEntities
-      )
-      updateProperty.setShowInEditView(property.showInEditView)
-      updateProperty.setShowRawValueInForms(property.showRawValueInForms)
-      updateProperty.setSection(property.section)
+  propertyAssignmentCreation(property, index) {
+    let creation = new dto.PropertyAssignmentCreation()
+    creation.setOrdinal(index + 1)
+    creation.setMandatory(property.mandatory)
+    creation.setInitialValueForExistingEntities(
+      property.initialValueForExistingEntities
+    )
+    creation.setShowInEditView(property.showInEditView)
+    creation.setShowRawValueInForms(property.showRawValueInForms)
+    creation.setSection(property.section)
 
-      if (property.code) {
-        updateProperty.setPropertyTypeId(
-          new dto.PropertyTypePermId(property.code)
-        )
-      }
+    if (property.code) {
+      creation.setPropertyTypeId(new dto.PropertyTypePermId(property.code))
+    }
 
-      if (property.plugin) {
-        updateProperty.setPluginId(new dto.PluginPermId(property.plugin))
-      }
+    if (property.plugin) {
+      creation.setPluginId(new dto.PluginPermId(property.plugin))
+    }
 
-      return updateProperty
-    })
+    return creation
+  }
 
+  updateTypeOperation(assignments) {
     const update = new dto.SampleTypeUpdate()
     update.setTypeId(
       new dto.EntityTypePermId(this.type.code, dto.EntityKind.SAMPLE)
@@ -254,9 +290,9 @@ export default class ObjectTypeHandlerSave {
         ? new dto.PluginPermId(this.type.validationPlugin)
         : null
     )
-    update.getPropertyAssignments().set(updateProperties)
+    update.getPropertyAssignments().set(assignments)
 
-    return [new dto.UpdateSampleTypesOperation([update])]
+    return new dto.UpdateSampleTypesOperation([update])
   }
 
   execute() {
@@ -269,7 +305,13 @@ export default class ObjectTypeHandlerSave {
         this.setState({
           loading: true
         })
-        return this.doExecute()
+
+        const operations = this.createOperations()
+        const options = new dto.SynchronousOperationExecutionOptions()
+        options.setExecuteInOrder(true)
+
+        return this.facade
+          .executeOperations(operations, options)
           .then(() => {
             return this.validateHandler.setEnabled(false)
           })
@@ -286,35 +328,4 @@ export default class ObjectTypeHandlerSave {
         })
       })
   }
-
-  doExecute() {
-    return this.facade
-      .loadPropertyTypes(this.type.code)
-      .then(loadedPropertyTypes => {
-        const { toCreate, toUpdate, toDelete } = this.preparePropertyChanges(
-          loadedPropertyTypes
-        )
-        const deletePropertyAssignmentsOperations = this.deletePropertyAssignmentsOperations(
-          toDelete
-        )
-        const createUpdateDeletePropertyTypesOperations = this.createUpdateDeletePropertyTypesOperations(
-          toCreate,
-          toUpdate,
-          toDelete
-        )
-        const updateTypeAndAssignmentsOperations = this.updateTypeAndAssignmentsOperations()
-
-        const options = new dto.SynchronousOperationExecutionOptions()
-        options.setExecuteInOrder(true)
-
-        return this.facade.executeOperations(
-          [
-            ...deletePropertyAssignmentsOperations,
-            ...createUpdateDeletePropertyTypesOperations,
-            ...updateTypeAndAssignmentsOperations
-          ],
-          options
-        )
-      })
-  }
 }
diff --git a/openbis_ng_ui/src/js/components/types/objectType/ObjectTypeParametersProperty.jsx b/openbis_ng_ui/src/js/components/types/objectType/ObjectTypeParametersProperty.jsx
index 6d6a6ad7f86..62a578f99ec 100644
--- a/openbis_ng_ui/src/js/components/types/objectType/ObjectTypeParametersProperty.jsx
+++ b/openbis_ng_ui/src/js/components/types/objectType/ObjectTypeParametersProperty.jsx
@@ -4,7 +4,8 @@ import Typography from '@material-ui/core/Typography'
 import CheckboxField from '../../common/form/CheckboxField.jsx'
 import TextField from '../../common/form/TextField.jsx'
 import SelectField from '../../common/form/SelectField.jsx'
-import ObjectTypeUsageWarning from './ObjectTypeUsageWarning.jsx'
+import ObjectTypeWarningUsage from './ObjectTypeWarningUsage.jsx'
+import ObjectTypeWarningLegacy from './ObjectTypeWarningLegacy.jsx'
 import { dto } from '../../../services/openbis.js'
 import logger from '../../../common/logger.js'
 
@@ -176,7 +177,8 @@ class ObjectTypeParametersProperty extends React.PureComponent {
         <Typography variant='h6' className={classes.header}>
           Property
         </Typography>
-        {this.renderWarning(property)}
+        {this.renderWarningLegacy(property)}
+        {this.renderWarningUsage(property)}
         {this.renderCode(property)}
         {this.renderDataType(property)}
         {this.renderVocabulary(property)}
@@ -193,12 +195,25 @@ class ObjectTypeParametersProperty extends React.PureComponent {
     )
   }
 
-  renderWarning(property) {
+  renderWarningLegacy(property) {
+    if (this.isLegacy(property)) {
+      const { classes } = this.props
+      return (
+        <div className={classes.field}>
+          <ObjectTypeWarningLegacy />
+        </div>
+      )
+    } else {
+      return null
+    }
+  }
+
+  renderWarningUsage(property) {
     if (property.usages > 0) {
       const { classes } = this.props
       return (
         <div className={classes.field}>
-          <ObjectTypeUsageWarning subject='property' usages={property.usages} />
+          <ObjectTypeWarningUsage subject='property' usages={property.usages} />
         </div>
       )
     } else {
@@ -216,6 +231,7 @@ class ObjectTypeParametersProperty extends React.PureComponent {
           name='label'
           mandatory={true}
           error={property.errors.label}
+          disabled={this.isLegacy(property)}
           value={property.label}
           onChange={this.handleChange}
           onFocus={this.handleFocus}
@@ -255,6 +271,7 @@ class ObjectTypeParametersProperty extends React.PureComponent {
           name='description'
           mandatory={true}
           error={property.errors.description}
+          disabled={this.isLegacy(property)}
           value={property.description}
           onChange={this.handleChange}
           onFocus={this.handleFocus}
@@ -282,7 +299,7 @@ class ObjectTypeParametersProperty extends React.PureComponent {
           name='dataType'
           mandatory={true}
           error={property.errors.dataType}
-          disabled={property.usages > 0}
+          disabled={property.usages > 0 || this.isLegacy(property)}
           value={property.dataType}
           options={options}
           onChange={this.handleChange}
@@ -318,7 +335,7 @@ class ObjectTypeParametersProperty extends React.PureComponent {
             name='vocabulary'
             mandatory={true}
             error={property.errors.vocabulary}
-            disabled={property.usages > 0}
+            disabled={property.usages > 0 || this.isLegacy(property)}
             value={property.vocabulary}
             options={options}
             onChange={this.handleChange}
@@ -357,7 +374,7 @@ class ObjectTypeParametersProperty extends React.PureComponent {
             name='materialType'
             mandatory={true}
             error={property.errors.materialType}
-            disabled={property.usages > 0}
+            disabled={property.usages > 0 || this.isLegacy(property)}
             value={property.materialType}
             options={options}
             onChange={this.handleChange}
@@ -381,6 +398,7 @@ class ObjectTypeParametersProperty extends React.PureComponent {
             label='XML Schema'
             name='schema'
             error={property.errors.schema}
+            disabled={this.isLegacy(property)}
             value={property.schema}
             multiline={true}
             onChange={this.handleChange}
@@ -404,6 +422,7 @@ class ObjectTypeParametersProperty extends React.PureComponent {
             label='XSLT Script'
             name='transformation'
             error={property.errors.transformation}
+            disabled={this.isLegacy(property)}
             value={property.transformation}
             multiline={true}
             onChange={this.handleChange}
@@ -440,6 +459,7 @@ class ObjectTypeParametersProperty extends React.PureComponent {
           label='Dynamic Plugin'
           name='plugin'
           error={property.errors.plugin}
+          disabled={property.usages > 0}
           value={property.plugin}
           options={options}
           onChange={this.handleChange}
@@ -535,6 +555,12 @@ class ObjectTypeParametersProperty extends React.PureComponent {
       return null
     }
   }
+
+  isLegacy(property) {
+    return (
+      property.original && !property.code.startsWith(this.props.type.code + '.')
+    )
+  }
 }
 
 export default withStyles(styles)(ObjectTypeParametersProperty)
diff --git a/openbis_ng_ui/src/js/components/types/objectType/ObjectTypeParametersType.jsx b/openbis_ng_ui/src/js/components/types/objectType/ObjectTypeParametersType.jsx
index 0a5b20a4b5c..9a4aff70cf1 100644
--- a/openbis_ng_ui/src/js/components/types/objectType/ObjectTypeParametersType.jsx
+++ b/openbis_ng_ui/src/js/components/types/objectType/ObjectTypeParametersType.jsx
@@ -4,7 +4,7 @@ import Typography from '@material-ui/core/Typography'
 import CheckboxField from '../../common/form/CheckboxField.jsx'
 import TextField from '../../common/form/TextField.jsx'
 import SelectField from '../../common/form/SelectField.jsx'
-import ObjectTypeUsageWarning from './ObjectTypeUsageWarning.jsx'
+import ObjectTypeWarningUsage from './ObjectTypeWarningUsage.jsx'
 import logger from '../../../common/logger.js'
 
 const styles = theme => ({
@@ -144,7 +144,7 @@ class ObjectTypeParametersType extends React.PureComponent {
       const { classes } = this.props
       return (
         <div className={classes.field}>
-          <ObjectTypeUsageWarning subject='type' usages={type.usages} />
+          <ObjectTypeWarningUsage subject='type' usages={type.usages} />
         </div>
       )
     } else {
diff --git a/openbis_ng_ui/src/js/components/types/objectType/ObjectTypeUsageWarning.jsx b/openbis_ng_ui/src/js/components/types/objectType/ObjectTypeWarning.jsx
similarity index 63%
rename from openbis_ng_ui/src/js/components/types/objectType/ObjectTypeUsageWarning.jsx
rename to openbis_ng_ui/src/js/components/types/objectType/ObjectTypeWarning.jsx
index e12f33ea854..79b98e7a8fa 100644
--- a/openbis_ng_ui/src/js/components/types/objectType/ObjectTypeUsageWarning.jsx
+++ b/openbis_ng_ui/src/js/components/types/objectType/ObjectTypeWarning.jsx
@@ -15,20 +15,19 @@ const styles = theme => ({
   }
 })
 
-class ObjectTypeUsageWarning extends React.PureComponent {
+class ObjectTypeWarning extends React.PureComponent {
   render() {
-    logger.log(logger.DEBUG, 'ObjectTypeUsageWarning.render')
+    logger.log(logger.DEBUG, 'ObjectTypeWarning.render')
 
-    const { classes, subject, usages } = this.props
+    const { classes, children } = this.props
 
     return (
       <Typography variant='body2' className={classes.warning}>
         <WarningIcon />
-        This {subject} is already used by {usages}
-        {usages > 1 ? ' entities' : ' entity'}.
+        {children}
       </Typography>
     )
   }
 }
 
-export default withStyles(styles)(ObjectTypeUsageWarning)
+export default withStyles(styles)(ObjectTypeWarning)
diff --git a/openbis_ng_ui/src/js/components/types/objectType/ObjectTypeWarningLegacy.jsx b/openbis_ng_ui/src/js/components/types/objectType/ObjectTypeWarningLegacy.jsx
new file mode 100644
index 00000000000..358c2f57b14
--- /dev/null
+++ b/openbis_ng_ui/src/js/components/types/objectType/ObjectTypeWarningLegacy.jsx
@@ -0,0 +1,17 @@
+import React from 'react'
+import ObjectTypeWarning from './ObjectTypeWarning.jsx'
+import logger from '../../../common/logger.js'
+
+class ObjectTypeWarningLegacy extends React.PureComponent {
+  render() {
+    logger.log(logger.DEBUG, 'ObjectTypeWarningLegacy.render')
+
+    return (
+      <ObjectTypeWarning>
+        This property is legacy (reusable among types).
+      </ObjectTypeWarning>
+    )
+  }
+}
+
+export default ObjectTypeWarningLegacy
diff --git a/openbis_ng_ui/src/js/components/types/objectType/ObjectTypeWarningUsage.jsx b/openbis_ng_ui/src/js/components/types/objectType/ObjectTypeWarningUsage.jsx
new file mode 100644
index 00000000000..b236d160bc9
--- /dev/null
+++ b/openbis_ng_ui/src/js/components/types/objectType/ObjectTypeWarningUsage.jsx
@@ -0,0 +1,20 @@
+import React from 'react'
+import ObjectTypeWarning from './ObjectTypeWarning.jsx'
+import logger from '../../../common/logger.js'
+
+class ObjectTypeWarningUsage extends React.PureComponent {
+  render() {
+    logger.log(logger.DEBUG, 'ObjectTypeWarningUsage.render')
+
+    const { subject, usages } = this.props
+
+    return (
+      <ObjectTypeWarning>
+        This {subject} is already used by {usages}
+        {usages > 1 ? ' entities' : ' entity'}.
+      </ObjectTypeWarning>
+    )
+  }
+}
+
+export default ObjectTypeWarningUsage
-- 
GitLab