All Downloads are FREE. Search and download functionalities are using the official Maven repository.

prerna.reactor.database.CommandReactor Maven / Gradle / Ivy

The newest version!
package prerna.reactor.database;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;

import org.apache.commons.io.FileUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import prerna.auth.AuthProvider;
import prerna.sablecc2.om.PixelDataType;
import prerna.sablecc2.om.PixelOperationType;
import prerna.sablecc2.om.ReactorKeysEnum;
import prerna.sablecc2.om.nounmeta.NounMetadata;
import prerna.util.CmdExecUtil;
import prerna.util.Constants;
import prerna.util.Utility;
import prerna.util.git.GitPushUtils;
import prerna.util.git.GitRepoUtils;
import prerna.util.git.reactors.GitBaseReactor;

public class CommandReactor extends GitBaseReactor {

	private static final Logger classLogger = LogManager.getLogger(CommandReactor.class);

	private static final String CLASS_NAME = CommandReactor.class.getName();

	static final Set approvedProdCommands = new HashSet(
			Arrays.asList("PULL", "CLONE", "RESET", "STATUS"));
	// takes in a the name and engine and mounts the engine assets as that variable
	// name in both python and R
	// I need to accomodate for when I should over ride
	// for instance a user could have saved a recipe with some mapping and then
	// later, they would like to use a different mapping

	public CommandReactor() {
		this.keysToGet = new String[] { ReactorKeysEnum.COMMAND.getKey() };
		this.keyRequired = new int[] { 1 };
	}

