comparison OrthancServer/OrthancRestApi/OrthancRestResources.cpp @ 751:5197fd35333c

refactoring of OrthancRestApi
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 14 Apr 2014 11:28:35 +0200
parents
children a60040857ce6
comparison
equal deleted inserted replaced
750:4afad8cb94fd 751:5197fd35333c
1 /**
2 * Orthanc - A Lightweight, RESTful DICOM Store
3 * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege,
4 * Belgium
5 *
6 * This program is free software: you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation, either version 3 of the
9 * License, or (at your option) any later version.
10 *
11 * In addition, as a special exception, the copyright holders of this
12 * program give permission to link the code of its release with the
13 * OpenSSL project's "OpenSSL" library (or with modified versions of it
14 * that use the same license as the "OpenSSL" library), and distribute
15 * the linked executables. You must obey the GNU General Public License
16 * in all respects for all of the code used other than "OpenSSL". If you
17 * modify file(s) with this exception, you may extend this exception to
18 * your version of the file(s), but you are not obligated to do so. If
19 * you do not wish to do so, delete this exception statement from your
20 * version. If you delete this exception statement from all source files
21 * in the program, then also delete it here.
22 *
23 * This program is distributed in the hope that it will be useful, but
24 * WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
26 * General Public License for more details.
27 *
28 * You should have received a copy of the GNU General Public License
29 * along with this program. If not, see <http://www.gnu.org/licenses/>.
30 **/
31
32
33 #include "OrthancRestApi.h"
34
35 #include "../ServerToolbox.h"
36
37 #include <glog/logging.h>
38
39 namespace Orthanc
40 {
41 // List all the patients, studies, series or instances ----------------------
42
43 template <enum ResourceType resourceType>
44 static void ListResources(RestApi::GetCall& call)
45 {
46 Json::Value result;
47 OrthancRestApi::GetIndex(call).GetAllUuids(result, resourceType);
48 call.GetOutput().AnswerJson(result);
49 }
50
51 template <enum ResourceType resourceType>
52 static void GetSingleResource(RestApi::GetCall& call)
53 {
54 Json::Value result;
55 if (OrthancRestApi::GetIndex(call).LookupResource(result, call.GetUriComponent("id", ""), resourceType))
56 {
57 call.GetOutput().AnswerJson(result);
58 }
59 }
60
61 template <enum ResourceType resourceType>
62 static void DeleteSingleResource(RestApi::DeleteCall& call)
63 {
64 Json::Value result;
65 if (OrthancRestApi::GetIndex(call).DeleteResource(result, call.GetUriComponent("id", ""), resourceType))
66 {
67 call.GetOutput().AnswerJson(result);
68 }
69 }
70
71
72 // Get information about a single patient -----------------------------------
73
74 static void IsProtectedPatient(RestApi::GetCall& call)
75 {
76 std::string publicId = call.GetUriComponent("id", "");
77 bool isProtected = OrthancRestApi::GetIndex(call).IsProtectedPatient(publicId);
78 call.GetOutput().AnswerBuffer(isProtected ? "1" : "0", "text/plain");
79 }
80
81
82 static void SetPatientProtection(RestApi::PutCall& call)
83 {
84 ServerContext& context = OrthancRestApi::GetContext(call);
85
86 std::string publicId = call.GetUriComponent("id", "");
87 std::string s = Toolbox::StripSpaces(call.GetPutBody());
88
89 if (s == "0")
90 {
91 context.GetIndex().SetProtectedPatient(publicId, false);
92 call.GetOutput().AnswerBuffer("", "text/plain");
93 }
94 else if (s == "1")
95 {
96 context.GetIndex().SetProtectedPatient(publicId, true);
97 call.GetOutput().AnswerBuffer("", "text/plain");
98 }
99 else
100 {
101 // Bad request
102 }
103 }
104
105
106 // Get information about a single instance ----------------------------------
107
108 static void GetInstanceFile(RestApi::GetCall& call)
109 {
110 ServerContext& context = OrthancRestApi::GetContext(call);
111
112 std::string publicId = call.GetUriComponent("id", "");
113 context.AnswerDicomFile(call.GetOutput(), publicId, FileContentType_Dicom);
114 }
115
116
117 static void ExportInstanceFile(RestApi::PostCall& call)
118 {
119 ServerContext& context = OrthancRestApi::GetContext(call);
120
121 std::string publicId = call.GetUriComponent("id", "");
122
123 std::string dicom;
124 context.ReadFile(dicom, publicId, FileContentType_Dicom);
125
126 Toolbox::WriteFile(dicom, call.GetPostBody());
127
128 call.GetOutput().AnswerBuffer("{}", "application/json");
129 }
130
131
132 template <bool simplify>
133 static void GetInstanceTags(RestApi::GetCall& call)
134 {
135 ServerContext& context = OrthancRestApi::GetContext(call);
136
137 std::string publicId = call.GetUriComponent("id", "");
138
139 Json::Value full;
140 context.ReadJson(full, publicId);
141
142 if (simplify)
143 {
144 Json::Value simplified;
145 SimplifyTags(simplified, full);
146 call.GetOutput().AnswerJson(simplified);
147 }
148 else
149 {
150 call.GetOutput().AnswerJson(full);
151 }
152 }
153
154
155 static void ListFrames(RestApi::GetCall& call)
156 {
157 Json::Value instance;
158 if (OrthancRestApi::GetIndex(call).LookupResource(instance, call.GetUriComponent("id", ""), ResourceType_Instance))
159 {
160 unsigned int numberOfFrames = 1;
161
162 try
163 {
164 Json::Value tmp = instance["MainDicomTags"]["NumberOfFrames"];
165 numberOfFrames = boost::lexical_cast<unsigned int>(tmp.asString());
166 }
167 catch (...)
168 {
169 }
170
171 Json::Value result = Json::arrayValue;
172 for (unsigned int i = 0; i < numberOfFrames; i++)
173 {
174 result.append(i);
175 }
176
177 call.GetOutput().AnswerJson(result);
178 }
179 }
180
181
182 template <enum ImageExtractionMode mode>
183 static void GetImage(RestApi::GetCall& call)
184 {
185 ServerContext& context = OrthancRestApi::GetContext(call);
186
187 std::string frameId = call.GetUriComponent("frame", "0");
188
189 unsigned int frame;
190 try
191 {
192 frame = boost::lexical_cast<unsigned int>(frameId);
193 }
194 catch (boost::bad_lexical_cast)
195 {
196 return;
197 }
198
199 std::string publicId = call.GetUriComponent("id", "");
200 std::string dicomContent, png;
201 context.ReadFile(dicomContent, publicId, FileContentType_Dicom);
202
203 try
204 {
205 FromDcmtkBridge::ExtractPngImage(png, dicomContent, frame, mode);
206 call.GetOutput().AnswerBuffer(png, "image/png");
207 }
208 catch (OrthancException& e)
209 {
210 if (e.GetErrorCode() == ErrorCode_ParameterOutOfRange)
211 {
212 // The frame number is out of the range for this DICOM
213 // instance, the resource is not existent
214 }
215 else
216 {
217 std::string root = "";
218 for (size_t i = 1; i < call.GetFullUri().size(); i++)
219 {
220 root += "../";
221 }
222
223 call.GetOutput().Redirect(root + "app/images/unsupported.png");
224 }
225 }
226 }
227
228
229
230 static void GetResourceStatistics(RestApi::GetCall& call)
231 {
232 std::string publicId = call.GetUriComponent("id", "");
233 Json::Value result;
234 OrthancRestApi::GetIndex(call).GetStatistics(result, publicId);
235 call.GetOutput().AnswerJson(result);
236 }
237
238
239
240 // Handling of metadata -----------------------------------------------------
241
242 static void CheckValidResourceType(RestApi::Call& call)
243 {
244 std::string resourceType = call.GetUriComponent("resourceType", "");
245 StringToResourceType(resourceType.c_str());
246 }
247
248
249 static void ListMetadata(RestApi::GetCall& call)
250 {
251 CheckValidResourceType(call);
252
253 std::string publicId = call.GetUriComponent("id", "");
254 std::list<MetadataType> metadata;
255
256 OrthancRestApi::GetIndex(call).ListAvailableMetadata(metadata, publicId);
257 Json::Value result = Json::arrayValue;
258
259 for (std::list<MetadataType>::const_iterator
260 it = metadata.begin(); it != metadata.end(); ++it)
261 {
262 result.append(EnumerationToString(*it));
263 }
264
265 call.GetOutput().AnswerJson(result);
266 }
267
268
269 static void GetMetadata(RestApi::GetCall& call)
270 {
271 CheckValidResourceType(call);
272
273 std::string publicId = call.GetUriComponent("id", "");
274 std::string name = call.GetUriComponent("name", "");
275 MetadataType metadata = StringToMetadata(name);
276
277 std::string value;
278 if (OrthancRestApi::GetIndex(call).LookupMetadata(value, publicId, metadata))
279 {
280 call.GetOutput().AnswerBuffer(value, "text/plain");
281 }
282 }
283
284
285 static void DeleteMetadata(RestApi::DeleteCall& call)
286 {
287 CheckValidResourceType(call);
288
289 std::string publicId = call.GetUriComponent("id", "");
290 std::string name = call.GetUriComponent("name", "");
291 MetadataType metadata = StringToMetadata(name);
292
293 if (metadata >= MetadataType_StartUser &&
294 metadata <= MetadataType_EndUser)
295 {
296 // It is forbidden to modify internal metadata
297 OrthancRestApi::GetIndex(call).DeleteMetadata(publicId, metadata);
298 call.GetOutput().AnswerBuffer("", "text/plain");
299 }
300 }
301
302
303 static void SetMetadata(RestApi::PutCall& call)
304 {
305 CheckValidResourceType(call);
306
307 std::string publicId = call.GetUriComponent("id", "");
308 std::string name = call.GetUriComponent("name", "");
309 MetadataType metadata = StringToMetadata(name);
310 std::string value = call.GetPutBody();
311
312 if (metadata >= MetadataType_StartUser &&
313 metadata <= MetadataType_EndUser)
314 {
315 // It is forbidden to modify internal metadata
316 OrthancRestApi::GetIndex(call).SetMetadata(publicId, metadata, value);
317 call.GetOutput().AnswerBuffer("", "text/plain");
318 }
319 }
320
321
322
323
324 // Handling of attached files -----------------------------------------------
325
326 static void ListAttachments(RestApi::GetCall& call)
327 {
328 std::string resourceType = call.GetUriComponent("resourceType", "");
329 std::string publicId = call.GetUriComponent("id", "");
330 std::list<FileContentType> attachments;
331 OrthancRestApi::GetIndex(call).ListAvailableAttachments(attachments, publicId, StringToResourceType(resourceType.c_str()));
332
333 Json::Value result = Json::arrayValue;
334
335 for (std::list<FileContentType>::const_iterator
336 it = attachments.begin(); it != attachments.end(); ++it)
337 {
338 result.append(EnumerationToString(*it));
339 }
340
341 call.GetOutput().AnswerJson(result);
342 }
343
344
345 static bool GetAttachmentInfo(FileInfo& info, RestApi::Call& call)
346 {
347 CheckValidResourceType(call);
348
349 std::string publicId = call.GetUriComponent("id", "");
350 std::string name = call.GetUriComponent("name", "");
351 FileContentType contentType = StringToContentType(name);
352
353 return OrthancRestApi::GetIndex(call).LookupAttachment(info, publicId, contentType);
354 }
355
356
357 static void GetAttachmentOperations(RestApi::GetCall& call)
358 {
359 FileInfo info;
360 if (GetAttachmentInfo(info, call))
361 {
362 Json::Value operations = Json::arrayValue;
363
364 operations.append("compressed-data");
365
366 if (info.GetCompressedMD5() != "")
367 {
368 operations.append("compressed-md5");
369 }
370
371 operations.append("compressed-size");
372 operations.append("data");
373
374 if (info.GetUncompressedMD5() != "")
375 {
376 operations.append("md5");
377 }
378
379 operations.append("size");
380
381 if (info.GetCompressedMD5() != "" &&
382 info.GetUncompressedMD5() != "")
383 {
384 operations.append("verify-md5");
385 }
386
387 call.GetOutput().AnswerJson(operations);
388 }
389 }
390
391
392 template <int uncompress>
393 static void GetAttachmentData(RestApi::GetCall& call)
394 {
395 ServerContext& context = OrthancRestApi::GetContext(call);
396
397 CheckValidResourceType(call);
398
399 std::string publicId = call.GetUriComponent("id", "");
400 std::string name = call.GetUriComponent("name", "");
401
402 std::string content;
403 context.ReadFile(content, publicId, StringToContentType(name),
404 (uncompress == 1));
405
406 call.GetOutput().AnswerBuffer(content, "application/octet-stream");
407 }
408
409
410 static void GetAttachmentSize(RestApi::GetCall& call)
411 {
412 FileInfo info;
413 if (GetAttachmentInfo(info, call))
414 {
415 call.GetOutput().AnswerBuffer(boost::lexical_cast<std::string>(info.GetUncompressedSize()), "text/plain");
416 }
417 }
418
419
420 static void GetAttachmentCompressedSize(RestApi::GetCall& call)
421 {
422 FileInfo info;
423 if (GetAttachmentInfo(info, call))
424 {
425 call.GetOutput().AnswerBuffer(boost::lexical_cast<std::string>(info.GetCompressedSize()), "text/plain");
426 }
427 }
428
429
430 static void GetAttachmentMD5(RestApi::GetCall& call)
431 {
432 FileInfo info;
433 if (GetAttachmentInfo(info, call) &&
434 info.GetUncompressedMD5() != "")
435 {
436 call.GetOutput().AnswerBuffer(boost::lexical_cast<std::string>(info.GetUncompressedMD5()), "text/plain");
437 }
438 }
439
440
441 static void GetAttachmentCompressedMD5(RestApi::GetCall& call)
442 {
443 FileInfo info;
444 if (GetAttachmentInfo(info, call) &&
445 info.GetCompressedMD5() != "")
446 {
447 call.GetOutput().AnswerBuffer(boost::lexical_cast<std::string>(info.GetCompressedMD5()), "text/plain");
448 }
449 }
450
451
452 static void VerifyAttachment(RestApi::PostCall& call)
453 {
454 ServerContext& context = OrthancRestApi::GetContext(call);
455 CheckValidResourceType(call);
456
457 std::string publicId = call.GetUriComponent("id", "");
458 std::string name = call.GetUriComponent("name", "");
459
460 FileInfo info;
461 if (!GetAttachmentInfo(info, call) ||
462 info.GetCompressedMD5() == "" ||
463 info.GetUncompressedMD5() == "")
464 {
465 // Inexistent resource, or no MD5 available
466 return;
467 }
468
469 bool ok = false;
470
471 // First check whether the compressed data is correctly stored in the disk
472 std::string data;
473 context.ReadFile(data, publicId, StringToContentType(name), false);
474
475 std::string actualMD5;
476 Toolbox::ComputeMD5(actualMD5, data);
477
478 if (actualMD5 == info.GetCompressedMD5())
479 {
480 // The compressed data is OK. If a compression algorithm was
481 // applied to it, now check the MD5 of the uncompressed data.
482 if (info.GetCompressionType() == CompressionType_None)
483 {
484 ok = true;
485 }
486 else
487 {
488 context.ReadFile(data, publicId, StringToContentType(name), true);
489 Toolbox::ComputeMD5(actualMD5, data);
490 ok = (actualMD5 == info.GetUncompressedMD5());
491 }
492 }
493
494 if (ok)
495 {
496 LOG(INFO) << "The attachment " << name << " of resource " << publicId << " has the right MD5";
497 call.GetOutput().AnswerBuffer("{}", "application/json");
498 }
499 else
500 {
501 LOG(INFO) << "The attachment " << name << " of resource " << publicId << " has bad MD5!";
502 }
503 }
504
505
506 static void UploadAttachment(RestApi::PutCall& call)
507 {
508 ServerContext& context = OrthancRestApi::GetContext(call);
509 CheckValidResourceType(call);
510
511 std::string publicId = call.GetUriComponent("id", "");
512 std::string name = call.GetUriComponent("name", "");
513
514 const void* data = call.GetPutBody().size() ? &call.GetPutBody()[0] : NULL;
515
516 FileContentType contentType = StringToContentType(name);
517 if (contentType >= FileContentType_StartUser && // It is forbidden to modify internal attachments
518 contentType <= FileContentType_EndUser &&
519 context.AddAttachment(publicId, StringToContentType(name), data, call.GetPutBody().size()))
520 {
521 call.GetOutput().AnswerBuffer("{}", "application/json");
522 }
523 }
524
525
526 static void DeleteAttachment(RestApi::DeleteCall& call)
527 {
528 CheckValidResourceType(call);
529
530 std::string publicId = call.GetUriComponent("id", "");
531 std::string name = call.GetUriComponent("name", "");
532 FileContentType contentType = StringToContentType(name);
533
534 if (contentType >= FileContentType_StartUser &&
535 contentType <= FileContentType_EndUser)
536 {
537 // It is forbidden to delete internal attachments
538 OrthancRestApi::GetIndex(call).DeleteAttachment(publicId, contentType);
539 call.GetOutput().AnswerBuffer("{}", "application/json");
540 }
541 }
542
543
544
545
546 void OrthancRestApi::RegisterResources()
547 {
548 Register("/instances", ListResources<ResourceType_Instance>);
549 Register("/patients", ListResources<ResourceType_Patient>);
550 Register("/series", ListResources<ResourceType_Series>);
551 Register("/studies", ListResources<ResourceType_Study>);
552
553 Register("/instances/{id}", DeleteSingleResource<ResourceType_Instance>);
554 Register("/instances/{id}", GetSingleResource<ResourceType_Instance>);
555 Register("/patients/{id}", DeleteSingleResource<ResourceType_Patient>);
556 Register("/patients/{id}", GetSingleResource<ResourceType_Patient>);
557 Register("/series/{id}", DeleteSingleResource<ResourceType_Series>);
558 Register("/series/{id}", GetSingleResource<ResourceType_Series>);
559 Register("/studies/{id}", DeleteSingleResource<ResourceType_Study>);
560 Register("/studies/{id}", GetSingleResource<ResourceType_Study>);
561
562 Register("/instances/{id}/statistics", GetResourceStatistics);
563 Register("/patients/{id}/statistics", GetResourceStatistics);
564 Register("/studies/{id}/statistics", GetResourceStatistics);
565 Register("/series/{id}/statistics", GetResourceStatistics);
566
567 Register("/instances/{id}/file", GetInstanceFile);
568 Register("/instances/{id}/export", ExportInstanceFile);
569 Register("/instances/{id}/tags", GetInstanceTags<false>);
570 Register("/instances/{id}/simplified-tags", GetInstanceTags<true>);
571 Register("/instances/{id}/frames", ListFrames);
572
573 Register("/instances/{id}/frames/{frame}/preview", GetImage<ImageExtractionMode_Preview>);
574 Register("/instances/{id}/frames/{frame}/image-uint8", GetImage<ImageExtractionMode_UInt8>);
575 Register("/instances/{id}/frames/{frame}/image-uint16", GetImage<ImageExtractionMode_UInt16>);
576 Register("/instances/{id}/frames/{frame}/image-int16", GetImage<ImageExtractionMode_Int16>);
577 Register("/instances/{id}/preview", GetImage<ImageExtractionMode_Preview>);
578 Register("/instances/{id}/image-uint8", GetImage<ImageExtractionMode_UInt8>);
579 Register("/instances/{id}/image-uint16", GetImage<ImageExtractionMode_UInt16>);
580 Register("/instances/{id}/image-int16", GetImage<ImageExtractionMode_Int16>);
581
582 Register("/patients/{id}/protected", IsProtectedPatient);
583 Register("/patients/{id}/protected", SetPatientProtection);
584
585 Register("/{resourceType}/{id}/metadata", ListMetadata);
586 Register("/{resourceType}/{id}/metadata/{name}", DeleteMetadata);
587 Register("/{resourceType}/{id}/metadata/{name}", GetMetadata);
588 Register("/{resourceType}/{id}/metadata/{name}", SetMetadata);
589
590 Register("/{resourceType}/{id}/attachments", ListAttachments);
591 Register("/{resourceType}/{id}/attachments/{name}", DeleteAttachment);
592 Register("/{resourceType}/{id}/attachments/{name}", GetAttachmentOperations);
593 Register("/{resourceType}/{id}/attachments/{name}/compressed-data", GetAttachmentData<0>);
594 Register("/{resourceType}/{id}/attachments/{name}/compressed-md5", GetAttachmentCompressedMD5);
595 Register("/{resourceType}/{id}/attachments/{name}/compressed-size", GetAttachmentCompressedSize);
596 Register("/{resourceType}/{id}/attachments/{name}/data", GetAttachmentData<1>);
597 Register("/{resourceType}/{id}/attachments/{name}/md5", GetAttachmentMD5);
598 Register("/{resourceType}/{id}/attachments/{name}/size", GetAttachmentSize);
599 Register("/{resourceType}/{id}/attachments/{name}/verify-md5", VerifyAttachment);
600 Register("/{resourceType}/{id}/attachments/{name}", UploadAttachment);
601 }
602 }