Mantid
Loading...
Searching...
No Matches
CatalogPublish.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
9
16
17#include <Poco/Net/AcceptCertificateHandler.h>
18#include <Poco/Net/HTTPRequest.h>
19#include <Poco/Net/HTTPResponse.h>
20#include <Poco/Net/HTTPSClientSession.h>
21#include <Poco/Net/PrivateKeyPassphraseHandler.h>
22#include <Poco/Net/SSLException.h>
23#include <Poco/Net/SSLManager.h>
24#include <Poco/Path.h>
25#include <Poco/SharedPtr.h>
26#include <Poco/StreamCopier.h>
27#include <Poco/URI.h>
28
29#include <boost/regex.hpp>
30#include <fstream>
31
32namespace Mantid::ICat {
33DECLARE_ALGORITHM(CatalogPublish)
34
35
36void CatalogPublish::init() {
37 declareProperty(std::make_unique<API::FileProperty>("FileName", "", API::FileProperty::OptionalLoad),
38 "The file to publish.");
39 declareProperty(std::make_unique<API::WorkspaceProperty<API::Workspace>>(
41 "The workspace to publish.");
42 declareProperty("NameInCatalog", "",
43 "The name to give to the file being saved. The file name or workspace "
44 "name is used by default. "
45 "This can only contain alphanumerics, underscores or periods.");
46 declareProperty("InvestigationNumber", "", "The investigation number where the published file will be saved to.");
47 declareProperty("DataFileDescription", "", "A short description of the datafile you are publishing to the catalog.");
48 declareProperty("Session", "", "The session information of the catalog to use.");
49}
50
53 // Used for error checking.
54 std::string ws = getPropertyValue("InputWorkspace");
55 std::string filePath = getPropertyValue("FileName");
56 std::string nameInCatalog = getPropertyValue("NameInCatalog");
58
59 // Prevent invalid/malicious file names being saved to the catalog.
60 boost::regex re("^[a-zA-Z0-9_.]*$");
61 if (!boost::regex_match(nameInCatalog.begin(), nameInCatalog.end(), re)) {
62 throw std::runtime_error("The filename can only contain characters, "
63 "numbers, underscores and periods");
64 }
65
66 // Error checking to ensure a workspace OR a file is selected. Never both.
67 if ((ws.empty() && filePath.empty()) || (!ws.empty() && !filePath.empty())) {
68 throw std::runtime_error("Please select a workspace or a file to publish. Not both.");
69 }
70
71 // Cast a catalog to a catalogInfoService to access publishing functionality.
72 auto catalogInfoService = std::dynamic_pointer_cast<API::ICatalogInfoService>(
73 API::CatalogManager::Instance().getCatalog(getPropertyValue("Session")));
74
75 // Check if the catalog created supports publishing functionality.
76 if (!catalogInfoService)
77 throw std::runtime_error("The catalog that you are using does not support "
78 "publishing to the archives.");
79
80 // The user want to upload a file.
81 if (!filePath.empty()) {
82 std::string fileName = Poco::Path(filePath).getFileName();
83 // If the user has not set the name to save the file as, then use the
84 // filename of the file being uploaded.
85 if (nameInCatalog.empty()) {
86 setProperty("NameInCatalog", fileName);
87 g_log.notice("NameInCatalog has not been set. Using filename instead: " + fileName + ".");
88 }
89 } else // The user wants to upload a workspace.
90 {
91 if (nameInCatalog.empty()) {
92 setProperty("NameInCatalog", workspace->getName());
93 g_log.notice("NameInCatalog has not been set. Using workspace name instead: " + workspace->getName() + ".");
94 }
95
96 // Save workspace to a .nxs file in the user's default directory.
98 // Overwrite the filePath string to the location of the file (from which the
99 // workspace was saved to).
100 filePath =
101 Mantid::Kernel::ConfigService::Instance().getString("defaultsave.directory") + workspace->getName() + ".nxs";
102 }
103
104 // Obtain the mode to used base on file extension.
105 std::ios_base::openmode mode = isDataFile(filePath) ? std::ios_base::binary : std::ios_base::in;
106 // Stream the contents of the file the user wants to publish & store it in
107 // file.
108 std::ifstream fileStream(filePath.c_str(), mode);
109 // Verify that the file can be opened correctly.
110 if (fileStream.rdstate() & std::ios::failbit)
111 throw Mantid::Kernel::Exception::FileError("Error on opening file at: ", filePath);
112 // Publish the contents of the file to the server.
113 publish(fileStream,
114 catalogInfoService->getUploadURL(getPropertyValue("InvestigationNumber"), getPropertyValue("NameInCatalog"),
115 getPropertyValue("DataFileDescription")));
116 // If a workspace was published, then we want to also publish the history of a
117 // workspace.
118 if (!ws.empty())
119 publishWorkspaceHistory(catalogInfoService, workspace);
120}
121
127void CatalogPublish::publish(std::istream &fileContents, const std::string &uploadURL) {
128 try {
129 Poco::URI uri(uploadURL);
130 std::string path(uri.getPathAndQuery());
131
132 Poco::SharedPtr<Poco::Net::InvalidCertificateHandler> certificateHandler =
133 new Poco::Net::AcceptCertificateHandler(true);
134 // Currently do not use any means of authentication. This should be updated
135 // IDS has signed certificate.
136 const Poco::Net::Context::Ptr context =
137 new Poco::Net::Context(Poco::Net::Context::CLIENT_USE, "", "", "", Poco::Net::Context::VERIFY_NONE);
138 // Create a singleton for holding the default context. E.g. any future
139 // requests to publish are made to this certificate and context.
140 Poco::Net::SSLManager::instance().initializeClient(nullptr, certificateHandler, context);
141 Poco::Net::HTTPSClientSession session(uri.getHost(), uri.getPort(), context);
142
143 // Send the HTTP request, and obtain the output stream to write to. E.g. the
144 // data to publish to the server.
145 Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_PUT, path, Poco::Net::HTTPMessage::HTTP_1_1);
146 // Sets the encoding type of the request. This enables us to stream data to
147 // the server.
148 request.setChunkedTransferEncoding(true);
149 std::ostream &os = session.sendRequest(request);
150 // Copy data from the input stream to the server (request) output stream.
151 Poco::StreamCopier::copyStream(fileContents, os);
152
153 // Close the request by requesting a response.
154 Poco::Net::HTTPResponse response;
155 // Store the response for use IF an error occurs (e.g. 404).
156 std::istream &responseStream = session.receiveResponse(response);
157
158 // Obtain the status returned by the server to verify if it was a success.
159 Poco::Net::HTTPResponse::HTTPStatus HTTPStatus = response.getStatus();
160 // The error message returned by the IDS (if one exists).
161 std::string IDSError = CatalogAlgorithmHelper().getIDSError(HTTPStatus, responseStream);
162 // Cancel the algorithm and display the message if it exists.
163 if (!IDSError.empty()) {
164 // As an error occurred we must cancel the algorithm.
165 // We cannot throw an exception here otherwise it is caught below as
166 // Poco::Exception catches runtimes,
167 // and then the I/O error is thrown as it is generated above first.
168 this->cancel();
169 // Output an appropriate error message from the JSON object returned by
170 // the IDS.
171 g_log.error(IDSError);
172 }
173 } catch (Poco::Net::SSLException &error) {
174 throw std::runtime_error(error.displayText());
175 }
176 // This is bad, but is needed to catch a POCO I/O error.
177 // For more info see comments (of I/O error) in CatalogDownloadDataFiles.cpp
178 catch (Poco::Exception &) {
179 }
180}
181
187bool CatalogPublish::isDataFile(const std::string &filePath) {
188 std::string extension = Poco::Path(filePath).getExtension();
189 std::transform(extension.begin(), extension.end(), extension.begin(), tolower);
190 return extension == "raw" || extension == "nxs";
191}
192
200 // Create the save nexus algorithm to use.
201 auto saveNexus = Mantid::API::AlgorithmManager::Instance().create("SaveNexus");
202 saveNexus->initialize();
203 // Set the required properties & execute.
204 saveNexus->setProperty("InputWorkspace", workspace->getName());
205 saveNexus->setProperty("FileName", Mantid::Kernel::ConfigService::Instance().getString("defaultsave.directory") +
206 workspace->getName() + ".nxs");
207 saveNexus->execute();
208}
209
217 std::stringstream ss;
218 // Obtain the workspace history as a string.
220 // Use the name the use wants to save the file to the server as and append .py
221 std::string fileName = Poco::Path(Poco::Path(getPropertyValue("NameInCatalog")).getFileName()).getBaseName() + ".py";
222 // Publish the workspace history to the server.
223 publish(ss, catalogInfoService->getUploadURL(getPropertyValue("InvestigationNumber"), fileName,
224 getPropertyValue("DataFileDescription")));
225}
226
233 auto wsHistory = Mantid::API::AlgorithmManager::Instance().createUnmanaged("GeneratePythonScript");
234 wsHistory->initialize();
235 wsHistory->setProperty("InputWorkspace", workspace->getName());
236 wsHistory->execute();
237 return wsHistory->getPropertyValue("ScriptText");
238}
239} // namespace Mantid::ICat
#define DECLARE_ALGORITHM(classname)
Definition: Algorithm.h:576
double error
Definition: IndexPeaks.cpp:133
IPeaksWorkspace_sptr workspace
Definition: IndexPeaks.cpp:114
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
Definition: Algorithm.cpp:2026
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Definition: Algorithm.cpp:2076
Kernel::Logger & g_log
Definition: Algorithm.h:451
void cancel() override
Raises the cancel flag.
Definition: Algorithm.cpp:1644
@ OptionalLoad
to specify a file to read but the file doesn't have to exist
Definition: FileProperty.h:53
A property class for workspaces.
const std::string getIDSError(const Poco::Net::HTTPResponse::HTTPStatus &HTTPStatus, std::istream &responseStream)
Obtain the error message returned by the IDS.
CatalogPublish is responsible for publishing user data to the data archive.
void saveWorkspaceToNexus(Mantid::API::Workspace_sptr &workspace)
Saves the workspace as a nexus file to the user's default directory.
void exec() override
Override algorithm execute method.
void publishWorkspaceHistory(Mantid::API::ICatalogInfoService_sptr &catalogInfoService, Mantid::API::Workspace_sptr &workspace)
Publish the history of a given workspace.
const std::string generateWorkspaceHistory(Mantid::API::Workspace_sptr &workspace)
Generate the history of a given workspace.
bool isDataFile(const std::string &filePath)
True if the extension of the file is a datafile.
void publish(std::istream &fileContents, const std::string &uploadURL)
Stream the contents of a file to a given URL.
Records the filename and the description of failure.
Definition: Exception.h:98
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void notice(const std::string &msg)
Logs at notice level.
Definition: Logger.cpp:95
void error(const std::string &msg)
Logs at error level.
Definition: Logger.cpp:77
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
std::shared_ptr< Workspace > Workspace_sptr
shared pointer to Mantid::API::Workspace
Definition: Workspace_fwd.h:20
std::shared_ptr< ICatalogInfoService > ICatalogInfoService_sptr
@ Input
An input workspace.
Definition: Property.h:53