Kuali Rice Development
  1. Kuali Rice Development
  2. KULRICE-12625

Attribute definitions not being correctly picked up from data dictionary for input fields

    Details

    • Type: Bug Fix Bug Fix
    • Status: Closed Closed
    • Priority: Critical Critical
    • Resolution: Fixed
    • Affects Version/s: 2.4
    • Fix Version/s: 2.4.2
    • Component/s: Development
    • Security Level: Public (Public: Anyone can view)
    • Labels:
      None
    • Similar issues:
      KULRICE-13844Attribute definitions for document objects not getting picked up by view helper
      KULRICE-8951Property editors not getting picked up on add line and collection line items
      KULRICE-10121Attribute Definition: Conversion Script
      KULRICE-4137Validations and labels for group qualfications not getting correctly picked up
      KULRICE-9143Non data dictionary attributes missing shortLabel in DataDictionaryTypeServiceBase
      KULRICE-1597Look into regex date validation via the data dictionary
      KULRICE-10120Attribute Definition: Conversion Guide
      KULRICE-2621Remove old fields from data dictionary related to Document Type merge, KIM work, etc., create a script which will remove them from data dictionary files
      KULRICE-11891Default Values not being applied on Input and Data Fields
      KULRICE-12638Binding path for PrerequisiteConstraints defined in data dictionary wrong on reload
    • Rice Module:
      KRAD
    • KRAD Feature Area:
      UIF Component
    • Application Requirement:
      KS
    • KAI Review Status:
      Not Required
    • KTI Review Status:
      Not Required
    • Code Review Status:
      Not Required
    • Include in Release Notes?:
      Yes

      Description

      Hi,

      I'm trying to resolve an issue we are having with data dictionary constraints after upgrading to Rice v2.4 and hope someone here might be able to help.

      We have a DD entry somewhat like this (only more complicated) ....

      <bean id="Foo" parent="DataObjectEntry">
      <property name="name" value="foo"/>
      <property name="dataObjectClass" value="org.kuali.student.Foo"/>
      <property name="complexAttributes">
      <list><ref bean="Foo.descr"/></list>
      </property>
      </bean>

      <bean id="Foo.descr" parent="ComplexAttributeDefinition">
      <property name="name" value="descr"/>
      <property name="dataObjectEntry">
      <bean parent="DataObjectEntry">
      <property name="name" value="descr"/>
      <property name="dataObjectClass" value="org.kuali.student.Bar"/>
      <property name="attributes">
      <list><ref bean="Foo.descr.text"/></list>
      ...

      <bean id="Foo.descr.text" parent="AttributeDefinition">
      <property name="name" value="text"/>
      <property name="dataType" value="STRING" />
      <property name="required" value="true"/>
      </bean>

      A DD entry for org.kuali.student.Bar is also defined like this ...

      <bean id="Bar" parent="DataObjectEntry">
      <property name="name" value="foo"/>
      <property name="dataObjectClass" value="org.kuali.student.Bar"/>
      <property name="attributes">
      <list><ref bean="Bar.text"/></list>
      </property>
      </bean>

      <bean id="Bar.text" parent="AttributeDefinition">
      <property name="name" value="text"/>
      <property name="dataType" value="STRING" />
      <property name="required" value="false"/>
      </bean>

      (Hope I got all of that right). Notice that the Bar.text:requred=false and Foo.descr.text=true.

      So, we have an InputField which references Foo.descr.text, but the AttributeDefinition that gets fished out of the DD is the one for Bar.text, not Foo.descr.text.

      I have debugged it when the binding path is parsed, we'll say "foo.desc.text", it finds "foo" (org.kuali.student.Foo) just fine, but when it then looks for a "descr" attribute def under "foo" and comes up empty handed because the DD only contains an entry for "descr.text". Then somewhere up the stack (I assume) it determines that there is a DataObjectEntry for Bar, so it looks for Bar.text, finds it, and uses that AttributeDefininition.

      Seems like when it doesn't find a match for descr under Foo it should look for "descr.text". Or maybe there should be an entry in the DD for descr under Foo and it should keep parsing.

      At any rate, this config worked fine in 2.3. Does this sound like a bug? Can you suggest workaround?

      Thanks,

      Glenn Sudduth,
      Kuali Student

        Issue Links

          Activity

          Hide
          Mark Fyffe (Inactive) added a comment -

          Looking into this, first on Rice 2.5 following the example Glenn originally posted, then will confirm the fix running KS trunk against 2.4.1.

          From: Glenn gsudduth@gmail.com
          Sent: Wednesday, May 07, 2014 12:45 PM
          To: Mark Fyffe
          Cc: rice.usergroup.krad@kuali.org
          Subject: Re:

          Unknown macro: {Rice KRAD User Group}

          Data dictionary constraints not applied correctly in v2.4

          Hi Mark,

          Here is a mapping of made up entities to KS entities and their respective source files in SVN ...

          Foo == CourseInfo,
          Foo.descr == CourseInfo.descr,
          Foo.descr.text == CourseInfo.descr.plain ...

          https://svn.kuali.org/repos/student/enrollment/ks-api/trunk/ks-enroll-api/src/main/resources/ks-CourseInfo-dictionary.xml
          https://svn.kuali.org/repos/student/enrollment/ks-api/trunk/ks-enroll-api/src/main/resources/ks-CourseInfo-dictionary-generated.xml

          Bar == RichTextInfo,
          Bar.text == RichTextInfo.plain ...

          https://svn.kuali.org/repos/student/enrollment/ks-api/trunk/ks-enroll-api/src/main/resources/ks-RichTextInfo-dictionary.xml
          https://svn.kuali.org/repos/student/enrollment/ks-api/trunk/ks-enroll-api/src/main/resources/ks-RichTextInfo-dictionary-generated.xml

          Let me know if you need any other info.

          Thanks,

          Glenn

          Show
          Mark Fyffe (Inactive) added a comment - Looking into this, first on Rice 2.5 following the example Glenn originally posted, then will confirm the fix running KS trunk against 2.4.1. From: Glenn gsudduth@gmail.com Sent: Wednesday, May 07, 2014 12:45 PM To: Mark Fyffe Cc: rice.usergroup.krad@kuali.org Subject: Re: Unknown macro: {Rice KRAD User Group} Data dictionary constraints not applied correctly in v2.4 Hi Mark, Here is a mapping of made up entities to KS entities and their respective source files in SVN ... Foo == CourseInfo, Foo.descr == CourseInfo.descr, Foo.descr.text == CourseInfo.descr.plain ... https://svn.kuali.org/repos/student/enrollment/ks-api/trunk/ks-enroll-api/src/main/resources/ks-CourseInfo-dictionary.xml https://svn.kuali.org/repos/student/enrollment/ks-api/trunk/ks-enroll-api/src/main/resources/ks-CourseInfo-dictionary-generated.xml Bar == RichTextInfo, Bar.text == RichTextInfo.plain ... https://svn.kuali.org/repos/student/enrollment/ks-api/trunk/ks-enroll-api/src/main/resources/ks-RichTextInfo-dictionary.xml https://svn.kuali.org/repos/student/enrollment/ks-api/trunk/ks-enroll-api/src/main/resources/ks-RichTextInfo-dictionary-generated.xml Let me know if you need any other info. Thanks, – Glenn
          Hide
          Mark Fyffe (Inactive) added a comment -

          I've set up a unit test in my local Rice 2.4 project and have confirmed that this InitializeDataFieldFromDictionaryTask is not picking up the correct attribute.

          Here is the View and attributes I'm using, based on Glenn's initial report:

          	<bean id="TestView" parent="Uif-FormView">
          		<property name="items">
          			<list>
          				<bean id="TestPage" parent="Uif-Page">
          					<property name="items">
          						<list>
          							<bean parent="Uif-VerticalBoxSection">
          								<property name="items">
          									<list>
          										<bean parent="Uif-InputField" p:label="Foo" p:propertyName="foo.descr.text">
          											<property name="control">
          												<bean parent="Uif-TextControl" />
          											</property>
          										</bean>
          										<bean parent="Uif-InputField" p:label="Bar" p:propertyName="bar.text">
          											<property name="control">
          												<bean parent="Uif-TextControl" />
          											</property>
          										</bean>
          									</list>
          								</property>
          							</bean>
          						</list>
          					</property>
          				</bean>
          			</list>
          		</property>
          	</bean>
          
          	<bean id="Foo" parent="DataObjectEntry">
          		<property name="name" value="foo" />
          		<property name="dataObjectClass" value="org.kuali.rice.krad.uif.field.InitialzeDataFieldFromDictionaryTest$Foo" />
          		<property name="complexAttributes">
          			<list>
          				<ref bean="Foo.descr" />
          			</list>
          		</property>
          	</bean>
          
          	<bean id="Foo.descr" parent="ComplexAttributeDefinition">
          		<property name="name" value="descr" />
          		<property name="dataObjectEntry">
          			<bean parent="DataObjectEntry">
          				<property name="name" value="descr" />
          				<property name="dataObjectClass" value="org.kuali.rice.krad.uif.field.InitialzeDataFieldFromDictionaryTest$Bar" />
          				<property name="attributes">
          					<list>
          						<ref bean="Foo.descr.text" />
          					</list>
          				</property>
          			</bean>
          		</property>
          	</bean>
          
          	<bean id="Foo.descr.text" parent="AttributeDefinition">
          		<property name="name" value="text" />
          		<property name="dataType" value="STRING" />
          		<property name="required" value="true" />
          	</bean>
          
          	<bean id="Bar" parent="DataObjectEntry">
          		<property name="name" value="foo" />
          		<property name="dataObjectClass" value="org.kuali.rice.krad.uif.field.InitialzeDataFieldFromDictionaryTest$Bar" />
          		<property name="attributes">
          			<list>
          				<ref bean="Bar.text" />
          			</list>
          		</property>
          	</bean>
          
          	<bean id="Bar.text" parent="AttributeDefinition">
          		<property name="name" value="text" />
          		<property name="dataType" value="STRING" />
          		<property name="required" value="false" />
          	</bean>
          

          And the test:

                      View view = ViewLifecycle.getView();
                      Object model = ViewLifecycle.getModel();
          
                      String foopath = "currentPage.items[0].items[0]";
                      DataField foofield = ObjectPropertyUtils.getPropertyValue(view, foopath);
                      InitializeComponentPhase foophase = LifecyclePhaseFactory.initialize(foofield, model,
                              foopath, Collections.<String> emptyList());
                      InitializeDataFieldFromDictionaryTask footask = LifecycleTaskFactory.getTask(
                              InitializeDataFieldFromDictionaryTask.class, foophase);
          
                      String barpath = "currentPage.items[0].items[1]";
                      DataField barfield = ObjectPropertyUtils.getPropertyValue(view, barpath);
                      InitializeComponentPhase barphase = LifecyclePhaseFactory.initialize(barfield, model,
                              barpath, Collections.<String> emptyList());
                      InitializeDataFieldFromDictionaryTask bartask = LifecycleTaskFactory.getTask(
                              InitializeDataFieldFromDictionaryTask.class, barphase);
          
                      AttributeDefinition fooattribute = footask.findNestedDictionaryAttribute(foofield.getPropertyName());
                      assertTrue(fooattribute.isRequired());
          
                      AttributeDefinition barattribute = bartask.findNestedDictionaryAttribute(barfield.getPropertyName());
                      assertFalse(barattribute.isRequired());
          

          From the javadoc for findNestedDictionaryAttribute(path):

          Recursively drills down the property path (if nested) to find an AttributeDefinition, the first attribute definition found will be returned

          e.g. suppose parentPath is 'document' and propertyPath is 'account.subAccount.name', first the property type for document will be retrieved using the view metadata and used as the dictionary entry, with the propertyPath as the dictionary attribute, if an attribute definition exists it will be returned. Else, the first part of the property path is added to the parent, making the parentPath 'document.account' and the propertyPath 'subAccount.name', the method is then called again to perform the process with those parameters. The recursion continues until an attribute field is found, or the propertyPath is no longer nested

          In looking at the implementation of this method, it is difficult to see how the behavior described is implemented. The implementation is overly complex, but the behavior described is relatively simple. It appears that where we're going wrong is that only the next path token (ie. descr) is being considered on each step rather than the tail of the path (ie. descr.text). I'm working now to simplify this method based on the behavior described in the javadoc comments.

          Show
          Mark Fyffe (Inactive) added a comment - I've set up a unit test in my local Rice 2.4 project and have confirmed that this InitializeDataFieldFromDictionaryTask is not picking up the correct attribute. Here is the View and attributes I'm using, based on Glenn's initial report: <bean id= "TestView" parent= "Uif-FormView" > <property name= "items" > <list> <bean id= "TestPage" parent= "Uif-Page" > <property name= "items" > <list> <bean parent= "Uif-VerticalBoxSection" > <property name= "items" > <list> <bean parent= "Uif-InputField" p:label= "Foo" p:propertyName= "foo.descr.text" > <property name= "control" > <bean parent= "Uif-TextControl" /> </property> </bean> <bean parent= "Uif-InputField" p:label= "Bar" p:propertyName= "bar.text" > <property name= "control" > <bean parent= "Uif-TextControl" /> </property> </bean> </list> </property> </bean> </list> </property> </bean> </list> </property> </bean> <bean id= "Foo" parent= "DataObjectEntry" > <property name= "name" value= "foo" /> <property name= "dataObjectClass" value= "org.kuali.rice.krad.uif.field.InitialzeDataFieldFromDictionaryTest$Foo" /> <property name= "complexAttributes" > <list> <ref bean= "Foo.descr" /> </list> </property> </bean> <bean id= "Foo.descr" parent= "ComplexAttributeDefinition" > <property name= "name" value= "descr" /> <property name= "dataObjectEntry" > <bean parent= "DataObjectEntry" > <property name= "name" value= "descr" /> <property name= "dataObjectClass" value= "org.kuali.rice.krad.uif.field.InitialzeDataFieldFromDictionaryTest$Bar" /> <property name= "attributes" > <list> <ref bean= "Foo.descr.text" /> </list> </property> </bean> </property> </bean> <bean id= "Foo.descr.text" parent= "AttributeDefinition" > <property name= "name" value= "text" /> <property name= "dataType" value= "STRING" /> <property name= "required" value= " true " /> </bean> <bean id= "Bar" parent= "DataObjectEntry" > <property name= "name" value= "foo" /> <property name= "dataObjectClass" value= "org.kuali.rice.krad.uif.field.InitialzeDataFieldFromDictionaryTest$Bar" /> <property name= "attributes" > <list> <ref bean= "Bar.text" /> </list> </property> </bean> <bean id= "Bar.text" parent= "AttributeDefinition" > <property name= "name" value= "text" /> <property name= "dataType" value= "STRING" /> <property name= "required" value= " false " /> </bean> And the test: View view = ViewLifecycle.getView(); Object model = ViewLifecycle.getModel(); String foopath = "currentPage.items[0].items[0]" ; DataField foofield = ObjectPropertyUtils.getPropertyValue(view, foopath); InitializeComponentPhase foophase = LifecyclePhaseFactory.initialize(foofield, model, foopath, Collections.< String > emptyList()); InitializeDataFieldFromDictionaryTask footask = LifecycleTaskFactory.getTask( InitializeDataFieldFromDictionaryTask.class, foophase); String barpath = "currentPage.items[0].items[1]" ; DataField barfield = ObjectPropertyUtils.getPropertyValue(view, barpath); InitializeComponentPhase barphase = LifecyclePhaseFactory.initialize(barfield, model, barpath, Collections.< String > emptyList()); InitializeDataFieldFromDictionaryTask bartask = LifecycleTaskFactory.getTask( InitializeDataFieldFromDictionaryTask.class, barphase); AttributeDefinition fooattribute = footask.findNestedDictionaryAttribute(foofield.getPropertyName()); assertTrue(fooattribute.isRequired()); AttributeDefinition barattribute = bartask.findNestedDictionaryAttribute(barfield.getPropertyName()); assertFalse(barattribute.isRequired()); From the javadoc for findNestedDictionaryAttribute(path): Recursively drills down the property path (if nested) to find an AttributeDefinition, the first attribute definition found will be returned e.g. suppose parentPath is 'document' and propertyPath is 'account.subAccount.name', first the property type for document will be retrieved using the view metadata and used as the dictionary entry, with the propertyPath as the dictionary attribute, if an attribute definition exists it will be returned. Else, the first part of the property path is added to the parent, making the parentPath 'document.account' and the propertyPath 'subAccount.name', the method is then called again to perform the process with those parameters. The recursion continues until an attribute field is found, or the propertyPath is no longer nested In looking at the implementation of this method, it is difficult to see how the behavior described is implemented. The implementation is overly complex, but the behavior described is relatively simple. It appears that where we're going wrong is that only the next path token (ie. descr) is being considered on each step rather than the tail of the path (ie. descr.text). I'm working now to simplify this method based on the behavior described in the javadoc comments.
          Hide
          Mark Fyffe (Inactive) added a comment -

          I have correct the behavior of InitializeDataFieldFromDictionaryTask and committed the rice-2.4 branch.

          To simplify the algorithm, I migrated the code originally in #getModelClassPath() to ObjectPropertyUtils#getCanonicalPath(). The purpose of this method is to remove index/key references from property paths, and is a generally useful utility implemented in multiple places throughout rice-krad-web-framework. Migrating from InitializeDataFieldFromDictionaryTask is a step toward consolidating these implementations.

          Once the canonical path is available, the need for parsing the path as a fully formed expression is no longer necessary, so I was able to strip down #findNestedDictionaryAttribute() to an indexOf/while loop, checking path elements as described in the javadoc comments until an attribute is found.

          The unit tests described in earlier comments are now passing, and I have confirmed that the CourseInfo.descr.plan input field now behaves as a required field by running the KSCM course maintenence doc with this update.

          Show
          Mark Fyffe (Inactive) added a comment - I have correct the behavior of InitializeDataFieldFromDictionaryTask and committed the rice-2.4 branch. To simplify the algorithm, I migrated the code originally in #getModelClassPath() to ObjectPropertyUtils#getCanonicalPath(). The purpose of this method is to remove index/key references from property paths, and is a generally useful utility implemented in multiple places throughout rice-krad-web-framework. Migrating from InitializeDataFieldFromDictionaryTask is a step toward consolidating these implementations. Once the canonical path is available, the need for parsing the path as a fully formed expression is no longer necessary, so I was able to strip down #findNestedDictionaryAttribute() to an indexOf/while loop, checking path elements as described in the javadoc comments until an attribute is found. The unit tests described in earlier comments are now passing, and I have confirmed that the CourseInfo.descr.plan input field now behaves as a required field by running the KSCM course maintenence doc with this update.

            People

            • Assignee:
              Mark Fyffe (Inactive)
              Reporter:
              Jerry Neal (Inactive)
            • Votes:
              0 Vote for this issue
              Watchers:
              5 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:

                Time Tracking

                Estimated:
                Original Estimate - 6 hours
                6h
                Remaining:
                Remaining Estimate - 0 minutes
                0m
                Logged:
                Time Spent - 6 hours
                6h

                  Structure Helper Panel