	@Override
	public NounMetadata execute() {
		String disable_terminal = Utility.getDIHelperProperty(Constants.DISABLE_TERMINAL);
		if (disable_terminal != null && !disable_terminal.isEmpty()) {
			if (Boolean.parseBoolean(disable_terminal)) {
				throw new IllegalArgumentException("Terminal and user code execution has been disabled.");
			}
		}

		// check if git is disabled
		String disable_git_terminal = Utility.getDIHelperProperty(Constants.DISABLE_GIT_TERMINAL);
		if (disable_git_terminal != null && !disable_git_terminal.isEmpty()) {
			if (Boolean.parseBoolean(disable_git_terminal)) {
				throw new IllegalArgumentException("Git terminal has been disabled.");
			}
		}

		String gitProvider = Utility.getDIHelperProperty(Constants.GIT_PROVIDER);
		if (gitProvider == null) {
			gitProvider = "";
		}

		organizeKeys();
		String command = keyValue.get(keysToGet[0]);
		CmdExecUtil util = this.insight.getCmdUtil();

		if (util == null) {
			return getError("No context is set - please use SetContext() to set context");
		}

		// uncomment this line to see it in action. We want to test it for .. etc.
		// before committing into play.
		// util = null;
		String git = "";
		String gitCommand = null;
		String preCloneMessage = null;
		String postCloneMessage = null;

		StringTokenizer commands = new StringTokenizer(command);
		if (commands.countTokens() >= 2) {
			git = commands.nextToken().trim();
			gitCommand = commands.nextToken().trim();
		}

		////////////////////////////////////////// PRE PROCESSING
		////////////////////////////////////////// //////////////////////////////////////////////

		// process push
		// try to see if this is a push
		if (git != null && git.equalsIgnoreCase("git") && gitCommand != null && gitCommand.equalsIgnoreCase("push")) {
			NounMetadata pushOutput = processPush(command, util.getWorkingDir());
			if (pushOutput != null) {
				return pushOutput;
			}
		}

		// pre processing for clone
		Boolean isCloneAllowed = null;
		if (git != null && git.equalsIgnoreCase("git") && gitCommand != null && gitCommand.equalsIgnoreCase("clone")) {
			isCloneAllowed = preProcessClone(command, util.getWorkingDir());

			// allow it but basically say we will blow your git folder away
			if (isCloneAllowed != null && !isCloneAllowed)
				preCloneMessage = "You are cloning into a folder that is already part of git. Tracking at this level will be disabled";
			// return NounMetadata.getErrorNounMessage("Clone is not allowed at this level
			// ");
		}

		// pre-processing for cd
		// basically we try to see if the cd dir being done exists
		// if so no issue ootherwise need to gitclone from the repository
		if (git != null && git.equalsIgnoreCase("cd") && gitCommand != null) {
			File file = new File(Utility.normalizePath(util.getWorkingDir()) + "/" + gitCommand);
			if (!file.exists()) {
				// clone the git repository
				cloneRepo(gitCommand, util.getWorkingDir());
			}
		}

		// pre-process mkdir to say you cannot create folders at main level
		if (git.equalsIgnoreCase("mkdir") && util.getWorkingDir().endsWith("app_root"))
			return NounMetadata.getErrorNounMessage("You cannot make directory in app root folder");

		// pre process commit
		// add user name and email
		if (git != null && git.equalsIgnoreCase("git") && gitCommand.equalsIgnoreCase("commit")) {
			// add the user name
			// git config user.name
			// git confir user.email
			// and user email
			String[] userEmail = this.insight.getUser().getUserCredential(AuthProvider.GITHUB);
			if (userEmail[0] == null) {
				// get it from the email
				userEmail[0] = userEmail[1].substring(0, userEmail[1].indexOf("@"));
			}
			this.insight.getCmdUtil().executeCommand("git config user.name " + userEmail[0]);
			this.insight.getCmdUtil().executeCommand("git config user.email " + userEmail[1]);
		}

		if (git != null && git.equalsIgnoreCase("git") && gitCommand.equalsIgnoreCase("config")) {
			// command should not be allowed..
			if (command.contains("global")) {
				return NounMetadata.getErrorNounMessage("Global config cannot be set in this environment");
			}
		}

		// check that it is only git pull or git clone in prod for CFG
		// for this, trusted repo and default branch must be limited
		if (git != null && git.equalsIgnoreCase("git")
				&& gitProvider.equalsIgnoreCase(AuthProvider.GITLAB.toString())) {
			String trustedRepo = Utility.getDIHelperProperty(Constants.GIT_TRUSTED_REPO);
			String defaultBranch = Utility.getDIHelperProperty(Constants.GIT_DEFAULT_BRANCH);

			if (trustedRepo != null && !trustedRepo.isEmpty()) {
				if (defaultBranch != null && !defaultBranch.isEmpty()) {
					if (!approvedProdCommands.contains(gitCommand.toUpperCase())) {
						return NounMetadata.getErrorNounMessage(
								"Only git clone, pull, status, reset are allowed in this environment");
					}
				}
			}
		}

		if (git != null && git.equalsIgnoreCase("git") && gitCommand.equalsIgnoreCase("pull")
				&& gitProvider.equalsIgnoreCase(AuthProvider.GITLAB.toString())) {
			String token = getToken();
			return GitPushUtils.pull(util.getWorkingDir(), token, AuthProvider.GITLAB);
			// return new NounMetadata("Git Pulled", PixelDataType.CONST_STRING,
			// PixelOperationType.HELP);

		}

		if (git != null && git.equalsIgnoreCase("git") && gitCommand.equalsIgnoreCase("checkout")
				&& gitProvider.equalsIgnoreCase(AuthProvider.GITLAB.toString())) {
			String token = getToken();
			String branch = commands.nextToken();

			return GitPushUtils.checkout(util.getWorkingDir(), branch, token, AuthProvider.GITLAB);
			// return new NounMetadata("Checked out " + branch, PixelDataType.CONST_STRING,
			// PixelOperationType.HELP);

		}

		if (git != null && git.equalsIgnoreCase("git") && gitCommand.equalsIgnoreCase("clone")
				&& gitProvider.equalsIgnoreCase(AuthProvider.GITLAB.toString())) {
			String token = getToken();
			String repo = commands.nextToken();

			return GitPushUtils.clone(util.getWorkingDir(), repo, token, AuthProvider.GITLAB);
			// return new NounMetadata("Cloned " + repo, PixelDataType.CONST_STRING,
			// PixelOperationType.HELP);
		}

		String output = util.executeCommand(command);

		////////////////////////////////////////// 
		// POST PROCESSING
		////////////////////////////////////////// 

		// post processing
		if (git != null && git.equalsIgnoreCase("git") && gitCommand != null && gitCommand.equalsIgnoreCase("clone")) {
			// try to see if this is a clone if so add it to the clone properties
			postProcessClone(command, util.getWorkingDir(), isCloneAllowed);
			postCloneMessage = "If this is a java project, please make sure to adjust the target directory (XML Element build/directory) to ${classesDir}";
		}

		if ((command.startsWith("dir") || command.startsWith("ls"))) {
			// try to see if this is a clone if so add it to the clone properties
			String dir = util.getWorkingDir();
			// if(gitCommand != null) - this will break for ls -l
			// dir = gitCommand;
			output = postProcessDir(command, dir, output);
		}
		if (preCloneMessage != null)
			output = preCloneMessage + "\n" + output;

		if (postCloneMessage != null)
			output = output + "\n" + postCloneMessage;

		return new NounMetadata(output, PixelDataType.CONST_STRING, PixelOperationType.HELP);
	}


