diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5304ebd
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+
+.idea/
+out/
+*.jar
diff --git a/intelli-qs-plugin.iml b/intelli-qs-plugin.iml
new file mode 100644
index 0000000..e025b20
--- /dev/null
+++ b/intelli-qs-plugin.iml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/META-INF/plugin.xml b/resources/META-INF/plugin.xml
new file mode 100644
index 0000000..ca37f41
--- /dev/null
+++ b/resources/META-INF/plugin.xml
@@ -0,0 +1,36 @@
+
+ com.qualisystems.pythonDriverPlugin
+ Quali Python Driver Uploader
+ 1.0
+ QualiSystems
+
+
+ Make sure you have a `deployment.xml` file present in your project root.
+
+ ]]>
+
+
+
+
+
+ com.intellij.modules.lang
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/resources/qs-icon.png b/resources/qs-icon.png
new file mode 100644
index 0000000..c652538
Binary files /dev/null and b/resources/qs-icon.png differ
diff --git a/src/com/qualisystems/pythonDriverPlugin/DriverPublisherSettings.java b/src/com/qualisystems/pythonDriverPlugin/DriverPublisherSettings.java
new file mode 100644
index 0000000..bc8c499
--- /dev/null
+++ b/src/com/qualisystems/pythonDriverPlugin/DriverPublisherSettings.java
@@ -0,0 +1,44 @@
+package com.qualisystems.pythonDriverPlugin;
+
+import org.apache.commons.lang.ArrayUtils;
+
+import java.util.Properties;
+
+public class DriverPublisherSettings {
+
+ String serverRootAddress;
+ int port;
+ String driverUniqueName;
+ String username;
+ String password;
+ String domain;
+ String[] fileFilters;
+
+ private static final String[] DefaultFileFilters = new String[] { ".idea", "deployment", "deployment.xml" };
+
+ public static DriverPublisherSettings fromProperties(Properties deploymentProperties) throws IllegalArgumentException {
+
+ if (!deploymentProperties.containsKey("driverUniqueName"))
+ throw new IllegalArgumentException("Missing `driverUniqueName` key in project's deployment.xml");
+
+ if (!deploymentProperties.containsKey("serverRootAddress"))
+ throw new IllegalArgumentException("Missing `serverRootAddress` key in project's deployment.xml");
+
+ DriverPublisherSettings settings = new DriverPublisherSettings();
+
+ settings.serverRootAddress = deploymentProperties.getProperty("serverRootAddress");
+ settings.port = Integer.parseInt(deploymentProperties.getProperty("port", "8029"));
+ settings.driverUniqueName = deploymentProperties.getProperty("driverUniqueName");
+ settings.username = deploymentProperties.getProperty("username", "admin");
+ settings.password = deploymentProperties.getProperty("password", "admin");
+ settings.domain = deploymentProperties.getProperty("domain", "Global");
+
+ String fileFiltersValue = deploymentProperties.getProperty("fileFilters", "");
+
+ String[] extraFilters = fileFiltersValue.isEmpty() ? new String[0] : fileFiltersValue.split(";");
+
+ settings.fileFilters = (String[])ArrayUtils.addAll(DefaultFileFilters, extraFilters);
+
+ return settings;
+ }
+}
diff --git a/src/com/qualisystems/pythonDriverPlugin/QualiPublishDriverAction.java b/src/com/qualisystems/pythonDriverPlugin/QualiPublishDriverAction.java
new file mode 100644
index 0000000..a6b22a9
--- /dev/null
+++ b/src/com/qualisystems/pythonDriverPlugin/QualiPublishDriverAction.java
@@ -0,0 +1,120 @@
+package com.qualisystems.pythonDriverPlugin;
+
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.progress.Task;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.Messages;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.*;
+import java.net.UnknownHostException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Properties;
+
+public class QualiPublishDriverAction extends AnAction {
+
+ public static final String DeploymentSettingsFileName = "deployment.xml";
+
+ @Override
+ public void actionPerformed(AnActionEvent anActionEvent) {
+
+ final Project project = anActionEvent.getData(CommonDataKeys.PROJECT);
+
+ if (project == null) return;
+
+ final File deploymentSettingsFile = new File(project.getBasePath(), DeploymentSettingsFileName);
+
+ if (!deploymentSettingsFile.exists()) {
+
+ Messages.showErrorDialog(
+ project,
+ "Could not find deployment.xml in the project folder, cannot upload driver.",
+ "Missing Deployment Configuration File");
+
+ return;
+ }
+
+ ProgressManager.getInstance().run(new Task.Backgroundable(project, "Publishing Python Driver on CloudShell") {
+
+ public Exception _exception;
+ public DriverPublisherSettings _settings;
+
+ @Override
+ public void onSuccess() {
+
+ if (_exception != null) {
+
+ _exception.printStackTrace();
+
+ if (_exception instanceof UnknownHostException)
+ Messages.showErrorDialog(
+ project,
+ "Failed uploading new driver file:\n Unknown Host",
+ "Publishing Python Driver on CloudShell");
+ else
+ Messages.showErrorDialog(
+ project,
+ "Failed uploading new driver file:\n" + _exception.toString(),
+ "Publishing Python Driver on CloudShell");
+
+ return;
+ }
+
+ if (_settings == null) return;
+
+ Messages.showInfoMessage(
+ project,
+ String.format("Successfully uploaded new driver file for driver `%s`", _settings.driverUniqueName),
+ "Publishing Python Driver on CloudShell");
+ }
+
+ @Override
+ public void run(@NotNull ProgressIndicator progressIndicator) {
+
+ try {
+
+ _settings = getDeploymentSettingsFromFile(deploymentSettingsFile);
+
+ ResourceManagementService resourceManagementService =
+ ResourceManagementService.OpenConnection(_settings.serverRootAddress, _settings.port, _settings.username, _settings.password, _settings.domain);
+
+ File zippedProjectFile = zipProjectFolder(project.getBasePath(), _settings);
+
+ resourceManagementService.updateDriver(_settings.driverUniqueName, zippedProjectFile);
+
+ } catch (Exception e) {
+
+ _exception = e;
+ }
+ }
+ });
+ }
+
+ private File zipProjectFolder(String directory, DriverPublisherSettings settings) throws IOException {
+
+ ZipHelper zipHelper = new ZipHelper(settings.fileFilters);
+
+ Path deploymentFilePath = Paths.get(directory, "deployment", settings.driverUniqueName + ".zip");
+
+ zipHelper.zipDir(directory, deploymentFilePath.toString());
+
+ return deploymentFilePath.toFile();
+ }
+
+ private DriverPublisherSettings getDeploymentSettingsFromFile(File deploymentSettingsFile) throws IOException {
+
+ Properties properties = new Properties();
+
+ properties.loadFromXML(Files.newInputStream(deploymentSettingsFile.toPath()));
+
+ DriverPublisherSettings settings = DriverPublisherSettings.fromProperties(properties);
+
+ return settings;
+ }
+}
diff --git a/src/com/qualisystems/pythonDriverPlugin/ResourceManagementService.java b/src/com/qualisystems/pythonDriverPlugin/ResourceManagementService.java
new file mode 100644
index 0000000..efbc617
--- /dev/null
+++ b/src/com/qualisystems/pythonDriverPlugin/ResourceManagementService.java
@@ -0,0 +1,133 @@
+package com.qualisystems.pythonDriverPlugin;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.NodeList;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.InetAddress;
+import java.nio.file.Files;
+import java.util.Base64;
+
+public class ResourceManagementService {
+
+ private static final String TargetServerURLFormat = "http://%s:%s/%s";
+
+ private static final String LoginEndpointPath = "ResourceManagerApiService/Logon";
+ private static final String LogonRequestFormat =
+ "\n" +
+ "%s\n" +
+ "%s\n" +
+ "%s\n" +
+ "";
+
+ private static final String UpdateDriverEndpointPath = "ResourceManagerApiService/UpdateDriver";
+ private static final String UpdateDriverRequestFormat =
+ "\n" +
+ "%s\n" +
+ "%s\n" +
+ "%s\n" +
+ "";
+
+ public static ResourceManagementService OpenConnection(String serverAddress, int port, String username, String password, String domain) throws Exception {
+
+ ResourceManagementService resourceManagementService = new ResourceManagementService(serverAddress, port);
+
+ resourceManagementService.login(username, password, domain);
+
+ return resourceManagementService;
+ }
+
+ private void login(String username, String password, String domain) throws Exception {
+
+ String serverURL = String.format(TargetServerURLFormat, _serverAddress, _port, LoginEndpointPath);
+
+ Document doc = sendMessage(new URL(serverURL), String.format(LogonRequestFormat, username, password, domain));
+
+ NodeList tokenElement = doc.getElementsByTagName("Token");
+
+ if (tokenElement.getLength() != 1)
+ throw new Exception("No token element in logon response");
+
+ _authToken = tokenElement.item(0).getAttributes().getNamedItem("Token").getTextContent();
+ }
+
+ public void updateDriver(String driverName, File newDriverFile) throws Exception {
+
+ String base64DriverFile =
+ Base64.getEncoder().encodeToString(Files.readAllBytes(newDriverFile.toPath()));
+
+ String serverURL = String.format(TargetServerURLFormat, _serverAddress, _port, UpdateDriverEndpointPath);
+
+ sendMessage(new URL(serverURL), String.format(UpdateDriverRequestFormat, driverName, base64DriverFile, newDriverFile.getName()));
+ }
+
+ private Document sendMessage(URL requestURL, String message) throws Exception {
+
+ HttpURLConnection con = (HttpURLConnection) requestURL.openConnection();
+
+ con.setRequestMethod("POST");
+ con.setRequestProperty("DateTimeFormat", "MM/dd/yyyy HH:mm");
+ con.setRequestProperty("ClientTimeZoneId", "UTC");
+ con.setRequestProperty("Content-Type", "text/xml");
+ con.setRequestProperty("Accept", "*/*");
+ con.setRequestProperty("Authorization", String.format("MachineName=%s;Token=%s", _hostname, _authToken));
+ con.setRequestProperty("Host", _serverAddress + ":" + Integer.toString(_port));
+
+ con.setDoOutput(true);
+
+ DataOutputStream wr = new DataOutputStream(con.getOutputStream());
+
+ wr.writeBytes(message);
+
+ wr.flush();
+ wr.close();
+
+ int responseCode = con.getResponseCode();
+
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder builder = factory.newDocumentBuilder();
+
+ Document responseXml = builder.parse(con.getInputStream());
+
+ NodeList elements = responseXml.getElementsByTagName("Error");
+
+ if (elements.getLength() > 0)
+ throw new Exception(String.format("API Message: %s", elements.item(0).getTextContent()));
+
+ if (!isSuccessResponseCode(responseCode))
+ throw new Exception("Error making request, Response code: " + responseCode);
+
+ return responseXml;
+ }
+
+ private boolean isSuccessResponseCode(int responseCode) {
+ return responseCode >= 200 && responseCode < 300;
+ }
+
+ private final String _serverAddress;
+ private final String _hostname;
+ private final int _port;
+
+ private String _authToken;
+
+ private ResourceManagementService(String serverAddress, int port) {
+
+ _serverAddress = serverAddress;
+ _port = port;
+
+ String hostname;
+
+ try {
+ hostname = InetAddress.getLocalHost().getHostName();
+ } catch (Exception ex) {
+ hostname = "localhost";
+ }
+
+ _hostname = hostname;
+ }
+}
diff --git a/src/com/qualisystems/pythonDriverPlugin/ZipHelper.java b/src/com/qualisystems/pythonDriverPlugin/ZipHelper.java
new file mode 100644
index 0000000..e975995
--- /dev/null
+++ b/src/com/qualisystems/pythonDriverPlugin/ZipHelper.java
@@ -0,0 +1,99 @@
+package com.qualisystems.pythonDriverPlugin;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+class ZipHelper {
+
+ private final String[] _filters;
+
+ public ZipHelper(String... filters) {
+
+ if (filters == null)
+ filters = new String[0];
+
+ _filters = filters;
+ }
+
+ public void zipDir(String dirName, String nameZipFile) throws IOException {
+
+ ZipOutputStream zip = null;
+ FileOutputStream fW = null;
+
+ Files.createDirectories(Paths.get(new File(nameZipFile).getParent()));
+
+ fW = new FileOutputStream(nameZipFile);
+ zip = new ZipOutputStream(fW);
+
+ initialAddFolderToZip(dirName, zip);
+
+ zip.close();
+ fW.close();
+ }
+
+ private void initialAddFolderToZip(String srcFolder, ZipOutputStream zip) throws IOException {
+
+ File folder = new File(srcFolder);
+
+ for (String fileName : folder.list()) {
+ addFileToZip("", srcFolder + "/" + fileName, zip, false);
+ }
+ }
+
+ private void addFolderToZip(String path, String srcFolder, ZipOutputStream zip) throws IOException {
+ File folder = new File(srcFolder);
+ if (folder.list().length == 0) {
+ addFileToZip(path , srcFolder, zip, true);
+ }
+ else {
+ for (String fileName : folder.list()) {
+ if (path.equals("")) {
+ addFileToZip(folder.getName(), srcFolder + "/" + fileName, zip, false);
+ }
+ else {
+ addFileToZip(path + "/" + folder.getName(), srcFolder + "/" + fileName, zip, false);
+ }
+ }
+ }
+ }
+
+ private void addFileToZip(String path, String srcFile, ZipOutputStream zip, boolean flag) throws IOException {
+
+ File folder = new File(srcFile);
+
+ if (flag) {
+
+ zip.putNextEntry(new ZipEntry(path + "/" +folder.getName() + "/"));
+
+ } else {
+
+ String nameInZip = (path.isEmpty() ? path : path + "/") + folder.getName();
+
+ for (String filter : _filters)
+ if (nameInZip.equalsIgnoreCase(filter))
+ return;
+
+ if (folder.isDirectory()) {
+
+ addFolderToZip(path, srcFile, zip);
+
+ } else {
+
+ byte[] buf = new byte[1024];
+ int len;
+
+ FileInputStream in = new FileInputStream(srcFile);
+ zip.putNextEntry(new ZipEntry(nameInZip));
+
+ while ((len = in.read(buf)) > 0)
+ zip.write(buf, 0, len);
+ }
+ }
+ }
+}