Mercurial > hg > orthanc
comparison OrthancServer/Sources/OrthancRestApi/OrthancRestArchive.cpp @ 4044:d25f4c0fa160 framework
splitting code into OrthancFramework and OrthancServer
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Wed, 10 Jun 2020 20:30:34 +0200 |
parents | OrthancServer/OrthancRestApi/OrthancRestArchive.cpp@6ddad3e0b569 |
children | 05b8fd21089c |
comparison
equal
deleted
inserted
replaced
4043:6c6239aec462 | 4044:d25f4c0fa160 |
---|---|
1 /** | |
2 * Orthanc - A Lightweight, RESTful DICOM Store | |
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics | |
4 * Department, University Hospital of Liege, Belgium | |
5 * Copyright (C) 2017-2020 Osimis S.A., Belgium | |
6 * | |
7 * This program is free software: you can redistribute it and/or | |
8 * modify it under the terms of the GNU General Public License as | |
9 * published by the Free Software Foundation, either version 3 of the | |
10 * License, or (at your option) any later version. | |
11 * | |
12 * In addition, as a special exception, the copyright holders of this | |
13 * program give permission to link the code of its release with the | |
14 * OpenSSL project's "OpenSSL" library (or with modified versions of it | |
15 * that use the same license as the "OpenSSL" library), and distribute | |
16 * the linked executables. You must obey the GNU General Public License | |
17 * in all respects for all of the code used other than "OpenSSL". If you | |
18 * modify file(s) with this exception, you may extend this exception to | |
19 * your version of the file(s), but you are not obligated to do so. If | |
20 * you do not wish to do so, delete this exception statement from your | |
21 * version. If you delete this exception statement from all source files | |
22 * in the program, then also delete it here. | |
23 * | |
24 * This program is distributed in the hope that it will be useful, but | |
25 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
27 * General Public License for more details. | |
28 * | |
29 * You should have received a copy of the GNU General Public License | |
30 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
31 **/ | |
32 | |
33 | |
34 #include "../PrecompiledHeadersServer.h" | |
35 #include "OrthancRestApi.h" | |
36 | |
37 #include "../../Core/HttpServer/FilesystemHttpSender.h" | |
38 #include "../../Core/OrthancException.h" | |
39 #include "../../Core/SerializationToolbox.h" | |
40 #include "../OrthancConfiguration.h" | |
41 #include "../ServerContext.h" | |
42 #include "../ServerJobs/ArchiveJob.h" | |
43 | |
44 | |
45 namespace Orthanc | |
46 { | |
47 static const char* const KEY_RESOURCES = "Resources"; | |
48 static const char* const KEY_EXTENDED = "Extended"; | |
49 static const char* const KEY_TRANSCODE = "Transcode"; | |
50 | |
51 static void AddResourcesOfInterestFromArray(ArchiveJob& job, | |
52 const Json::Value& resources) | |
53 { | |
54 if (resources.type() != Json::arrayValue) | |
55 { | |
56 throw OrthancException(ErrorCode_BadFileFormat, | |
57 "Expected a list of strings (Orthanc identifiers)"); | |
58 } | |
59 | |
60 for (Json::Value::ArrayIndex i = 0; i < resources.size(); i++) | |
61 { | |
62 if (resources[i].type() != Json::stringValue) | |
63 { | |
64 throw OrthancException(ErrorCode_BadFileFormat, | |
65 "Expected a list of strings (Orthanc identifiers)"); | |
66 } | |
67 else | |
68 { | |
69 job.AddResource(resources[i].asString()); | |
70 } | |
71 } | |
72 } | |
73 | |
74 | |
75 static void AddResourcesOfInterest(ArchiveJob& job /* inout */, | |
76 const Json::Value& body /* in */) | |
77 { | |
78 if (body.type() == Json::arrayValue) | |
79 { | |
80 AddResourcesOfInterestFromArray(job, body); | |
81 } | |
82 else if (body.type() == Json::objectValue) | |
83 { | |
84 if (body.isMember(KEY_RESOURCES)) | |
85 { | |
86 AddResourcesOfInterestFromArray(job, body[KEY_RESOURCES]); | |
87 } | |
88 else | |
89 { | |
90 throw OrthancException(ErrorCode_BadFileFormat, | |
91 "Missing field " + std::string(KEY_RESOURCES) + | |
92 " in the JSON body"); | |
93 } | |
94 } | |
95 else | |
96 { | |
97 throw OrthancException(ErrorCode_BadFileFormat); | |
98 } | |
99 } | |
100 | |
101 | |
102 static DicomTransferSyntax GetTransferSyntax(const std::string& value) | |
103 { | |
104 DicomTransferSyntax syntax; | |
105 if (LookupTransferSyntax(syntax, value)) | |
106 { | |
107 return syntax; | |
108 } | |
109 else | |
110 { | |
111 throw OrthancException(ErrorCode_ParameterOutOfRange, | |
112 "Unknown transfer syntax: " + value); | |
113 } | |
114 } | |
115 | |
116 | |
117 static void GetJobParameters(bool& synchronous, /* out */ | |
118 bool& extended, /* out */ | |
119 bool& transcode, /* out */ | |
120 DicomTransferSyntax& syntax, /* out */ | |
121 int& priority, /* out */ | |
122 const Json::Value& body, /* in */ | |
123 const bool defaultExtended /* in */) | |
124 { | |
125 synchronous = OrthancRestApi::IsSynchronousJobRequest | |
126 (true /* synchronous by default */, body); | |
127 | |
128 priority = OrthancRestApi::GetJobRequestPriority(body); | |
129 | |
130 if (body.type() == Json::objectValue && | |
131 body.isMember(KEY_EXTENDED)) | |
132 { | |
133 extended = SerializationToolbox::ReadBoolean(body, KEY_EXTENDED); | |
134 } | |
135 else | |
136 { | |
137 extended = defaultExtended; | |
138 } | |
139 | |
140 if (body.type() == Json::objectValue && | |
141 body.isMember(KEY_TRANSCODE)) | |
142 { | |
143 transcode = true; | |
144 syntax = GetTransferSyntax(SerializationToolbox::ReadString(body, KEY_TRANSCODE)); | |
145 } | |
146 else | |
147 { | |
148 transcode = false; | |
149 } | |
150 } | |
151 | |
152 | |
153 static void SubmitJob(RestApiOutput& output, | |
154 ServerContext& context, | |
155 std::unique_ptr<ArchiveJob>& job, | |
156 int priority, | |
157 bool synchronous, | |
158 const std::string& filename) | |
159 { | |
160 if (job.get() == NULL) | |
161 { | |
162 throw OrthancException(ErrorCode_NullPointer); | |
163 } | |
164 | |
165 job->SetDescription("REST API"); | |
166 | |
167 if (synchronous) | |
168 { | |
169 boost::shared_ptr<TemporaryFile> tmp; | |
170 | |
171 { | |
172 OrthancConfiguration::ReaderLock lock; | |
173 tmp.reset(lock.GetConfiguration().CreateTemporaryFile()); | |
174 } | |
175 | |
176 job->SetSynchronousTarget(tmp); | |
177 | |
178 Json::Value publicContent; | |
179 context.GetJobsEngine().GetRegistry().SubmitAndWait | |
180 (publicContent, job.release(), priority); | |
181 | |
182 { | |
183 // The archive is now created: Prepare the sending of the ZIP file | |
184 FilesystemHttpSender sender(tmp->GetPath(), MimeType_Zip); | |
185 sender.SetContentFilename(filename); | |
186 | |
187 // Send the ZIP | |
188 output.AnswerStream(sender); | |
189 } | |
190 } | |
191 else | |
192 { | |
193 OrthancRestApi::SubmitGenericJob(output, context, job.release(), false, priority); | |
194 } | |
195 } | |
196 | |
197 | |
198 template <bool IS_MEDIA, | |
199 bool DEFAULT_IS_EXTENDED /* only makes sense for media (i.e. not ZIP archives) */ > | |
200 static void CreateBatch(RestApiPostCall& call) | |
201 { | |
202 ServerContext& context = OrthancRestApi::GetContext(call); | |
203 | |
204 Json::Value body; | |
205 if (call.ParseJsonRequest(body)) | |
206 { | |
207 bool synchronous, extended, transcode; | |
208 DicomTransferSyntax transferSyntax; | |
209 int priority; | |
210 GetJobParameters(synchronous, extended, transcode, transferSyntax, | |
211 priority, body, DEFAULT_IS_EXTENDED); | |
212 | |
213 std::unique_ptr<ArchiveJob> job(new ArchiveJob(context, IS_MEDIA, extended)); | |
214 AddResourcesOfInterest(*job, body); | |
215 | |
216 if (transcode) | |
217 { | |
218 job->SetTranscode(transferSyntax); | |
219 } | |
220 | |
221 SubmitJob(call.GetOutput(), context, job, priority, synchronous, "Archive.zip"); | |
222 } | |
223 else | |
224 { | |
225 throw OrthancException(ErrorCode_BadFileFormat, | |
226 "Expected a list of resources to archive in the body"); | |
227 } | |
228 } | |
229 | |
230 | |
231 template <bool IS_MEDIA, | |
232 bool DEFAULT_IS_EXTENDED /* only makes sense for media (i.e. not ZIP archives) */ > | |
233 static void CreateSingleGet(RestApiGetCall& call) | |
234 { | |
235 ServerContext& context = OrthancRestApi::GetContext(call); | |
236 | |
237 std::string id = call.GetUriComponent("id", ""); | |
238 | |
239 bool extended; | |
240 if (IS_MEDIA) | |
241 { | |
242 extended = call.HasArgument("extended"); | |
243 } | |
244 else | |
245 { | |
246 extended = false; | |
247 } | |
248 | |
249 std::unique_ptr<ArchiveJob> job(new ArchiveJob(context, IS_MEDIA, extended)); | |
250 job->AddResource(id); | |
251 | |
252 static const char* const TRANSCODE = "transcode"; | |
253 if (call.HasArgument(TRANSCODE)) | |
254 { | |
255 job->SetTranscode(GetTransferSyntax(call.GetArgument(TRANSCODE, ""))); | |
256 } | |
257 | |
258 SubmitJob(call.GetOutput(), context, job, 0 /* priority */, | |
259 true /* synchronous */, id + ".zip"); | |
260 } | |
261 | |
262 | |
263 template <bool IS_MEDIA, | |
264 bool DEFAULT_IS_EXTENDED /* only makes sense for media (i.e. not ZIP archives) */ > | |
265 static void CreateSinglePost(RestApiPostCall& call) | |
266 { | |
267 ServerContext& context = OrthancRestApi::GetContext(call); | |
268 | |
269 std::string id = call.GetUriComponent("id", ""); | |
270 | |
271 Json::Value body; | |
272 if (call.ParseJsonRequest(body)) | |
273 { | |
274 bool synchronous, extended, transcode; | |
275 DicomTransferSyntax transferSyntax; | |
276 int priority; | |
277 GetJobParameters(synchronous, extended, transcode, transferSyntax, | |
278 priority, body, DEFAULT_IS_EXTENDED); | |
279 | |
280 std::unique_ptr<ArchiveJob> job(new ArchiveJob(context, IS_MEDIA, extended)); | |
281 job->AddResource(id); | |
282 | |
283 if (transcode) | |
284 { | |
285 job->SetTranscode(transferSyntax); | |
286 } | |
287 | |
288 SubmitJob(call.GetOutput(), context, job, priority, synchronous, id + ".zip"); | |
289 } | |
290 else | |
291 { | |
292 throw OrthancException(ErrorCode_BadFileFormat); | |
293 } | |
294 } | |
295 | |
296 | |
297 void OrthancRestApi::RegisterArchive() | |
298 { | |
299 Register("/patients/{id}/archive", | |
300 CreateSingleGet<false /* ZIP */, false /* extended makes no sense in ZIP */>); | |
301 Register("/studies/{id}/archive", | |
302 CreateSingleGet<false /* ZIP */, false /* extended makes no sense in ZIP */>); | |
303 Register("/series/{id}/archive", | |
304 CreateSingleGet<false /* ZIP */, false /* extended makes no sense in ZIP */>); | |
305 | |
306 Register("/patients/{id}/archive", | |
307 CreateSinglePost<false /* ZIP */, false /* extended makes no sense in ZIP */>); | |
308 Register("/studies/{id}/archive", | |
309 CreateSinglePost<false /* ZIP */, false /* extended makes no sense in ZIP */>); | |
310 Register("/series/{id}/archive", | |
311 CreateSinglePost<false /* ZIP */, false /* extended makes no sense in ZIP */>); | |
312 | |
313 Register("/patients/{id}/media", | |
314 CreateSingleGet<true /* media */, false /* not extended by default */>); | |
315 Register("/studies/{id}/media", | |
316 CreateSingleGet<true /* media */, false /* not extended by default */>); | |
317 Register("/series/{id}/media", | |
318 CreateSingleGet<true /* media */, false /* not extended by default */>); | |
319 | |
320 Register("/patients/{id}/media", | |
321 CreateSinglePost<true /* media */, false /* not extended by default */>); | |
322 Register("/studies/{id}/media", | |
323 CreateSinglePost<true /* media */, false /* not extended by default */>); | |
324 Register("/series/{id}/media", | |
325 CreateSinglePost<true /* media */, false /* not extended by default */>); | |
326 | |
327 Register("/tools/create-archive", | |
328 CreateBatch<false /* ZIP */, false /* extended makes no sense in ZIP */>); | |
329 Register("/tools/create-media", | |
330 CreateBatch<true /* media */, false /* not extended by default */>); | |
331 Register("/tools/create-media-extended", | |
332 CreateBatch<true /* media */, true /* extended by default */>); | |
333 } | |
334 } |