PageRenderTime 56ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/cli/src/main/java/com/gooddata/processor/CreateZendeskV3Projects.java

https://github.com/chrbayer84/GoodData-CL
Java | 368 lines | 263 code | 38 blank | 67 comment | 45 complexity | a40cd836785d48b1d502964d37850e47 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. /*
  2. * Copyright (c) 2009, GoodData Corporation. All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without modification, are permitted provided
  5. * that the following conditions are met:
  6. *
  7. * * Redistributions of source code must retain the above copyright notice, this list of conditions and
  8. * the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
  10. * and the following disclaimer in the documentation and/or other materials provided with the distribution.
  11. * * Neither the name of the GoodData Corporation nor the names of its contributors may be used to endorse
  12. * or promote products derived from this software without specific prior written permission.
  13. *
  14. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
  15. * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
  16. * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
  17. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  18. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  19. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  20. * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  21. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  22. */
  23. package com.gooddata.processor;
  24. import com.gooddata.Constants;
  25. import com.gooddata.exception.*;
  26. import com.gooddata.integration.model.Project;
  27. import com.gooddata.integration.rest.GdcRESTApiWrapper;
  28. import com.gooddata.integration.rest.configuration.NamePasswordConfiguration;
  29. import com.gooddata.util.CSVReader;
  30. import com.gooddata.util.CSVWriter;
  31. import com.gooddata.util.FileUtil;
  32. import com.gooddata.util.StringUtil;
  33. import org.apache.commons.cli.*;
  34. import org.apache.log4j.Logger;
  35. import java.io.*;
  36. import java.sql.SQLException;
  37. import java.util.*;
  38. /**
  39. * Utility that creates a new Zendesk V3 project for every V1 Zendesk project.
  40. * The utility copies all users from the old V1 project to the new V3 project.
  41. * It needs to be executed under the bear@gooddata.com, as adding users to projects is only allowed to the admin of the
  42. * domain where the user has been created. We assume that all Zendesk users are in the default GoodData domain.
  43. *
  44. * @author Zdenek Svoboda <zd@gooddata.org>
  45. * @version 1.0
  46. */
  47. public class CreateZendeskV3Projects {
  48. private static Logger l = Logger.getLogger(CreateZendeskV3Projects.class);
  49. private boolean finishedSucessfuly = false;
  50. //Options data
  51. public static String[] CLI_PARAM_HELP = {"help", "h"};
  52. public static String[] CLI_PARAM_USERNAME = {"username", "u"};
  53. public static String[] CLI_PARAM_PASSWORD = {"password", "p"};
  54. public static String[] CLI_PARAM_HOST = {"host", "h"};
  55. public static String[] CLI_PARAM_TEMPLATE = {"template", "t"};
  56. public static String[] CLI_PARAM_VERSION = {"version", "v"};
  57. public static String[] CLI_PARAM_LIST = {"input", "i"};
  58. public static String[] CLI_PARAM_OUTPUT = {"output", "o"};
  59. public static String[] CLI_PARAM_TOKEN = {"token", "a"};
  60. // Command line options
  61. private static Options ops = new Options();
  62. public static Option[] mandatoryOptions = {
  63. new Option(CLI_PARAM_USERNAME[1], CLI_PARAM_USERNAME[0], true, "GoodData username"),
  64. new Option(CLI_PARAM_PASSWORD[1], CLI_PARAM_PASSWORD[0], true, "GoodData password"),
  65. new Option(CLI_PARAM_LIST[1], CLI_PARAM_LIST[0], true, "List of the V1 projects that needs to be converted (single column CSV with the V1 project hash)"),
  66. new Option(CLI_PARAM_OUTPUT[1], CLI_PARAM_OUTPUT[0], true, "List that associates the old V1 project and the new V3 project (two columns: [old hash,new hash])."),
  67. new Option(CLI_PARAM_TEMPLATE[1], CLI_PARAM_TEMPLATE[0], true, "The template to create the new V3 projects from")
  68. };
  69. public static Option[] optionalOptions = {
  70. new Option(CLI_PARAM_HOST[1], CLI_PARAM_HOST[0], true, "GoodData host (default secure.gooddata.com)"),
  71. new Option(CLI_PARAM_TOKEN[1], CLI_PARAM_TOKEN[0], true, "Create project access token.")
  72. };
  73. public static Option[] helpOptions = {
  74. new Option(CLI_PARAM_HELP[1], CLI_PARAM_HELP[0], false, "Print command reference"),
  75. new Option(CLI_PARAM_VERSION[1], CLI_PARAM_VERSION[0], false, "Prints the tool version.")
  76. };
  77. private CliParams cliParams = null;
  78. private ProcessingContext ctx = new ProcessingContext();
  79. public CreateZendeskV3Projects(CommandLine ln) {
  80. try {
  81. cliParams = parse(ln);
  82. String username = cliParams.get(CLI_PARAM_USERNAME[0]);
  83. String password = cliParams.get(CLI_PARAM_PASSWORD[0]);
  84. String input = cliParams.get(CLI_PARAM_LIST[0]);
  85. String output = cliParams.get(CLI_PARAM_OUTPUT[0]);
  86. String template = cliParams.get(CLI_PARAM_TEMPLATE[0]);
  87. String host = cliParams.get(CLI_PARAM_HOST[0]);
  88. String token = cliParams.get(CLI_PARAM_TOKEN[0]);
  89. NamePasswordConfiguration config = new NamePasswordConfiguration("https", host, username, password);
  90. cliParams.setHttpConfig(config);
  91. CSVReader reader = FileUtil.createUtf8CsvReader(new File(input));
  92. CSVWriter writer = FileUtil.createUtf8CsvWriter(new File(output));
  93. CSVWriter tool = FileUtil.createUtf8CsvWriter(new File(output+".tool"));
  94. tool.writeNext(new String[] {"login","project","role","project-state","user-state"});
  95. int rowCnt = 1;
  96. String[] row = reader.readNext();
  97. while(row != null && row.length > 0) {
  98. if(row.length > 1) {
  99. throw new InvalidArgumentException("The row "+rowCnt+" of the '"+input+"' contains more than one column!");
  100. }
  101. String oldProjectHash = row[0];
  102. String newProjectHash = "ERROR: Failed to access the corresponding V1 project.";
  103. try {
  104. newProjectHash = processProject(oldProjectHash, template, token, tool);
  105. }
  106. catch (GdcProjectAccessException e) {
  107. l.info("The project "+oldProjectHash+" either doesn't exist, is disabled, or can't be accessed by the user that invoked this tool.");
  108. }
  109. writer.writeNext(new String[] {oldProjectHash,newProjectHash});
  110. writer.flush();
  111. tool.flush();
  112. row = reader.readNext();
  113. rowCnt++;
  114. }
  115. writer.close();
  116. tool.close();
  117. reader.close();
  118. finishedSucessfuly = true;
  119. } catch (InterruptedException e) {
  120. l.error("Interrupted during project creation." + e.getMessage());
  121. l.debug(e);
  122. Throwable c = e.getCause();
  123. while (c != null) {
  124. l.debug("Caused by: ", c);
  125. c = c.getCause();
  126. }
  127. finishedSucessfuly = false;
  128. } catch (IOException e) {
  129. l.error("Encountered an IO problem. Please check that all files that you use in your command line arguments and commands exist." + e.getMessage());
  130. l.debug(e);
  131. Throwable c = e.getCause();
  132. while (c != null) {
  133. l.debug("Caused by: ", c);
  134. c = c.getCause();
  135. }
  136. finishedSucessfuly = false;
  137. }
  138. catch (HttpMethodException e) {
  139. l.debug("Error executing GoodData REST API: " + e);
  140. Throwable c = e.getCause();
  141. while (c != null) {
  142. l.debug("Caused by: ", c);
  143. c = c.getCause();
  144. }
  145. String msg = e.getMessage();
  146. String requestId = e.getRequestId();
  147. if (requestId != null) {
  148. msg += "\n\n" +
  149. "If you believe this is not your fault, good people from support\n" +
  150. "portal (http://support.gooddata.com) may help you.\n\n" +
  151. "Show them this error ID: " + requestId;
  152. }
  153. l.error(msg);
  154. finishedSucessfuly = false;
  155. } catch (GdcRestApiException e) {
  156. l.error("REST API invocation error: " + e.getMessage());
  157. l.debug(e, e);
  158. Throwable c = e.getCause();
  159. while (c != null) {
  160. if (c instanceof HttpMethodException) {
  161. HttpMethodException ex = (HttpMethodException) c;
  162. String msg = ex.getMessage();
  163. if (msg != null && msg.length() > 0 && msg.indexOf("/ldm/manage") > 0) {
  164. l.error("Error creating/updating logical data model (executing MAQL DDL).");
  165. if (msg.indexOf(".date") > 0) {
  166. l.error("Bad time dimension schemaReference.");
  167. } else {
  168. l.error("You are either trying to create a data object that already exists " +
  169. "(executing the same MAQL multiple times) or providing a wrong reference " +
  170. "or schemaReference in your XML configuration.");
  171. }
  172. }
  173. }
  174. l.debug("Caused by: ", c);
  175. c = c.getCause();
  176. }
  177. finishedSucessfuly = false;
  178. } catch (GdcException e) {
  179. l.error("Unrecognized error: " + e.getMessage());
  180. l.debug(e);
  181. Throwable c = e.getCause();
  182. while (c != null) {
  183. l.debug("Caused by: ", c);
  184. c = c.getCause();
  185. }
  186. finishedSucessfuly = false;
  187. } finally {
  188. /*
  189. if (cliParams != null)
  190. context.getRestApi(cliParams).logout();
  191. */
  192. }
  193. }
  194. /**
  195. * Creates a new V3 projects from the template identified by the templateUri for the V1 project that is passed in
  196. * the oldProjectHash parameter
  197. * Copies all users from the V1 to the V3 project with appropriate roles
  198. * @param oldProjectHash the old V1 project hash
  199. * @param templateUri the new V3 project template URI
  200. * @param token project creation token (redirects the new projects to the correct DWH server)
  201. * @return the new V3 project hash
  202. */
  203. private String processProject(String oldProjectHash, String templateUri, String token, CSVWriter tool) throws InterruptedException {
  204. Project project = ctx.getRestApi(cliParams).getProjectById(oldProjectHash);
  205. Map<String,GdcRESTApiWrapper.GdcUser> activeUsers = new HashMap<String,GdcRESTApiWrapper.GdcUser>();
  206. l.info("Getting users from project " + oldProjectHash);
  207. List<GdcRESTApiWrapper.GdcUser> users = ctx.getRestApi(cliParams).getProjectUsers(oldProjectHash, true);
  208. for(GdcRESTApiWrapper.GdcUser user : users) {
  209. activeUsers.put(user.getUri(), user);
  210. }
  211. l.info(users.size() + " users retrieved from project " + oldProjectHash);
  212. l.info("Getting roles from project " + oldProjectHash);
  213. List<GdcRESTApiWrapper.GdcRole> roles = ctx.getRestApi(cliParams).getProjectRoles(oldProjectHash);
  214. l.info(roles.size() + " roles retrieved from project " + oldProjectHash);
  215. String newName = project.getName()+" (new)";
  216. String newProjectHash = ctx.getRestApi(cliParams).createProject(StringUtil.toTitle(newName), StringUtil.toTitle(newName), templateUri, "Pg", token);
  217. checkProjectCreationStatus(newProjectHash, cliParams, ctx);
  218. l.info("New V3 project created: " + newProjectHash);
  219. for(GdcRESTApiWrapper.GdcRole role : roles) {
  220. l.info("Getting users from role " + role.getIdentifier());
  221. List<String> userUris = ctx.getRestApi(cliParams).getRoleUsers(role, true);
  222. l.info(userUris.size() + " users retrieved from role " + role.getIdentifier());
  223. for(String userUri : userUris) {
  224. GdcRESTApiWrapper.GdcUser user = activeUsers.get(userUri);
  225. if(user != null) {
  226. l.info("Adding user "+user.getLogin()+" to the new V3 project " + newProjectHash+ " with role "+role.getIdentifier());
  227. tool.writeNext(new String[] {user.getLogin(),newProjectHash,role.getIdentifier(),"ENABLED","ENABLED"});
  228. }
  229. else {
  230. l.info("Detected suspended user " + userUri);
  231. }
  232. }
  233. }
  234. return newProjectHash;
  235. }
  236. /**
  237. * Parse and validate the cli arguments
  238. *
  239. * @param ln parsed command line
  240. * @return parsed cli parameters wrapped in the CliParams
  241. * @throws com.gooddata.exception.InvalidArgumentException in case of nonexistent or incorrect cli args
  242. */
  243. protected CliParams parse(CommandLine ln) throws InvalidArgumentException {
  244. l.debug("Parsing cli " + ln);
  245. CliParams cp = new CliParams();
  246. if (cp.containsKey(CLI_PARAM_VERSION[0])) {
  247. l.info("GoodData CL version 1.2.67");
  248. System.exit(0);
  249. }
  250. if (ln.hasOption(CLI_PARAM_HELP[1])) {
  251. printHelp();
  252. }
  253. for (Option o : mandatoryOptions) {
  254. String name = o.getLongOpt();
  255. if (ln.hasOption(name))
  256. cp.put(name, ln.getOptionValue(name));
  257. else {
  258. l.info("Please specify the mandatory option "+name+"("+o.getOpt()+")");
  259. printHelp();
  260. System.exit(0);
  261. }
  262. }
  263. for (Option o : optionalOptions) {
  264. String name = o.getLongOpt();
  265. if (ln.hasOption(name))
  266. cp.put(name, ln.getOptionValue(name));
  267. }
  268. // use default host if there is no host in the CLI params
  269. if (!cp.containsKey(CLI_PARAM_HOST[0])) {
  270. cp.put(CLI_PARAM_HOST[0], Defaults.DEFAULT_HOST);
  271. }
  272. l.debug("Using host " + cp.get(CLI_PARAM_HOST[0]));
  273. return cp;
  274. }
  275. private void printHelp() {
  276. HelpFormatter formatter = new HelpFormatter();
  277. formatter.printHelp("createZendeskProjects [<options> ...]", ops);
  278. }
  279. private static boolean checkJavaVersion() {
  280. String version = System.getProperty("java.version");
  281. if (version.startsWith("1.8") || version.startsWith("1.7") || version.startsWith("1.6") || version.startsWith("1.5"))
  282. return true;
  283. l.error("You're running Java " + version + ". Please use Java 1.5 or higher for running this tool. " +
  284. "Please refer to http://java.sun.com/javase/downloads/index.jsp for a more recent Java version.");
  285. throw new InternalErrorException("You're running Java " + version + ". Please use use Java 1.5 or higher for running this tool. " +
  286. "Please refer to http://java.sun.com/javase/downloads/index.jsp for a more recent Java version.");
  287. }
  288. /**
  289. * The main CLI processor
  290. *
  291. * @param args command line argument
  292. */
  293. public static void main(String[] args) {
  294. checkJavaVersion();
  295. for (Option o : mandatoryOptions)
  296. ops.addOption(o);
  297. for (Option o : optionalOptions)
  298. ops.addOption(o);
  299. for (Option o : helpOptions)
  300. ops.addOption(o);
  301. try {
  302. CommandLineParser parser = new GnuParser();
  303. CommandLine cmdline = parser.parse(ops, args);
  304. CreateZendeskV3Projects gdi = new CreateZendeskV3Projects(cmdline);
  305. if (!gdi.finishedSucessfuly) {
  306. System.exit(1);
  307. }
  308. } catch (org.apache.commons.cli.ParseException e) {
  309. l.error("Error parsing command line parameters: ", e);
  310. l.debug("Error parsing command line parameters", e);
  311. }
  312. }
  313. /**
  314. * Checks the project status. Waits till the status is LOADING
  315. *
  316. * @param projectId project ID
  317. * @param p cli parameters
  318. * @param ctx current context
  319. * @throws InterruptedException internal problem with making file writable
  320. */
  321. private void checkProjectCreationStatus(String projectId, CliParams p, ProcessingContext ctx) throws InterruptedException {
  322. l.debug("Checking project " + projectId + " loading status.");
  323. String status = "LOADING";
  324. while ("LOADING".equalsIgnoreCase(status)) {
  325. status = ctx.getRestApi(p).getProjectStatus(projectId);
  326. l.debug("Project " + projectId + " loading status = " + status);
  327. Thread.sleep(Constants.POLL_INTERVAL);
  328. }
  329. }
  330. }