	/**
	 * @param command
	 * @param workingDir
	 * @return
	 */
	private NounMetadata processPush(String command, String workingDir) {
		StringTokenizer commands = new StringTokenizer(command);
		if (commands.countTokens() >= 2) {
			String gitCommand = commands.nextToken().trim();
			String push = commands.nextToken().trim();

			if (gitCommand.equalsIgnoreCase("git") && push.equalsIgnoreCase("push")) {

				// TODO Kunal - This is where I can add the limitations on where you can push to
				// check should be if its not master, its probably okay

				String remoteName = "origin";
				if (commands.hasMoreTokens())
					remoteName = commands.nextToken();

				String branch = "master";
				if (commands.hasMoreTokens())
					branch = commands.nextToken();

				// need to process this further
				// typically git push origin master

				// get the oauth token
				String token = getToken();

				// do a quick check to see if the remote is
				String url = GitRepoUtils.getConfigRemoteURL(workingDir, remoteName);
				// && url.contains("github")
				if (url != null) // need something to say these are Oauth2'able
				{
					GitPushUtils.push(workingDir, remoteName, branch, token);
					return new NounMetadata("Pushing Git", PixelDataType.CONST_STRING, PixelOperationType.HELP);
				}
			}
		}
		return null;
	}

	/**
	 * @param command
	 * @param workingDir
	 * @param cloneAllowed
	 */
	private void postProcessClone(String command, String workingDir, boolean cloneAllowed) {
		StringTokenizer commands = new StringTokenizer(command);
		if (commands.countTokens() >= 2) {
			String gitCommand = commands.nextToken().trim();
			String push = commands.nextToken().trim();

			if (gitCommand.equalsIgnoreCase("git") && push.equalsIgnoreCase("clone")) {
				String repoURL = null;
				if (commands.hasMoreTokens())
					repoURL = commands.nextToken();

				String dirName = Utility.getInstanceName(repoURL);

				// see if this directory exists in base folder
				String appBaseFolder = Utility.normalizePath(workingDir);
				if (cloneAllowed && appBaseFolder.endsWith("app_root") && new File(appBaseFolder + "/version").exists()
						&& new File(appBaseFolder + "/" + dirName).exists()) {
					// we are in the right location process now
					// add this to the properties
					// get the root
					FileInputStream fis = null;
					FileOutputStream fos = null;
					try {
						File repoFile = new File(appBaseFolder + "/version/repoList.txt");
						if (!repoFile.exists())
							repoFile.createNewFile();

						Properties prop = new Properties();
						fis = new FileInputStream(repoFile);

						prop.load(fis);
						prop.put(dirName, repoURL);
						fos = new FileOutputStream(repoFile);
						prop.store(fos, "Updating");

						// need to commit this file
						// cd into the version
						// git add *
						// git commit -m "adding repos"

						this.insight.getCmdUtil().executeCommand("cd version");
						this.insight.getCmdUtil().executeCommand("git add *");
						this.insight.getCmdUtil().executeCommand("git commit -m \"adding repos\" "); // dont know if we
						// need to add
						// the author
						// here else it
						// complains on
						// config

					} catch (FileNotFoundException e) {
						classLogger.error(Constants.STACKTRACE, e);
					} catch (IOException e) {
						classLogger.error(Constants.STACKTRACE, e);
					} finally {
						try {
							if (fis != null)
								fis.close();
						} catch (IOException e) {
							classLogger.error(Constants.STACKTRACE, e);
						}
						try {
							if (fos != null)
								fos.close();
						} catch (IOException e) {
							classLogger.error(Constants.STACKTRACE, e);
						}
					}
				}

				else if (!cloneAllowed) {
					File gitFolder = new File(Utility.normalizePath(workingDir) + File.separator + dirName + File.separator + ".git");
					if (gitFolder.exists()) {
						try {
							FileUtils.deleteDirectory(gitFolder);
						} catch (IOException e) {
							classLogger.error(Constants.STACKTRACE, e);
						}
					}
				}
			}
		}
	}

