-
Notifications
You must be signed in to change notification settings - Fork 558
Fields that are polymorphic fail due to first type being used. #137
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…he type specified in the selector. Add the ability to set the sObjectType when validating the field exists on the object. Use the type set in the selector to determine which polymorphic type to use. Example: Lead.Owner.UserRoleID Owner is GROUP | USER Group does not contain UserRoleID and fails when the getField call sets the sObjectField token to null.
|
Actually, polymorphic fields in Salesforce are a special case. Lead.Owner can relate to a Queue or a User. With a bit of experimenting and googling, it turns out that the related object type is an SObjectType called Name. It's a coincidence that there are fields with the same name on the User and Name SObjectTypes. It would be a dangerous assumption to say all fields on the User SObjectType will also exist on Name. So I think the fix is smaller, and shouldn't require any changes to method signatures. Just when trying to get the describe for the lookup referenceTo, it should be something like: |
|
In this case of Lead.Owner, the getDescribe method returns 2 values, Queue and User. Name would not match the values returned. The validation is performed using the defined selector's getSObjectType method to validate that the field exists on the specified sObjectType. The UserSelector.getSObjectType returns User.getSObjectType if it were another selector, it would return the type specific to it. For the above reason, I am not following the "dangerous assumption" statement. As stated, all the fields on User do not exist on Name. If the relatedSObjectType is set to Name, the validation will still fail when looping the fields to find a User or Queue specific field. For example, the field User.BadgeText does not exist on Name. While this proposed solution is simpler on the surface, it still contains the same error this solution solves. That is, when the process loops the fields for a given SObjectType, it may not find a valid match because the wrong type is being used for the matching process. |
|
Hi again, I didn't entirely follow this:
But hopefully your own example will clarify things here.
You are correct, User has a field called BadgeText, which Name does not. While a specific Lead record's (i.e. a single DB row) Owner is either a USER or a GROUP, the Lead SObjectType's (i.e. the whole DB table) Owner lookup is related to the Name SObjectType, not User or Group. |
|
Ah, I follow a bit more now. You are using the UserSelector to ensure you select all interesting fields about the Lead's Owner. In fact, you need a NameSelector to configure the fields on the main QueryFactory instance. So I still feel the fix would be: |
# Conflicts: # fflib/src/classes/fflib_QueryFactory.cls
|
Separate note here, the SF org user creds seem to be out of sync which is causing the validation deploy to fail. @afawcett |
|
Hi David, For example, if we are looping the values for Lead.Owner.BadgeText we get an array of [Owner, BadgeText] for the specific Lead sObjectType. The first iteration through we get Owner. On the describe we get back the array: [ Group, User]. Let's set the SObjectType to Name because there is more than 1 item in the array. The next iteration we are describing Name to find BadgeText, however, BadgeText does not exist on Name. This results in the same error as arbitrarily setting the sObjectType to the first item in the array. This PR addresses the issue by passing in the sObjectType from the concrete selector that is using the QueryFactory instance. I understand that this creates a tighter coupling between the 2 classes. Perhaps, passing an instance of the concrete SObjectSelector to the QueryFactory constructor and then subsequently calling the SObjectSelector instance's getSObjectType method is a bit better?? Interested to hear any proposals for this issue. |
|
@squattingdog i think its best to avoid tighter coupling so passing in the sobjectype feels fine to me. @dfruddffdc do you agree? |
|
@squattingdog i appreciate this is an old PR by now, wondered if you still had a few on my last question on the coupling? I think we are close to getting agreement and merging. |
|
@afawcett hi, sorry for the delay, I lost track of this over the holidays. Anyway, the current implementation of this PR passes the sObjectType. Are you saying that is the best option at this time? |
…pex-common into merge-from-fork-source
|
@squattingdog indeed it does! Sorry i misread your last para from an earlier comment. @agarciaffdc this is good in my view to merge. |
| this.setMessage( 'Invalid field \''+fieldName+'\' for object \''+objectType+'\'' ); | ||
| } | ||
| } | ||
| }public class InvalidRelationshipException extends Exception{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@squattingdog sorry for asking this when your PR is opened since so long ago. But could you clean format a bit? This class should be at least one line below the } And we usually add open brace below the line instead of beside. For instance:
}public class InvalidRelationshipException extends Exception{
should be:
}
public class InvalidRelationshipException extends Exception
{
Don't worry about the code it is not yours. We will merge it as soon as you format it a bit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yup, not sure how that happened...addressing those.
|
@dbtavernerffdc FYI |
|
FYI, there still seems to be an issue with the creds being used for the Travis CI builds. /home/travis/build/financialforcedev/fflib-apex-common/lib/exec_anon.xml:147: INVALID_LOGIN: Invalid username, password, security token; or user locked out. |
|
Thanks for the hands up @squattingdog We will work around it in order to be able to merge it without Travis confirmation on your fork. Then Travis works properly on the lib repo |
| } | ||
| } | ||
|
|
||
| public class InvalidRelationshipException extends Exception |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't find anywhere else in this pull request where this exception is referenced. Perhaps it should be thrown if the passed in object type is not in the reference to list.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good one!! @squattingdog could you check this one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@squattingdog once you check this one we will merge it quickly.
| if(relatedObjs.size() > 1) { | ||
| String relatedSObjectName = fflib_SObjectDescribe.getDescribe(relatedSObjectType).getDescribe().getName(); | ||
| for(Schema.sObjectType sot : relatedObjs) { | ||
| if(fflib_SObjectDescribe.getDescribe(sot).getDescribe().getName() == relatedSObjectName){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Out of interest why are we comparing the names and not just the sobject type objects?
|
@afawcett @ImJohnMDaniel can this be looked at? This is a pretty significant issue for any field (and increasing with the increase of polymorphic fields added with Industry clouds) that can have multiple objects as a related object. |
…ans up the couple of issues outlined on the prior PR comment thread (removes unused Exception and uses the SObjectType for the comparison vs. the String name)
daveespo
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewable status: 0 of 3 files reviewed, 4 unresolved discussions (waiting on @agarciaffdc, @daveespo, @dbtavernerffdc, and @squattingdog)
a discussion (no related file):
I created a new PR for this change request over in #402 since this PR is against the old mdapi format project structure and has conflicts that are too hard to resolve
Additionally, I addressed several comment threads below in that other branch so I'm marking them as resolved here and closing this PR without merging it.
fflib/src/classes/fflib_QueryFactory.cls, line 118 at r4 (raw file):
Previously, dbtavernerffdc wrote…
Out of interest why are we comparing the names and not just the sobject type objects?
I agree -- I changed this to compare the SObjectType and it properly compiled and all tests passed
fflib/src/classes/fflib_QueryFactory.cls, line 794 at r4 (raw file):
Previously, agarciaffdc wrote…
@squattingdog once you check this one we will merge it quickly.
This Exception seems to be unused so I removed it from the code base
Redux of PR #137 - rebased against latest master and merge conflicts resolved along with the two outstanding comments on the prior PR
When a polymorphic field is being used and the type chosen does not match the sObjectType set in the selector, the configureQueryFactoryFields method will fail.
Add the ability to set the sObjectType when validating the field exists on the object. Use the type set in the selector to determine which polymorphic type to use.
Example: Lead.Owner.UserRoleID
Owner is GROUP | USER
Group is the first type returned and does not contain UserRoleID. The method then fails when the getField result is null.
This change is