package org.karmaexchange.resources.derived; import static org.karmaexchange.util.OfyService.ofy; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.karmaexchange.auth.AuthProvider.UserInfo; import org.karmaexchange.auth.GlobalUidMapping; import org.karmaexchange.dao.BaseDao; import org.karmaexchange.dao.KeyWrapper; import org.karmaexchange.dao.Organization; import org.karmaexchange.dao.Organization.SourceOrganizationInfo; import org.karmaexchange.dao.OrganizationPrefs; import org.karmaexchange.dao.User; import org.karmaexchange.dao.derived.EventSourceInfo; import com.google.api.client.util.Maps; import com.google.common.collect.Lists; import com.googlecode.objectify.Key; import com.googlecode.objectify.VoidWork; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.ToString; @Data @NoArgsConstructor @EqualsAndHashCode(callSuper=true) @ToString(callSuper=true) public final class SourceUser extends BaseSourceUser { private boolean emailOptOut; private List<SourceOrganizationPrefs> orgPrefs; public SourceUser(User user, EventSourceInfo eventSourceBeingUpdated) { super(user); emailOptOut = user.isEmailOptOut(); orgPrefs = getListingOrgPrefs( user, eventSourceBeingUpdated.getOrgKey() ); } private List<SourceOrganizationPrefs> getListingOrgPrefs(User user, Key<Organization> listingOrgKey) { List<SourceOrganizationPrefs> filteredPrefs = Lists.newArrayList(); for (OrganizationPrefs orgPref : user.getOrgPrefs()) { if ( (orgPref.getSourceOrgInfo() != null) && KeyWrapper.toKey(orgPref.getSourceOrgInfo().getListingOrg()) .equals(listingOrgKey) ) { filteredPrefs.add( new SourceOrganizationPrefs(orgPref) ); } } return filteredPrefs; } public void upsert(EventSourceInfo listingOrgInfo) { validate(); Map<Key<Organization>, OrganizationPrefs> orgPrefsMap = toOrganizationPrefsMap(listingOrgInfo); GlobalUidMapping mapping = ofy().load().key(createGlobalUidMappingKey()).now(); if (mapping == null) { UserInfo userInfo = createUser(listingOrgInfo.getOrgKey()); User user = userInfo.getUser(); user.setEmailOptOut(emailOptOut); user.setOrgPrefs(new ArrayList<>(orgPrefsMap.values())); User.upsertNewUser(userInfo); } else { // We ignore the global emailOptOut field for existing users since the same // user can be registered with multiple orgs. Therefore only update the per org settings. ofy().transact( new UpdateUserPrefsTxn(mapping.getUserKey(), orgPrefsMap)); } } private Map<Key<Organization>, OrganizationPrefs> toOrganizationPrefsMap( EventSourceInfo listingOrgInfo) { Map<Key<Organization>, OrganizationPrefs> orgPrefsMap = Maps.newHashMap(); for (SourceOrganizationPrefs sourceOrgPrefs : orgPrefs) { OrganizationPrefs convOrgPrefs = sourceOrgPrefs.toOrganizationPrefs(listingOrgInfo); orgPrefsMap.put( KeyWrapper.toKey(convOrgPrefs.getOrg()), convOrgPrefs); } return orgPrefsMap; } private void validate() { for (SourceOrganizationPrefs orgPref : orgPrefs) { orgPref.validate(); } } @Data @NoArgsConstructor public static class SourceOrganizationPrefs { private SourceOrganization org; private boolean emailOptOut; public SourceOrganizationPrefs(OrganizationPrefs orgPrefs) { emailOptOut = orgPrefs.isEmailOptOut(); org = new SourceOrganization(orgPrefs.getSourceOrgInfo()); } public void validate() { org.validate(); } public OrganizationPrefs toOrganizationPrefs(EventSourceInfo listingOrgInfo) { Key<Organization> orgKey = org.lookupOrCreateOrg(listingOrgInfo); return new OrganizationPrefs( orgKey, new SourceOrganizationInfo(org.getId(), listingOrgInfo.getOrgKey()), emailOptOut); } } @Data @EqualsAndHashCode(callSuper=false) private static class UpdateUserPrefsTxn extends VoidWork { private final Key<User> userKey; private final Map<Key<Organization>, OrganizationPrefs> updatedOrgPrefsMap; public void vrun() { User user = ofy().load().key(userKey).now(); if (user == null) { // User has been deleted. return; } boolean updateRequired = false; Map<Key<Organization>, OrganizationPrefs> existingOrgPrefsMap = toOrgPrefsMap(user); for (Map.Entry<Key<Organization>, OrganizationPrefs> updatedOrgPrefEntry : updatedOrgPrefsMap.entrySet()) { OrganizationPrefs existingPrefs = existingOrgPrefsMap.get(updatedOrgPrefEntry.getKey()); OrganizationPrefs newPrefs = updatedOrgPrefEntry.getValue(); // Ignore identical updates. if ((existingPrefs != null) && existingPrefs.equals(newPrefs)) { continue; } // Insert / update required. existingOrgPrefsMap.put( updatedOrgPrefEntry.getKey(), updatedOrgPrefEntry.getValue()); updateRequired = true; } if (updateRequired) { user.setOrgPrefs(new ArrayList<>(existingOrgPrefsMap.values())); BaseDao.partialUpdate(user); } } private Map<Key<Organization>, OrganizationPrefs> toOrgPrefsMap(User user) { Map<Key<Organization>, OrganizationPrefs> orgPrefsMap = Maps.newHashMap(); for (OrganizationPrefs orgPrefsEl : user.getOrgPrefs()) { orgPrefsMap.put( KeyWrapper.toKey(orgPrefsEl.getOrg()), orgPrefsEl); } return orgPrefsMap; } } }