• Tim May 12
  • NEWBIE
  • 0 Points
  • Member since 2018

  • Chatter
    Feed
  • 0
    Best Answers
  • 0
    Likes Received
  • 0
    Likes Given
  • 2
    Questions
  • 2
    Replies
Hello,

Before starting, I know about DML operations and the order they should be performed in for test cases. Please do not comment on that. These test cases succeed and have been succeeding in partial and production for over 2 years. 

We recently enabled the Office 365 Outlook Configuration and we started getting test cases failing in production.

Here is the error that we see:
first error: MIXED_DML_OPERATION, DML operation on setup object is not permitted after you have updated a non-setup object (or vice versa): Case, original object: S2XUserMap: []

We saw this was once an issue in Winter'18 but has (apparently) been fixed.

https://trailblazer.salesforce.com/issues_view?id=a1p3A0000008gVyQAI

I think my next step is to go through all of the test cases and use a user and profile that is not mapped in the Outlook Office 365 config. These would be used in just the test case execution. 

Our current test cases were written by persona/profile so that we would test specific areas that our code needed to run against. We do create users and use System.runAs() for that test. 

Does anyone else have this happening to them also? Or did this happen to anyone else?
Hello,

I have implemented a Configurable Self Registration Handler in a Community and used the Auth.ConfigurableSelfRegHandler to extend how the User / Contact is created.

I want the Contact that is made to have a particular RecordType. I updated the method generateContact() to either look for a Contact or create a new Contact. I started with the example code that is created when you enable a handler in the Community.

Here is the error I am getting:

Update failed. First exception on row 0 with id <ID>; first error: MIXED_DML_OPERATION, DML operation on setup object is not permitted after you have updated a non-setup object (or vice versa): User, original object: Contact: []

I know the User object is a setup object but I figured Site.createExternalUser() would be able to bypass that. 

Thanks for the assistance, code is below. 
 
global class AutocreatedConfigSelfReg implements Auth.ConfigurableSelfRegHandler {

    private final Long CURRENT_TIME = Datetime.now().getTime();
    private final String[] UPPERCASE_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
    private final String[] LOWERCASE_CHARS = 'abcdefghijklmnopqrstuvwxyz'.split('');
    private final String[] NUMBER_CHARS = '1234567890'.split('');
    private final String[] SPECIAL_CHARS = '!#$%-_=+<>'.split('');

    // This method is called once after verification (if any was configured).
    // This method should create a user and insert it.
    // Password can be null.
    // Return null or throw an exception to fail creation.
    global Id createUser(Id accountId, Id profileId, Map<SObjectField, String> registrationAttributes, String password) {
        User u = new User();
        u.ProfileId = profileId;
        for (SObjectField field : registrationAttributes.keySet()) {
            String value = registrationAttributes.get(field);
            u.put(field, value);
        }

        u = handleUnsetRequiredFields(u);
        Contact cnct = generateContact(u, accountId);
        if (cnct.Id != null) {
            u.ContactId = cnct.Id;
        }
        if (String.isBlank(password)) {
            password = generateRandomPassword();
        }
        Site.validatePassword(u, password, password);
        if (u.contactId == null) {
            return Site.createExternalUser(u, accountId, password, True);
        }
        u.languagelocalekey = UserInfo.getLocale();
        u.localesidkey = UserInfo.getLocale();
        u.emailEncodingKey = 'UTF-8';
        u.timeZoneSidKey = UserInfo.getTimezone().getID();
        insert u;
        System.setPassword(u.Id, password);
        return u.id;
    }
    // Method to autogenerate a password if one isn't passed in.
    // By setting a password for a user, we won't send a 
    // welcome email to set the password.
    private String generateRandomPassword() {
        String[] characters = new List<String>(UPPERCASE_CHARS);
        characters.addAll(LOWERCASE_CHARS);
        characters.addAll(NUMBER_CHARS);
        characters.addAll(SPECIAL_CHARS);
        String newPassword = '';
        Boolean needsUpper = true, needsLower = true, needsNumber = true, needsSpecial = true;
        while (newPassword.length() < 50) {
            Integer randomInt = generateRandomInt(characters.size());
            String c = characters[randomInt];
            if (needsUpper && c.isAllUpperCase()) {
                needsUpper = false;
            } else if (needsLower && c.isAllLowerCase()) {
                needsLower = false;
            } else if (needsNumber && c.isNumeric()) {
                needsNumber = false;
            } else if (needsSpecial && !c.isAlphanumeric()) {
                needsSpecial = false;
            }
            newPassword += c; 
        }
        newPassword = addMissingPasswordRequirements(newPassword, needsLower, needsUpper, needsNumber, needsSpecial);
        return newPassword;
    }

    private String addMissingPasswordRequirements(String password, Boolean addLowerCase, Boolean addUpperCase, Boolean addNumber, Boolean addSpecial) {
        if (addLowerCase) {
            password += LOWERCASE_CHARS[generateRandomInt(LOWERCASE_CHARS.size())];
        }
        if (addUpperCase) {
            password += UPPERCASE_CHARS[generateRandomInt(UPPERCASE_CHARS.size())];
        }
        if (addNumber) {
            password += NUMBER_CHARS[generateRandomInt(NUMBER_CHARS.size())];
        }
        if (addSpecial) {
            password += SPECIAL_CHARS[generateRandomInt(SPECIAL_CHARS.size())];
        }
        return password;
    }
   // Generates a random number from 0 up to, but not including, max.
    private Integer generateRandomInt(Integer max) {
        return Math.mod(Math.abs(Crypto.getRandomInteger()), max);
    }

    // Loops over required fields that were not passed in to 
    // set to some default value.
    private User handleUnsetRequiredFields(User u) {
        if (String.isBlank(u.LastName)){
            u.LastName = generateLastName();
        }
        if (String.isBlank(u.Username)) {
            u.Username = generateUsername();
        }
        if (String.isBlank(u.Email)) {
            u.Email = generateEmail();
        }
        if (String.isBlank(u.Alias)) {
            u.Alias = generateAlias();
        }
        if (String.isBlank(u.CommunityNickname)) {
            u.CommunityNickname = generateCommunityNickname();
        }
        return u;
    }
    
    // Method to construct a contact for a user.
    private void generateContact(User u, Id accountId) {
    
        Contact cntct;
        
        // Contacts from the Community will have an additional contact record type
        List<RecordType> contactRecordTypes = [SELECT Id FROM RecordType
                                               WHERE SObjectType = 'Contact' and DeveloperName = 'Community_Contact'];

        // First step, look to see if there is a Contact already
        List<Contact> contactCheck = [SELECT Id, FirstName, LastName, AccountId
                                      FROM Contact WHERE Email = :u.Email];

        // There is one and only one Contact that we found, so return it.
        if (contactCheck.isEmpty() == false && contactCheck.size() == 1) {

            System.debug('Found an existing Contact based on Email Address');

            // Update the contact record with the information from the registration form
            // but make sure to not adjust the Account Id for security reasons.
            cntct = contactCheck.get(0);
            cntct.RecordTypeId = contactRecordTypes.get(0).Id;
            cntct.Active_Contact__c = true;
            cntct.LeadSource = 'Salesforce Community';
            cntct.FirstName = u.FirstName;
            cntct.LastName = u.LastName;
            update cntct;
        
        } else {
        
            // There was no Contact record found, so we need to make one
            // and put it in the right location.

            System.debug('Creating a new Contact');
            cntct.RecordTypeId = contactRecordTypes.get(0).Id;
            cntct.AccountId = accountId;
            cntct.Active_Contact__c = true;
            cntct.LeadSource = 'Salesforce Community';
            cntct.FirstName = u.FirstName;
            cntct.LastName = u.LastName;
            cntct.Email = u.Email;
            insert cntct;

        }

        System.debug('cntct: ' + cntct);
        return cntct;

    }
    // Default implementation to try to provide uniqueness.
    private String generateAlias() {
        String timeString = String.valueOf(CURRENT_TIME);
        return timeString.substring(timeString.length() - 8);
    }
    // Default implementation to try to provide uniqueness.
    private String generateLastName() {
        return 'ExternalUser' + CURRENT_TIME;
    }
    // Default implementation to try to provide uniqueness.
    private String generateUsername() {
        return 'externaluser' + CURRENT_TIME + '@company.com';
    }
    // Default implementation to try to provide uniqueness.
    private String generateEmail() {
        return 'externaluser' + CURRENT_TIME + '@company.com';
    }
    // Default implementation to try to provide uniqueness.
    private String generateCommunityNickname() {
        return 'ExternalUser' + CURRENT_TIME;
    }
}

 
Hello,

Before starting, I know about DML operations and the order they should be performed in for test cases. Please do not comment on that. These test cases succeed and have been succeeding in partial and production for over 2 years. 

We recently enabled the Office 365 Outlook Configuration and we started getting test cases failing in production.

Here is the error that we see:
first error: MIXED_DML_OPERATION, DML operation on setup object is not permitted after you have updated a non-setup object (or vice versa): Case, original object: S2XUserMap: []

We saw this was once an issue in Winter'18 but has (apparently) been fixed.

https://trailblazer.salesforce.com/issues_view?id=a1p3A0000008gVyQAI

I think my next step is to go through all of the test cases and use a user and profile that is not mapped in the Outlook Office 365 config. These would be used in just the test case execution. 

Our current test cases were written by persona/profile so that we would test specific areas that our code needed to run against. We do create users and use System.runAs() for that test. 

Does anyone else have this happening to them also? Or did this happen to anyone else?
Can anyone explain what the body attribute is used for.... I want to decrease the size of the FileCard , it is now 20rem and I am not able to change it to 10rem;