From ac0a2eb251110c4ace054521d16f9072d7a36c1b Mon Sep 17 00:00:00 2001
From: Viktor Kovtun <viktor.kovtun@id.ethz.ch>
Date: Sun, 18 Aug 2019 11:52:23 +0200
Subject: [PATCH] SSDM-8405 Made the Zenodo exporting plugin do actual export.

---
 .../eln-lims/html/js/server/ServerFacade.js   |  45 ++++++
 .../ZenodoExport/ZenodoExportController.js    |   8 +-
 .../exports-api/exportsApi.py                 |  31 +++-
 .../rc-exports-api/rcExports.py               |  34 +----
 .../zenodo-exports-api/exportsApi.py          |   1 +
 .../zenodo-exports-api/plugin.properties      |   1 +
 .../zenodo-exports-api/zenodoExports.py       | 137 +++++++++++++++++-
 7 files changed, 214 insertions(+), 43 deletions(-)
 create mode 120000 openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/zenodo-exports-api/exportsApi.py

diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/server/ServerFacade.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/server/ServerFacade.js
index 54e465edf47..b80412313fe 100644
--- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/server/ServerFacade.js
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/server/ServerFacade.js
@@ -378,6 +378,51 @@ function ServerFacade(openbisServer) {
 			});
 	};
 
+    this.exportZenodo = function(entities, includeRoot, metadataOnly, userInformation, callbackFunction) {
+        this.asyncExportZenodo({
+            "method": "exportAll",
+            "includeRoot": includeRoot,
+            "entities": entities,
+            "metadataOnly": metadataOnly,
+            "userInformation": userInformation,
+            "originUrl": window.location.origin,
+            "sessionToken": this.openbisServer.getSession(),
+        }, callbackFunction, "zenodo-exports-api");
+    };
+
+	this.asyncExportZenodo = function(parameters, callbackFunction, serviceId) {
+		require(["as/dto/service/execute/ExecuteAggregationServiceOperation",
+				"as/dto/operation/AsynchronousOperationExecutionOptions", "as/dto/service/id/DssServicePermId",
+				"as/dto/datastore/id/DataStorePermId", "as/dto/service/execute/AggregationServiceExecutionOptions"],
+			function(ExecuteAggregationServiceOperation, AsynchronousOperationExecutionOptions, DssServicePermId, DataStorePermId,
+					 AggregationServiceExecutionOptions) {
+				var dataStoreCode = profile.getDefaultDataStoreCode();
+				var dataStoreId = new DataStorePermId(dataStoreCode);
+				var dssServicePermId = new DssServicePermId(serviceId, dataStoreId);
+				var options = new AggregationServiceExecutionOptions();
+
+				options.withParameter("sessionToken", parameters["sessionToken"]);
+
+				options.withParameter("entities", parameters["entities"]);
+				options.withParameter("includeRoot", parameters["includeRoot"]);
+				options.withParameter("metadataOnly", parameters["metadataOnly"]);
+				options.withParameter("method", parameters["method"]);
+				options.withParameter("originUrl", parameters["originUrl"]);
+				options.withParameter("submissionType", parameters["submissionType"]);
+				options.withParameter("submissionUrl", parameters["submissionUrl"]);
+				options.withParameter("entities", parameters["entities"]);
+				options.withParameter("userId", parameters["userInformation"]["id"]);
+				options.withParameter("userEmail", parameters["userInformation"]["email"]);
+				options.withParameter("userFirstName", parameters["userInformation"]["firstName"]);
+				options.withParameter("userLastName", parameters["userInformation"]["lastName"]);
+
+				var operation = new ExecuteAggregationServiceOperation(dssServicePermId, options);
+				mainController.openbisV3.executeOperations([operation], new AsynchronousOperationExecutionOptions()).done(function(results) {
+					callbackFunction(results.executionId.permId);
+				});
+			});
+	};
+
 	//
 	// Gets submission types
 	//
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ZenodoExport/ZenodoExportController.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ZenodoExport/ZenodoExportController.js
index c79e5e40812..99e24671fe9 100644
--- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ZenodoExport/ZenodoExportController.js
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ZenodoExport/ZenodoExportController.js
@@ -26,10 +26,6 @@ function ZenodoExportController(parentController) {
         var _this = this;
         var selectedNodes = $(exportModel.tree).fancytree('getTree').getSelectedNodes();
 
-        var selectedOption = exportView.$submissionTypeDropdown.find(":selected");
-        var submissionUrl = selectedOption.val();
-        var submissionType = selectedOption.text();
-
         var toExport = [];
         for (var eIdx = 0; eIdx < selectedNodes.length; eIdx++) {
             var node = selectedNodes[eIdx];
@@ -38,12 +34,10 @@ function ZenodoExportController(parentController) {
 
         if (toExport.length === 0) {
             Util.showInfo('First select something to export.');
-        } else if (!submissionUrl) {
-            Util.showInfo('First select submission type.');
         } else {
             Util.blockUI();
             this.getUserInformation(function(userInformation) {
-                mainController.serverFacade.exportRc(toExport, true, false, submissionUrl, submissionType, userInformation,
+                mainController.serverFacade.exportZenodo(toExport, true, false, userInformation,
                         function(operationExecutionPermId) {
                             _this.waitForOpExecutionResponse(operationExecutionPermId, function(error, result) {
                                 Util.unblockUI();
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/exports-api/exportsApi.py b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/exports-api/exportsApi.py
index 932c0fa3023..7af095aa70a 100644
--- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/exports-api/exportsApi.py
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/exports-api/exportsApi.py
@@ -645,4 +645,33 @@ def getConfigurationProperty(transaction, propertyName):
     try:
         return threadProperties.getProperty(propertyName);
     except:
-        return None
\ No newline at end of file
+        return None
+
+
+def generateZipFile(entities, params, tempDirPath, tempZipFilePath):
+    # Generates ZIP file with selected item for export
+
+    sessionToken = params.get('sessionToken')
+    includeRoot = params.get('includeRoot')
+
+    fos = None
+    zos = None
+    try:
+        fos = FileOutputStream(tempZipFilePath)
+        zos = ZipOutputStream(fos)
+
+        fileMetadata = generateFilesInZip(zos, entities, includeRoot, sessionToken, tempDirPath)
+    finally:
+        if zos is not None:
+            zos.close()
+        if fos is not None:
+            fos.close()
+
+    return fileMetadata
+
+
+def checkResponseStatus(response):
+    status = response.getStatus()
+    if status >= 300:
+        reason = response.getReason()
+        raise ValueError('Unsuccessful response from the server: %s %s' % (status, reason))
\ No newline at end of file
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/rc-exports-api/rcExports.py b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/rc-exports-api/rcExports.py
index 99f41ac9578..66e91990f86 100644
--- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/rc-exports-api/rcExports.py
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/rc-exports-api/rcExports.py
@@ -35,7 +35,8 @@ from org.eclipse.jetty.client.util import BasicAuthentication
 from org.eclipse.jetty.http import HttpMethod
 from org.eclipse.jetty.util.ssl import SslContextFactory
 
-from exportsApi import displayResult, findEntitiesToExport, validateDataSize, getConfigurationProperty, generateFilesInZip, addToZipFile, cleanUp
+from exportsApi import displayResult, findEntitiesToExport, validateDataSize, getConfigurationProperty, generateFilesInZip, addToZipFile, cleanUp, \
+    generateZipFile, checkResponseStatus
 
 operationLog = Logger.getLogger(str(LogCategory.OPERATION) + '.rcExports.py')
 
@@ -106,7 +107,7 @@ def export(entities, tr, params, userInformation):
     exportZipFilePath = exportDirPath + '.zip'
     exportZipFileName = exportDirName + '.zip'
 
-    generateInternalZipFile(entities, params, contentDirPath, contentZipFilePath)
+    generateZipFile(entities, params, contentDirPath, contentZipFilePath)
     FileUtils.forceDelete(File(contentDirPath))
 
     generateExternalZipFile(params=params, exportDirPath=exportDirPath, contentZipFilePath=contentZipFilePath, contentZipFileName=contentZipFileName,
@@ -189,35 +190,6 @@ def fetchServiceDocument(url, httpClient):
     return json.dumps(map(collectionToDictionaryMapper, collections))
 
 
-def checkResponseStatus(response):
-    status = response.getStatus()
-    if status >= 300:
-        reason = response.getReason()
-        raise ValueError('Unsuccessful response from the server: %s %s' % (status, reason))
-
-
-def generateInternalZipFile(entities, params, tempDirPath, tempZipFilePath):
-    # Generates ZIP file with selected item for export
-
-    sessionToken = params.get('sessionToken')
-    includeRoot = params.get('includeRoot')
-
-    fos = None
-    zos = None
-    try:
-        fos = FileOutputStream(tempZipFilePath)
-        zos = ZipOutputStream(fos)
-
-        fileMetadata = generateFilesInZip(zos, entities, includeRoot, sessionToken, tempDirPath)
-    finally:
-        if zos is not None:
-            zos.close()
-        if fos is not None:
-            fos.close()
-
-    return fileMetadata
-
-
 def generateExternalZipFile(params, exportDirPath, contentZipFilePath, contentZipFileName, exportZipFileName, userInformation, entities):
     # Generates ZIP file which will go to the research collection server
 
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/zenodo-exports-api/exportsApi.py b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/zenodo-exports-api/exportsApi.py
new file mode 120000
index 00000000000..ccb8a49feb3
--- /dev/null
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/zenodo-exports-api/exportsApi.py
@@ -0,0 +1 @@
+../exports-api/exportsApi.py
\ No newline at end of file
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/zenodo-exports-api/plugin.properties b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/zenodo-exports-api/plugin.properties
index e294424d54a..a9094b31167 100644
--- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/zenodo-exports-api/plugin.properties
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/zenodo-exports-api/plugin.properties
@@ -2,3 +2,4 @@ label = Zenodo Exports API
 class = ch.systemsx.cisd.openbis.dss.generic.server.plugins.jython.JythonIngestionService
 script-path = zenodoExports.py
 service-document-url=https://test.research-collection.ethz.ch/swordv2/servicedocument
+accessToken=
\ No newline at end of file
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/zenodo-exports-api/zenodoExports.py b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/zenodo-exports-api/zenodoExports.py
index 9e00cec83ab..f39c6eefa69 100644
--- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/zenodo-exports-api/zenodoExports.py
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/zenodo-exports-api/zenodoExports.py
@@ -14,10 +14,27 @@
 # limitations under the License.
 #
 
+from __future__ import print_function
+
+import traceback
+
+import time
 from ch.systemsx.cisd.common.logging import LogCategory
+from java.io import File
+from java.nio.file import Paths
+from org.apache.commons.io import FileUtils
 from org.apache.log4j import Logger
+from org.eclipse.jetty.client import HttpClient
+from org.eclipse.jetty.client.util import MultiPartContentProvider
+from org.eclipse.jetty.client.util import PathContentProvider
+from org.eclipse.jetty.client.util import StringContentProvider
+from org.eclipse.jetty.http import HttpMethod
+from org.eclipse.jetty.util.ssl import SslContextFactory
+from org.json import JSONObject
+
+from exportsApi import findEntitiesToExport, validateDataSize, getConfigurationProperty, generateZipFile, checkResponseStatus
 
-operationLog = Logger.getLogger(str(LogCategory.OPERATION) + '.rcExports.py')
+operationLog = Logger.getLogger(str(LogCategory.OPERATION) + '.zenodoExports.py')
 
 
 def process(tr, params, tableBuilder):
@@ -27,8 +44,120 @@ def process(tr, params, tableBuilder):
     tr.setUserId(userId)
 
     if method == 'exportAll':
-        resultUrl = export(tr, params)
+        resultUrl = expandAndExport(tr, params)
+
+
+def expandAndExport(tr, params):
+    entitiesToExport = findEntitiesToExport(params)
+    validateDataSize(entitiesToExport, tr)
+
+    userInformation = {
+        'firstName': params.get('userFirstName'),
+        'lastName': params.get('userLastName'),
+        'email': params.get('userEmail'),
+    }
+
+    operationLog.info('Found ' + str(len(entitiesToExport)) + ' entities to export')
+    return export(entities=entitiesToExport, tr=tr, params=params, userInformation=userInformation)
+
+
+def export(entities, tr, params, userInformation):
+    #Create temporal folder
+    timeNow = time.time()
+
+    exportDirName = 'export_' + str(timeNow)
+    exportDir = File.createTempFile(exportDirName, None)
+    exportDirPath = exportDir.getCanonicalPath()
+    exportDir.delete()
+    exportDir.mkdir()
+
+    contentZipFileName = 'content.zip'
+    contentDirName = 'content_' + str(timeNow)
+    contentDir = File.createTempFile(contentDirName, None, exportDir)
+    contentDirPath = contentDir.getCanonicalPath()
+    contentDir.delete()
+    contentDir.mkdir()
+
+    contentZipFilePath = exportDirPath + '/' + contentZipFileName
+
+    exportZipFilePath = exportDirPath + '.zip'
+    exportZipFileName = exportDirName + '.zip'
+
+    generateZipFile(entities, params, contentDirPath, contentZipFilePath)
+    FileUtils.forceDelete(File(contentDirPath))
+
+    resultUrl = sendToZenodo(tr=tr, params=params, tempZipFileName=contentZipFileName, tempZipFilePath=contentZipFilePath)
+    # cleanUp(exportDirPath, exportZipFilePath)
+    return resultUrl
+
+
+def sendToZenodo(tr, params, tempZipFileName, tempZipFilePath):
+    depositRootUrl = 'https://localhost/api/deposit/depositions'
+
+    accessToken = str(getConfigurationProperty(tr, 'accessToken'))
+    operationLog.info('accessToken: %s' % accessToken)
+
+    headers = {
+        # 'Content-Type': 'application/json',
+        'Authorization': 'Bearer ' + accessToken,
+    }
+
+    httpClient = None
+    try:
+        httpClient = createHttpClient()
+
+        httpClient.setFollowRedirects(False)
+        httpClient.start()
+
+        depositionData = createDepositionResource(httpClient.newRequest(depositRootUrl), accessToken)
+
+        print('depositionData="%s"' % depositionData.toString())
+        print('depositionId=%d' % depositionData.get('id'))
+        depositionLinks = depositionData.get('links')
+        depositUrl = depositionLinks.get('files')
+
+        print('depositUrl=%s' % depositUrl)
+
+        submissionResult = submitFile(tempZipFilePath, accessToken, httpClient.newRequest(depositUrl))
+
+        print('submissionResult=%s' % submissionResult.toString())
+
+        result = depositionLinks.get('html')
+        print('result="%s"' % result)
+        return result
+    except Exception as e:
+        operationLog.error('Exception at: ' + traceback.format_exc())
+        operationLog.error('Exception: ' + str(e))
+        raise e
+    finally:
+        if httpClient is not None:
+            httpClient.stop()
+
+
+def submitFile(tempZipFilePath, accessToken, request):
+    multiPart = MultiPartContentProvider()
+    multiPart.addFilePart('file', 'content.zip', PathContentProvider(Paths.get(tempZipFilePath)), None)
+    multiPart.close()
+    addAuthenticationHeader(accessToken, request)
+    response = request.method(HttpMethod.POST).content(multiPart).send()
+    checkResponseStatus(response)
+    contentStr = response.getContentAsString()
+    return JSONObject(contentStr)
+
+
+def createDepositionResource(request, accessToken):
+    addAuthenticationHeader(accessToken, request)
+    response = request.method(HttpMethod.POST).content(StringContentProvider("{}"), "application/json").send()
+    checkResponseStatus(response)
+    contentStr = response.getContentAsString()
+    return JSONObject(contentStr)
+
+
+def addAuthenticationHeader(accessToken, request):
+    request.header('Authorization', 'Bearer ' + accessToken)
 
 
-def export(tr, params):
-    pass
+def createHttpClient():
+    sslContextFactory = SslContextFactory()
+    sslContextFactory.setTrustAll(True)
+    return HttpClient(sslContextFactory)
-- 
GitLab