	/**
	 * @param command
	 * @param workingDir
	 * @return
	 */
	private Boolean preProcessClone(String command, String workingDir) {
		StringTokenizer commands = new StringTokenizer(command);
		if (commands.countTokens() >= 2) {
			String gitCommand = commands.nextToken().trim();
			String push = commands.nextToken().trim();

			if (gitCommand.equalsIgnoreCase("git") && push.equalsIgnoreCase("clone")) {
				String repoURL = null;
				if (commands.hasMoreTokens())
					repoURL = commands.nextToken();

				String dirName = Utility.getInstanceName(repoURL);

				// see if this directory exists in base folder
				String appBaseFolder = workingDir;
				if (appBaseFolder.endsWith("app_root") && new File(appBaseFolder + "/version").exists()) {
					// we are in the right location process now
					// add this to the properties
					return true;
				} else
					return false;
			}
		}
		return null;
	}

	/**
	 * @param repoName
	 * @param workingDir
	 */
	private void cloneRepo(String repoName, String workingDir) {
		FileInputStream fis = null;
		try {
			File repoFile = new File(Utility.normalizePath(workingDir) + "/version/repoList.txt");

			if (repoFile.exists()) {
				Properties prop = new Properties();
				fis = new FileInputStream(repoFile);
				prop.load(fis);

				String url = prop.getProperty(repoName);
				
				insight.getCmdUtil().executeCommand("git clone " + url);
			}
		} catch (FileNotFoundException e) {
			classLogger.error(Constants.STACKTRACE, e);
		} catch (IOException e) {
			classLogger.error(Constants.STACKTRACE, e);
		} finally {
			try {
				if (fis != null)
					fis.close();
			} catch (IOException e) {
				classLogger.error(Constants.STACKTRACE, e);
			}
		}
	}

	/**
	 * @param repoName
	 * @param workingDir
	 * @param output
	 * @return
	 */
	private String postProcessDir(String repoName, String workingDir, String output) {
		String newOutput = output;
		File repoFile = new File(Utility.normalizePath(workingDir) + "/version/repoList.txt");
		if (repoFile.exists()) {
			Properties prop = new Properties();
			FileInputStream fis = null;
			try {
				fis = new FileInputStream(repoFile);
				prop.load(fis);

				String repos = "While the directories are not shown, Following Repos are available:";
				Enumeration keys = prop.keys();
				while (keys.hasMoreElements())
					repos = repos + "   " + keys.nextElement();

				repos = repos + "\n"
						+ "You can cd into any of these dirs and when you do the git clone will be invoked at this level automatically ";
				repos = repos + "\n\n" + "Version is SEMOSS's default git repository.";
				newOutput = newOutput + "\n" + repos;
			} catch (FileNotFoundException e) {
				classLogger.error(Constants.STACKTRACE, e);
			} catch (IOException e) {
				classLogger.error(Constants.STACKTRACE, e);
			} finally {
				try {
					if (fis != null)
						fis.close();
				} catch (IOException e) {
					classLogger.error(Constants.STACKTRACE, e);
				}
			}
		}
		return newOutput;
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy