content: /*
   *  Copyright (C) 2015 Stichting Akvo (Akvo Foundation)
   *
   *  This file is part of Akvo FLOW.
   *
   *  Akvo FLOW is free software: you can redistribute it and modify it under the terms of
   *  the GNU Affero General Public License (AGPL) as published by the Free Software Foundation,
   *  either version 3 of the License or any later version.
   *
   *  Akvo FLOW is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
   *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
   *  See the GNU Affero General Public License included below for more details.
   *
   *  The full license text can also be seen at <http://www.gnu.org/licenses/agpl.html>.
   */
  
  package org.akvo.gae.remoteapi;
  
  import java.io.File;
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  
  import com.google.appengine.api.datastore.DatastoreService;
  import com.google.appengine.api.datastore.Entity;
  import com.google.appengine.api.datastore.FetchOptions;
  import com.google.appengine.api.datastore.Query;
  import com.google.appengine.api.datastore.Query.Filter;
  import com.google.appengine.api.datastore.Query.FilterOperator;
  import com.google.appengine.repackaged.org.apache.commons.io.FileUtils;
  
  import static org.akvo.gae.remoteapi.DataUtils.*;
  
  /**
   * Add new users to the datastore configuration. Takes in the path of a CSV file containing a list
   * of users to be added to the relevant instance. The CSV file format is
   * emailAddress,permissionList,superAdmin e.g. test@example.com,10,False
   */
  public class AddUsers implements Process {
  
      @Override
      public void execute(DatastoreService ds, String[] args) throws Exception {
          if (args.length == 0 || args[0].length() == 0) {
              System.err.println("Usage: " + RemoteAPI.class.getName()
                      + "AddUsers <appid> <username> <password> <users file path>");
              System.exit(1);
          }
  
          final File f = new File(args[0]);
          List<String> userLines = FileUtils.readLines(f);
          if (userLines.isEmpty()) {
              System.err.println("Empty file");
              return;
          }
  
          Map<String, Entity> users = createUserEntites(userLines);
          Map<String, Entity> existingUsers = retrieveExistingUsers(ds, users.keySet());
          mergeUserProperties(users, existingUsers);
  
          System.out.println("Upserting " + users.size() + " users");
          ds.put(users.values());
      }
  
      private Map<String, Entity> createUserEntites(List<String> userLines) {
          Map<String, Entity> users = new HashMap<String, Entity>();
  
          for (String line : userLines) {
              String[] userParts = line.split(",", 3);
              if (userParts.length < 3 || userParts[0].trim().length() == 0) {
                  System.out.println("Skipping user: " + line);
                  continue;
              }
              Entity user = new Entity(USER_KIND);
              user.setProperty(USER_EMAIL_FIELD, userParts[0].trim());
              user.setProperty(USER_PERMISSION_FIELD, userParts[1].trim());
              user.setProperty(USER_ROLE_FIELD, Boolean.valueOf(userParts[2].trim()));
              users.put(userParts[0].trim(), user);
  
          }
          return users;
      }
  
      private Map<String, Entity> retrieveExistingUsers(DatastoreService ds,
              Set<String> emailAddresses) {
          Map<String, Entity> existingUsers = new HashMap<String, Entity>();
  
          Filter emailAddressFilter = new Query.FilterPredicate(USER_EMAIL_FIELD, FilterOperator.IN,
                  new ArrayList<String>(emailAddresses));
  
          Query userQuery = new Query(USER_KIND).setFilter(emailAddressFilter);
          for (Entity user : ds.prepare(userQuery).asList(FetchOptions.Builder.withDefaults())) {
              String email = (String) user.getProperty(USER_EMAIL_FIELD);
              existingUsers.put(email, user);
          }
  
          return existingUsers;
      }
  
      private void mergeUserProperties(Map<String, Entity> users, Map<String, Entity> existingUsers) {
          for (String email : users.keySet()) {
              if (existingUsers.containsKey(email)) {
                  Entity existingUser = existingUsers.get(email);
                  Entity newUser = users.get(email);
  
                  existingUser.setProperty(USER_PERMISSION_FIELD,
                          newUser.getProperty(USER_PERMISSION_FIELD));
                  existingUser.setProperty(USER_ROLE_FIELD, newUser.getProperty(USER_ROLE_FIELD));
  
                  users.put(email, existingUser);
              }
          }
      }
  }
  