0

I am facing Mixed DML exception error in our apex class for JIT provisioning for community user.

//This class provides logic for inbound just-in-time provisioning of single sign-on users in your Salesforce organization.
global class ssohandler implements Auth.SamlJitHandler {

    public string result {get; set;}
    public string debug {get; set;}
    public list<string> emails = new list<string>{'sebastian_page@blackbelthelp.com'};
    //JIT Handler Exception
    private class JitException extends Exception {}

    //Handle User
    private void handleUser(boolean create, User u, Map < String, String > attributes,
        String federationIdentifier, boolean isStandard) {
        system.debug('manual class');
        system.debug('federationIdentifier '+federationIdentifier);
        system.debug('user fed id '+attributes.get('User.FederationIdentifier'));
        if (create && attributes.containsKey('User.FederationIdentifier') && attributes.get('User.FederationIdentifier') != null) {
            u.FederationIdentifier = attributes.get('User.FederationIdentifier');
            federationIdentifier = attributes.get('User.FederationIdentifier');
        }
        else {
            u.FederationIdentifier = federationIdentifier;
        }
        Profile p;
        if (attributes.containsKey('User.ProfileId')) {
            String profileId = attributes.get('User.ProfileId');
            if(profileId != null && profileId != '') {
                p = [SELECT Id FROM Profile WHERE Id =: profileId];
            }
            system.debug('profile1: '+p);
        }
        if(p == null) {
            p = [SELECT Id FROM Profile WHERE Name ='MidStateTC_Overage'];
        }
        system.debug('profile2: '+p);
        u.ProfileId = p.Id;
        List<Contact> cons = [Select ID, Email, FirstName, LastName from Contact where Email=:federationIdentifier];
        Contact c;
        if(cons != null && cons.size() > 0) {
            c = cons[0];
        }
        if(c != null) {
            u.ContactId = c.Id;
        }
        if (attributes.containsKey('User.FirstName')) {
            u.FirstName = attributes.get('User.FirstName');
        } else if (create && c != null) {
            u.FirstName = c.FirstName;
        }
        if (attributes.containsKey('LastName')) {
            u.FirstName = attributes.get('LastName');
        } else if (create && c != null) {
            u.LastName = c.LastName;
        }
        if(attributes.containsKey('User.Email')) {
            u.Email = attributes.get('User.Email');
        } else if (create && c != null) {
            u.Email = c.Email+'.temp';
        }
        if (attributes.containsKey('UserName')) {
            u.UserName = attributes.get('UserName');
        } else if (create && c != null) {
            u.UserName = c.Email;
        }
        if (attributes.containsKey('User.CommunityNickname')) {
            u.CommunityNickname = attributes.get('User.CommunityNickname');
        }
        if (attributes.containsKey('User.IsActive')) {
            String IsActiveVal = attributes.get('User.IsActive');
            u.IsActive = '1'.equals(IsActiveVal) || Boolean.valueOf(IsActiveVal);
        }
        String uid = UserInfo.getUserId();
        User currentUser = [SELECT LocaleSidKey, LanguageLocaleKey, TimeZoneSidKey, EmailEncodingKey FROM User WHERE Id =: uid];
        if (attributes.containsKey('User.LocaleSidKey')) {
            u.LocaleSidKey = attributes.get('User.LocaleSidKey');
        } else if (create) {
            u.LocaleSidKey = currentUser.LocaleSidKey;
        }
        if (attributes.containsKey('User.LanguageLocaleKey')) {
            u.LanguageLocaleKey = attributes.get('User.LanguageLocaleKey');
        } else if (create) {
            u.LanguageLocaleKey = currentUser.LanguageLocaleKey;
        }
        if (attributes.containsKey('User.Alias')) {
            u.Alias = attributes.get('User.Alias');
        } else if (create) {
            String alias = '';
            if (u.FirstName == null) {
                alias = u.LastName;
            } else {
                alias = u.FirstName.charAt(0) + u.LastName;
            }
            if (alias.length() > 5) {
                alias = alias.substring(0, 5);
            }
            u.Alias = alias;
        }
        if (attributes.containsKey('User.TimeZoneSidKey')) {
            u.TimeZoneSidKey = attributes.get('User.TimeZoneSidKey');
        } else if (create) {
            u.TimeZoneSidKey = currentUser.TimeZoneSidKey;
        }
        if (attributes.containsKey('User.EmailEncodingKey')) {
            u.EmailEncodingKey = attributes.get('User.EmailEncodingKey');
        } else if (create) {
            u.EmailEncodingKey = currentUser.EmailEncodingKey;
        }

        try { 
            if (!create) {
                //update(u); //GB 1/10/2019
                } 
            else {
                Insert u;
                }
                system.debug('success ' + u);
                result = 'Success';
                system.debug('result ' + result + ' ' + debug);
        }
        catch(Exception e) {
            result = 'Failure';
            debug += ' User record not inserted';
            system.debug('result ' + result + ' ' + debug + ' ' + e);
            Messaging.SingleEmailMessage email1 = new Messaging.SingleEmailMessage();
            email1.setToAddresses(emails);
            email1.setSubject('Error in JIT Provisioning failed to insert');
            email1.setHtmlBody('Debug: <br/>' + debug + ' <br/> u.federationIdentifier: ' + federationIdentifier + e.getMessage());
            Messaging.sendEmail(new Messaging.Email[]{email1});
        }
    }


    //Handle Contact
    private void handleContact(boolean create, String accId, User u, Map < String, String > attributes) {

        Contact c;
        boolean newContact = false;

        List < Contact > lstContact = new List < Contact > ();
        Set < String > setEmailIds = new Set < String > ();
        Map < String, Contact > mapContactDetails = new Map < String, Contact > ();

        String firstName = '';

        lstContact = [SELECT ID, Name, FirstName, Email,AccountId FROM Contact WHERE AccountId=:accId];

        for (Contact cn: lstContact) {
            setEmailIds.add(cn.Email);
            mapContactDetails.put(cn.Email, cn);
        }

        if (setEmailIds.contains(attributes.get('User.Email'))) {
           firstName = mapContactDetails.get(attributes.get('User.Email')).FirstName;
        }

        if (create) {
            if (attributes.containsKey('User.Contact')) {
                String contact = attributes.get('User.Contact');
                c = [SELECT Id, AccountId FROM Contact WHERE Id =: contact];
                u.ContactId = contact;
            } else {
                c = new Contact();
                newContact = true;
            }
        } else {
            if (attributes.containsKey('User.Contact')) {
                String contact = attributes.get('User.Contact');
                c = [SELECT Id, AccountId FROM Contact WHERE Id =: contact];
                if (u.ContactId != c.Id) {
                    throw new JitException('Cannot change User.ContactId');
                }
            } else {
                String contact = u.ContactId;
                c = [SELECT Id, AccountId FROM Contact WHERE Id =: contact];
            }
        }
        if (!newContact && c.AccountId != accId) {
            throw new JitException('Mismatched account: ' + c.AccountId + ', ' + accId);
        }




        if (attributes.containsKey('Contact.Email')) {
            c.Email = attributes.get('Contact.Email');
        }

        if (attributes.containsKey('FirstName')) {
            c.FirstName = attributes.get('FirstName');
        } else if (String.isNotBlank(firstName)) {
            c.FirstName = firstName;
        }
        if (attributes.containsKey('Contact.LastName')) {
            c.LastName = attributes.get('Contact.LastName');
        }


        if (newContact) {
            c.AccountId = accId;
            insert(c);
            u.ContactId = c.Id;
        } else {
            update(c);
        }

        if (setEmailIds.contains(attributes.get('User.Email'))) {
            delete mapContactDetails.get(attributes.get('User.Email'));
        }
    }

    public Id createAccount(){
        id ids='0014F000005jgFpQAI';

         return ids;
    }

//Handle JIT
    private void handleJit(boolean create, User u, Id samlSsoProviderId, Id communityId, Id portalId, 
        String federationIdentifier, Map < String, String > attributes, String assertion) {



        if (communityId != null || portalId != null) {
            Id accountIds=createAccount();
                 handleContact(create, accountIds, u, attributes);
                 handleUser(create, u, attributes, federationIdentifier, false);
              } 
            else {



            handleUser(create, u, attributes, federationIdentifier, true);
        }
    }

    //For New Users
    global User createUser(Id samlSsoProviderId, Id communityId, Id portalId, 
        String federationIdentifier, Map < String, String > attributes, String assertion) {
        User u = new User();
       handleJit(true, u, samlSsoProviderId, communityId, portalId, federationIdentifier, attributes, assertion);
         return u;
    }

    //For Existing Users
    global void updateUser(Id userId, Id samlSsoProviderId, Id communityId, Id portalId,
        String federationIdentifier, Map < String, String > attributes, String assertion) {
        User u = [SELECT Id, FirstName, ContactId FROM User WHERE Id =: userId];
        handleJit(false, u, samlSsoProviderId, communityId, portalId, federationIdentifier, attributes, assertion);
    }
}
0

From your code for handleJit

DML operations on certain sObjects, sometimes referred to as setup objects, can’t be mixed with DML on other sObjects in the same transaction. This restriction exists because some sObjects affect the user’s access to records in the org. You must insert or update these types of sObjects in a different transaction to prevent operations from happening with incorrect access-level permissions

You are creating contact first and then creating user. Which was fine, but you are altering isActive field of User.

You can update a user in a transaction with other sObjects in Apex code saved using Salesforce API version 15.0 and later if the following fields are not also updated:

  1. UserRoleId
  2. IsActive
  3. ForecastEnabled
  4. IsPortalEnabled
  5. Username
  6. ProfileId

Solution: Declare a Queable, and use it in handleJIT

Class ContactCreatorQueueable implements  Queueable{
        boolean create;
        String accId;
        User u;
        Map < String, String > attributes;

        public ContactCreatorQueueable(boolean create, String accId, User u, Map < String, String > attributes){
            this.create = create;
            this.accId = accId;
            this.u = u;
            this.attributes = attributes;
        }

        public void execute(QueueableContext qc){
            new ssohandler().handleContact(create,accId,u, attributes);
        }
    }

Calling it as

 private void handleJit(boolean create, User u, Id samlSsoProviderId, Id communityId, Id portalId,
            String federationIdentifier, Map < String, String > attributes, String assertion) {



        if (communityId != null || portalId != null) {
            Id accountIds=createAccount();
            System.enqueueJob(new ContactCreatorQueueable(create, accountIds, u, attributes));
            handleUser(create, u, attributes, federationIdentifier, false);
        }
        else {



            handleUser(create, u, attributes, federationIdentifier, true);
        }
    }

Src: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_dml_non_mix_sobjects.htm

  • same problem we can not able to create user – Shiv Patel Feb 21 '19 at 21:47
  • can you check the flow, what all records you are creating in sequence? – Pranay Jaiswal Feb 21 '19 at 21:49
  • queueable is not solving the issue, since it is asynchronous and portal user expects contact id while inserting user record. – Arpit Sethi May 10 '19 at 8:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.