comparison OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp @ 5809:023a99146dd0 attach-custom-data tip

merged find-refactoring -> attach-custom-data
author Alain Mazy <am@orthanc.team>
date Tue, 24 Sep 2024 12:53:43 +0200
parents 8279eaab0d1d 25df40a274fd
children
comparison
equal deleted inserted replaced
5808:63c025cf6958 5809:023a99146dd0
20 * along with this program. If not, see <http://www.gnu.org/licenses/>. 20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 **/ 21 **/
22 22
23 23
24 #include "../PrecompiledHeadersServer.h" 24 #include "../PrecompiledHeadersServer.h"
25 #include "../ResourceFinder.h"
26
25 #include "OrthancRestApi.h" 27 #include "OrthancRestApi.h"
26 28
27 #include "../../../OrthancFramework/Sources/Compression/GzipCompressor.h" 29 #include "../../../OrthancFramework/Sources/Compression/GzipCompressor.h"
28 #include "../../../OrthancFramework/Sources/DicomFormat/DicomImageInformation.h" 30 #include "../../../OrthancFramework/Sources/DicomFormat/DicomImageInformation.h"
29 #include "../../../OrthancFramework/Sources/DicomParsing/DicomWebJsonVisitor.h" 31 #include "../../../OrthancFramework/Sources/DicomParsing/DicomWebJsonVisitor.h"
125 } 127 }
126 } 128 }
127 } 129 }
128 130
129 131
132 static bool ExpandResource(Json::Value& target,
133 ServerContext& context,
134 ResourceType level,
135 const std::string& identifier,
136 DicomToJsonFormat format,
137 bool retrieveMetadata)
138 {
139 ResourceFinder finder(level, true /* expand */);
140 finder.SetOrthancId(level, identifier);
141 finder.SetRetrieveMetadata(retrieveMetadata);
142
143 return finder.ExecuteOneResource(target, context, format, retrieveMetadata);
144 }
145
146
130 // List all the patients, studies, series or instances ---------------------- 147 // List all the patients, studies, series or instances ----------------------
131 148
132 static void AnswerListOfResources(RestApiOutput& output, 149 static void AnswerListOfResources1(RestApiOutput& output,
133 ServerContext& context, 150 ServerContext& context,
134 const std::list<std::string>& resources, 151 const std::list<std::string>& resources,
135 const std::map<std::string, std::string>& instancesIds, // optional: the id of an instance for each found resource. 152 const std::map<std::string, std::string>& instancesIds, // optional: the id of an instance for each found resource.
136 const std::map<std::string, boost::shared_ptr<DicomMap> >& resourcesMainDicomTags, // optional: all tags read from DB for a resource (current level and upper levels) 153 const std::map<std::string, boost::shared_ptr<DicomMap> >& resourcesMainDicomTags, // optional: all tags read from DB for a resource (current level and upper levels)
137 const std::map<std::string, boost::shared_ptr<Json::Value> >& resourcesDicomAsJson, // optional: the dicom-as-json for each resource 154 const std::map<std::string, boost::shared_ptr<Json::Value> >& resourcesDicomAsJson, // optional: the dicom-as-json for each resource
181 198
182 output.AnswerJson(answer); 199 output.AnswerJson(answer);
183 } 200 }
184 201
185 202
186 static void AnswerListOfResources(RestApiOutput& output, 203 static void AnswerListOfResources2(RestApiOutput& output,
187 ServerContext& context, 204 ServerContext& context,
188 const std::list<std::string>& resources, 205 const std::list<std::string>& resources,
189 ResourceType level, 206 ResourceType level,
190 bool expand, 207 bool expand,
191 DicomToJsonFormat format, 208 DicomToJsonFormat format,
194 { 211 {
195 std::map<std::string, std::string> unusedInstancesIds; 212 std::map<std::string, std::string> unusedInstancesIds;
196 std::map<std::string, boost::shared_ptr<DicomMap> > unusedResourcesMainDicomTags; 213 std::map<std::string, boost::shared_ptr<DicomMap> > unusedResourcesMainDicomTags;
197 std::map<std::string, boost::shared_ptr<Json::Value> > unusedResourcesDicomAsJson; 214 std::map<std::string, boost::shared_ptr<Json::Value> > unusedResourcesDicomAsJson;
198 215
199 AnswerListOfResources(output, context, resources, unusedInstancesIds, unusedResourcesMainDicomTags, unusedResourcesDicomAsJson, level, expand, format, requestedTags, allowStorageAccess); 216 AnswerListOfResources1(output, context, resources, unusedInstancesIds, unusedResourcesMainDicomTags, unusedResourcesDicomAsJson, level, expand, format, requestedTags, allowStorageAccess);
200 } 217 }
201 218
202 219
203 template <enum ResourceType resourceType> 220 template <enum ResourceType resourceType>
204 static void ListResources(RestApiGetCall& call) 221 static void ListResources(RestApiGetCall& call)
224 } 241 }
225 242
226 ServerIndex& index = OrthancRestApi::GetIndex(call); 243 ServerIndex& index = OrthancRestApi::GetIndex(call);
227 ServerContext& context = OrthancRestApi::GetContext(call); 244 ServerContext& context = OrthancRestApi::GetContext(call);
228 245
229 std::list<std::string> result; 246 if (true)
230 247 {
231 std::set<DicomTag> requestedTags; 248 /**
232 OrthancRestApi::GetRequestedTags(requestedTags, call); 249 * EXPERIMENTAL VERSION
233 250 **/
234 if (call.HasArgument("limit") || 251
235 call.HasArgument("since")) 252 // TODO-FIND: include the FindRequest options parsing in a method (parse from get-arguments and from post payload)
236 { 253 // TODO-FIND: support other values for expand like expand=MainDicomTags,Labels,Parent,SeriesStatus
237 if (!call.HasArgument("limit")) 254 const bool expand = (call.HasArgument("expand") &&
238 { 255 call.GetBooleanArgument("expand", true));
239 throw OrthancException(ErrorCode_BadRequest, 256
240 "Missing \"limit\" argument for GET request against: " + 257 std::set<DicomTag> requestedTags;
241 call.FlattenUri()); 258 OrthancRestApi::GetRequestedTags(requestedTags, call);
242 } 259
243 260 ResourceFinder finder(resourceType, expand);
244 if (!call.HasArgument("since")) 261 finder.AddRequestedTags(requestedTags);
245 { 262
246 throw OrthancException(ErrorCode_BadRequest, 263 if (call.HasArgument("limit") ||
247 "Missing \"since\" argument for GET request against: " + 264 call.HasArgument("since"))
248 call.FlattenUri()); 265 {
249 } 266 if (!call.HasArgument("limit"))
250 267 {
251 size_t since = boost::lexical_cast<size_t>(call.GetArgument("since", "")); 268 throw OrthancException(ErrorCode_BadRequest,
252 size_t limit = boost::lexical_cast<size_t>(call.GetArgument("limit", "")); 269 "Missing \"limit\" argument for GET request against: " +
253 index.GetAllUuids(result, resourceType, since, limit); 270 call.FlattenUri());
271 }
272
273 if (!call.HasArgument("since"))
274 {
275 throw OrthancException(ErrorCode_BadRequest,
276 "Missing \"since\" argument for GET request against: " +
277 call.FlattenUri());
278 }
279
280 uint64_t since = boost::lexical_cast<uint64_t>(call.GetArgument("since", ""));
281 uint64_t limit = boost::lexical_cast<uint64_t>(call.GetArgument("limit", ""));
282 finder.SetLimitsSince(since);
283 finder.SetLimitsCount(limit);
284 }
285
286 Json::Value answer;
287 finder.Execute(answer, context, OrthancRestApi::GetDicomFormat(call, DicomToJsonFormat_Human), false /* no "Metadata" field */);
288 call.GetOutput().AnswerJson(answer);
254 } 289 }
255 else 290 else
256 { 291 {
257 index.GetAllUuids(result, resourceType); 292 /**
258 } 293 * VERSION IN ORTHANC <= 1.12.4
259 294 **/
260 AnswerListOfResources(call.GetOutput(), context, result, resourceType, call.HasArgument("expand") && call.GetBooleanArgument("expand", true), 295
261 OrthancRestApi::GetDicomFormat(call, DicomToJsonFormat_Human), 296 std::list<std::string> result;
262 requestedTags, 297
263 true /* allowStorageAccess */); 298 std::set<DicomTag> requestedTags;
299 OrthancRestApi::GetRequestedTags(requestedTags, call);
300
301 if (call.HasArgument("limit") ||
302 call.HasArgument("since"))
303 {
304 if (!call.HasArgument("limit"))
305 {
306 throw OrthancException(ErrorCode_BadRequest,
307 "Missing \"limit\" argument for GET request against: " +
308 call.FlattenUri());
309 }
310
311 if (!call.HasArgument("since"))
312 {
313 throw OrthancException(ErrorCode_BadRequest,
314 "Missing \"since\" argument for GET request against: " +
315 call.FlattenUri());
316 }
317
318 size_t since = boost::lexical_cast<size_t>(call.GetArgument("since", ""));
319 size_t limit = boost::lexical_cast<size_t>(call.GetArgument("limit", ""));
320 index.GetAllUuids(result, resourceType, since, limit);
321 }
322 else
323 {
324 index.GetAllUuids(result, resourceType);
325 }
326
327 AnswerListOfResources2(call.GetOutput(), context, result, resourceType, call.HasArgument("expand") && call.GetBooleanArgument("expand", true),
328 OrthancRestApi::GetDicomFormat(call, DicomToJsonFormat_Human),
329 requestedTags,
330 true /* allowStorageAccess */);
331 }
264 } 332 }
265 333
266 334
267 335
268 template <enum ResourceType resourceType> 336 template <enum ResourceType resourceType>
287 const DicomToJsonFormat format = OrthancRestApi::GetDicomFormat(call, DicomToJsonFormat_Human); 355 const DicomToJsonFormat format = OrthancRestApi::GetDicomFormat(call, DicomToJsonFormat_Human);
288 356
289 std::set<DicomTag> requestedTags; 357 std::set<DicomTag> requestedTags;
290 OrthancRestApi::GetRequestedTags(requestedTags, call); 358 OrthancRestApi::GetRequestedTags(requestedTags, call);
291 359
292 Json::Value json; 360 if (true)
293 if (OrthancRestApi::GetContext(call).ExpandResource( 361 {
294 json, call.GetUriComponent("id", ""), resourceType, format, requestedTags, true /* allowStorageAccess */)) 362 /**
295 { 363 * EXPERIMENTAL VERSION
296 call.GetOutput().AnswerJson(json); 364 **/
365
366 ResourceFinder finder(resourceType, true /* expand */);
367 finder.AddRequestedTags(requestedTags);
368 finder.SetOrthancId(resourceType, call.GetUriComponent("id", ""));
369
370 Json::Value json;
371 if (finder.ExecuteOneResource(json, OrthancRestApi::GetContext(call), format, false /* no "Metadata" field */))
372 {
373 call.GetOutput().AnswerJson(json);
374 }
375 }
376 else
377 {
378 /**
379 * VERSION IN ORTHANC <= 1.12.4
380 **/
381
382 Json::Value json;
383 if (OrthancRestApi::GetContext(call).ExpandResource(
384 json, call.GetUriComponent("id", ""), resourceType, format, requestedTags, true /* allowStorageAccess */))
385 {
386 call.GetOutput().AnswerJson(json);
387 }
297 } 388 }
298 } 389 }
299 390
300 template <enum ResourceType resourceType> 391 template <enum ResourceType resourceType>
301 static void DeleteSingleResource(RestApiDeleteCall& call) 392 static void DeleteSingleResource(RestApiDeleteCall& call)
3140 ServerContext& context, 3231 ServerContext& context,
3141 ResourceType level, 3232 ResourceType level,
3142 bool expand, 3233 bool expand,
3143 const std::set<DicomTag>& requestedTags) const 3234 const std::set<DicomTag>& requestedTags) const
3144 { 3235 {
3145 AnswerListOfResources(output, context, resources_, instancesIds_, resourcesMainDicomTags_, resourcesDicomAsJson_, level, expand, format_, requestedTags, IsStorageAccessAllowedForAnswers(findStorageAccessMode_)); 3236 AnswerListOfResources1(output, context, resources_, instancesIds_, resourcesMainDicomTags_, resourcesDicomAsJson_, level, expand, format_, requestedTags, IsStorageAccessAllowedForAnswers(findStorageAccessMode_));
3146 } 3237 }
3147 }; 3238 };
3148 } 3239 }
3149 3240
3150 3241
3252 request[KEY_LABELS_CONSTRAINT].type() != Json::stringValue) 3343 request[KEY_LABELS_CONSTRAINT].type() != Json::stringValue)
3253 { 3344 {
3254 throw OrthancException(ErrorCode_BadRequest, 3345 throw OrthancException(ErrorCode_BadRequest,
3255 "Field \"" + std::string(KEY_LABELS_CONSTRAINT) + "\" must be an array of strings"); 3346 "Field \"" + std::string(KEY_LABELS_CONSTRAINT) + "\" must be an array of strings");
3256 } 3347 }
3348 else if (true)
3349 {
3350 /**
3351 * EXPERIMENTAL VERSION
3352 **/
3353
3354 bool expand = false;
3355 if (request.isMember(KEY_EXPAND))
3356 {
3357 expand = request[KEY_EXPAND].asBool();
3358 }
3359
3360 const ResourceType level = StringToResourceType(request[KEY_LEVEL].asCString());
3361
3362 ResourceFinder finder(level, expand);
3363 finder.SetDatabaseLimits(context.GetDatabaseLimits(level));
3364
3365 const DicomToJsonFormat format = OrthancRestApi::GetDicomFormat(request, DicomToJsonFormat_Human);
3366
3367 if (request.isMember(KEY_LIMIT))
3368 {
3369 int64_t tmp = request[KEY_LIMIT].asInt64();
3370 if (tmp < 0)
3371 {
3372 throw OrthancException(ErrorCode_ParameterOutOfRange,
3373 "Field \"" + std::string(KEY_LIMIT) + "\" must be a positive integer");
3374 }
3375 else if (tmp != 0) // This is for compatibility with Orthanc 1.12.4
3376 {
3377 finder.SetLimitsCount(static_cast<uint64_t>(tmp));
3378 }
3379 }
3380
3381 if (request.isMember(KEY_SINCE))
3382 {
3383 int64_t tmp = request[KEY_SINCE].asInt64();
3384 if (tmp < 0)
3385 {
3386 throw OrthancException(ErrorCode_ParameterOutOfRange,
3387 "Field \"" + std::string(KEY_SINCE) + "\" must be a positive integer");
3388 }
3389 else
3390 {
3391 finder.SetLimitsSince(static_cast<uint64_t>(tmp));
3392 }
3393 }
3394
3395 {
3396 bool caseSensitive = false;
3397 if (request.isMember(KEY_CASE_SENSITIVE))
3398 {
3399 caseSensitive = request[KEY_CASE_SENSITIVE].asBool();
3400 }
3401
3402 DatabaseLookup query;
3403
3404 Json::Value::Members members = request[KEY_QUERY].getMemberNames();
3405 for (size_t i = 0; i < members.size(); i++)
3406 {
3407 if (request[KEY_QUERY][members[i]].type() != Json::stringValue)
3408 {
3409 throw OrthancException(ErrorCode_BadRequest,
3410 "Tag \"" + members[i] + "\" must be associated with a string");
3411 }
3412
3413 const std::string value = request[KEY_QUERY][members[i]].asString();
3414
3415 if (!value.empty())
3416 {
3417 // An empty string corresponds to an universal constraint,
3418 // so we ignore it. This mimics the behavior of class
3419 // "OrthancFindRequestHandler"
3420 query.AddRestConstraint(FromDcmtkBridge::ParseTag(members[i]),
3421 value, caseSensitive, true);
3422 }
3423 }
3424
3425 finder.SetDatabaseLookup(query);
3426 }
3427
3428 if (request.isMember(KEY_REQUESTED_TAGS))
3429 {
3430 std::set<DicomTag> requestedTags;
3431 FromDcmtkBridge::ParseListOfTags(requestedTags, request[KEY_REQUESTED_TAGS]);
3432 finder.AddRequestedTags(requestedTags);
3433 }
3434
3435 if (request.isMember(KEY_LABELS)) // New in Orthanc 1.12.0
3436 {
3437 for (Json::Value::ArrayIndex i = 0; i < request[KEY_LABELS].size(); i++)
3438 {
3439 if (request[KEY_LABELS][i].type() != Json::stringValue)
3440 {
3441 throw OrthancException(ErrorCode_BadRequest, "Field \"" + std::string(KEY_LABELS) + "\" must contain strings");
3442 }
3443 else
3444 {
3445 finder.AddLabel(request[KEY_LABELS][i].asString());
3446 }
3447 }
3448 }
3449
3450 finder.SetLabelsConstraint(LabelsConstraint_All);
3451
3452 if (request.isMember(KEY_LABELS_CONSTRAINT))
3453 {
3454 const std::string& s = request[KEY_LABELS_CONSTRAINT].asString();
3455 if (s == "All")
3456 {
3457 finder.SetLabelsConstraint(LabelsConstraint_All);
3458 }
3459 else if (s == "Any")
3460 {
3461 finder.SetLabelsConstraint(LabelsConstraint_Any);
3462 }
3463 else if (s == "None")
3464 {
3465 finder.SetLabelsConstraint(LabelsConstraint_None);
3466 }
3467 else
3468 {
3469 throw OrthancException(ErrorCode_BadRequest, "Field \"" + std::string(KEY_LABELS_CONSTRAINT) + "\" must be \"All\", \"Any\", or \"None\"");
3470 }
3471 }
3472
3473 Json::Value answer;
3474 finder.Execute(answer, context, format, false /* no "Metadata" field */);
3475 call.GetOutput().AnswerJson(answer);
3476 }
3257 else 3477 else
3258 { 3478 {
3479 /**
3480 * VERSION IN ORTHANC <= 1.12.4
3481 **/
3259 bool expand = false; 3482 bool expand = false;
3260 if (request.isMember(KEY_EXPAND)) 3483 if (request.isMember(KEY_EXPAND))
3261 { 3484 {
3262 expand = request[KEY_EXPAND].asBool(); 3485 expand = request[KEY_EXPAND].asBool();
3263 } 3486 }
3398 } 3621 }
3399 3622
3400 ServerIndex& index = OrthancRestApi::GetIndex(call); 3623 ServerIndex& index = OrthancRestApi::GetIndex(call);
3401 ServerContext& context = OrthancRestApi::GetContext(call); 3624 ServerContext& context = OrthancRestApi::GetContext(call);
3402 3625
3626 const bool expand = (!call.HasArgument("expand") ||
3627 // this "expand" is the only one to have a false default value to keep backward compatibility
3628 call.GetBooleanArgument("expand", false));
3629 const DicomToJsonFormat format = OrthancRestApi::GetDicomFormat(call, DicomToJsonFormat_Human);
3630
3403 std::set<DicomTag> requestedTags; 3631 std::set<DicomTag> requestedTags;
3404 OrthancRestApi::GetRequestedTags(requestedTags, call); 3632 OrthancRestApi::GetRequestedTags(requestedTags, call);
3405 3633
3406 std::list<std::string> a, b, c; 3634 if (true)
3407 a.push_back(call.GetUriComponent("id", "")); 3635 {
3408 3636 /**
3409 ResourceType type = start; 3637 * EXPERIMENTAL VERSION
3410 while (type != end) 3638 **/
3411 { 3639
3412 b.clear(); 3640 ResourceFinder finder(end, expand);
3413 3641 finder.SetOrthancId(start, call.GetUriComponent("id", ""));
3414 for (std::list<std::string>::const_iterator 3642 finder.AddRequestedTags(requestedTags);
3415 it = a.begin(); it != a.end(); ++it) 3643
3416 { 3644 Json::Value answer;
3417 index.GetChildren(c, *it); 3645 finder.Execute(answer, context, format, false /* no "Metadata" field */);
3418 b.splice(b.begin(), c); 3646 call.GetOutput().AnswerJson(answer);
3419 } 3647 }
3420 3648 else
3421 type = GetChildResourceType(type); 3649 {
3422 3650 /**
3423 a.clear(); 3651 * VERSION IN ORTHANC <= 1.12.4
3424 a.splice(a.begin(), b); 3652 **/
3425 } 3653 std::list<std::string> a, b, c;
3426 3654 a.push_back(call.GetUriComponent("id", ""));
3427 AnswerListOfResources(call.GetOutput(), context, a, type, !call.HasArgument("expand") || call.GetBooleanArgument("expand", false), // this "expand" is the only one to have a false default value to keep backward compatibility 3655
3428 OrthancRestApi::GetDicomFormat(call, DicomToJsonFormat_Human), 3656 ResourceType type = start;
3429 requestedTags, 3657 while (type != end)
3430 true /* allowStorageAccess */); 3658 {
3659 b.clear();
3660
3661 for (std::list<std::string>::const_iterator
3662 it = a.begin(); it != a.end(); ++it)
3663 {
3664 index.GetChildren(c, *it);
3665 b.splice(b.begin(), c);
3666 }
3667
3668 type = GetChildResourceType(type);
3669
3670 a.clear();
3671 a.splice(a.begin(), b);
3672 }
3673
3674 AnswerListOfResources2(call.GetOutput(), context, a, type, expand, format, requestedTags, true /* allowStorageAccess */);
3675 }
3431 } 3676 }
3432 3677
3433 3678
3434 static void GetChildInstancesTags(RestApiGetCall& call) 3679 static void GetChildInstancesTags(RestApiGetCall& call)
3435 { 3680 {
3538 assert(currentType == end); 3783 assert(currentType == end);
3539 3784
3540 const DicomToJsonFormat format = OrthancRestApi::GetDicomFormat(call, DicomToJsonFormat_Human); 3785 const DicomToJsonFormat format = OrthancRestApi::GetDicomFormat(call, DicomToJsonFormat_Human);
3541 3786
3542 Json::Value resource; 3787 Json::Value resource;
3543 if (OrthancRestApi::GetContext(call).ExpandResource(resource, current, end, format, requestedTags, true /* allowStorageAccess */)) 3788
3544 { 3789 if (true)
3545 call.GetOutput().AnswerJson(resource); 3790 {
3791 /**
3792 * EXPERIMENTAL VERSION
3793 **/
3794 if (ExpandResource(resource, OrthancRestApi::GetContext(call), currentType, current, format, false))
3795 {
3796 call.GetOutput().AnswerJson(resource);
3797 }
3798 }
3799 else
3800 {
3801 /**
3802 * VERSION IN ORTHANC <= 1.12.4
3803 **/
3804 if (OrthancRestApi::GetContext(call).ExpandResource(resource, current, end, format, requestedTags, true /* allowStorageAccess */))
3805 {
3806 call.GetOutput().AnswerJson(resource);
3807 }
3546 } 3808 }
3547 } 3809 }
3548 3810
3549 3811
3550 static void ExtractPdf(RestApiGetCall& call) 3812 static void ExtractPdf(RestApiGetCall& call)
3971 } 4233 }
3972 4234
3973 for (std::set<std::string>::const_iterator 4235 for (std::set<std::string>::const_iterator
3974 it = interest.begin(); it != interest.end(); ++it) 4236 it = interest.begin(); it != interest.end(); ++it)
3975 { 4237 {
3976 Json::Value item; 4238 if (true)
3977 std::set<DicomTag> emptyRequestedTags; // not supported for bulk content
3978
3979 if (OrthancRestApi::GetContext(call).ExpandResource(item, *it, level, format, emptyRequestedTags, true /* allowStorageAccess */))
3980 { 4239 {
3981 if (metadata) 4240 /**
4241 * EXPERIMENTAL VERSION
4242 **/
4243 Json::Value item;
4244 if (ExpandResource(item, OrthancRestApi::GetContext(call), level, *it, format, metadata))
3982 { 4245 {
3983 AddMetadata(item[METADATA], index, *it, level); 4246 answer.append(item);
3984 } 4247 }
3985 4248 }
3986 answer.append(item); 4249 else
4250 {
4251 /**
4252 * VERSION IN ORTHANC <= 1.12.4
4253 **/
4254 Json::Value item;
4255 std::set<DicomTag> emptyRequestedTags; // not supported for bulk content
4256
4257 if (OrthancRestApi::GetContext(call).ExpandResource(item, *it, level, format, emptyRequestedTags, true /* allowStorageAccess */))
4258 {
4259 if (metadata)
4260 {
4261 AddMetadata(item[METADATA], index, *it, level);
4262 }
4263
4264 answer.append(item);
4265 }
3987 } 4266 }
3988 } 4267 }
3989 } 4268 }
3990 else 4269 else
3991 { 4270 {
3998 { 4277 {
3999 ResourceType level; 4278 ResourceType level;
4000 Json::Value item; 4279 Json::Value item;
4001 std::set<DicomTag> emptyRequestedTags; // not supported for bulk content 4280 std::set<DicomTag> emptyRequestedTags; // not supported for bulk content
4002 4281
4003 if (index.LookupResourceType(level, *it) && 4282 if (true)
4004 OrthancRestApi::GetContext(call).ExpandResource(item, *it, level, format, emptyRequestedTags, true /* allowStorageAccess */))
4005 { 4283 {
4006 if (metadata) 4284 /**
4285 * EXPERIMENTAL VERSION
4286 **/
4287 if (index.LookupResourceType(level, *it) &&
4288 ExpandResource(item, OrthancRestApi::GetContext(call), level, *it, format, metadata))
4007 { 4289 {
4008 AddMetadata(item[METADATA], index, *it, level); 4290 answer.append(item);
4009 } 4291 }
4010
4011 answer.append(item);
4012 } 4292 }
4013 else 4293 else
4014 { 4294 {
4015 CLOG(INFO, HTTP) << "Unknown resource during a bulk content retrieval: " << *it; 4295 /**
4296 * VERSION IN ORTHANC <= 1.12.4
4297 **/
4298 if (index.LookupResourceType(level, *it) &&
4299 OrthancRestApi::GetContext(call).ExpandResource(item, *it, level, format, emptyRequestedTags, true /* allowStorageAccess */))
4300 {
4301 if (metadata)
4302 {
4303 AddMetadata(item[METADATA], index, *it, level);
4304 }
4305
4306 answer.append(item);
4307 }
4308 else
4309 {
4310 CLOG(INFO, HTTP) << "Unknown resource during a bulk content retrieval: " << *it;
4311 }
4016 } 4312 }
4017 } 4313 }
4018 } 4314 }
4019 4315
4020 call.GetOutput().AnswerJson(answer); 4316 call.GetOutput().AnswerJson(answer);
4073 Register("/instances", ListResources<ResourceType_Instance>); 4369 Register("/instances", ListResources<ResourceType_Instance>);
4074 Register("/patients", ListResources<ResourceType_Patient>); 4370 Register("/patients", ListResources<ResourceType_Patient>);
4075 Register("/series", ListResources<ResourceType_Series>); 4371 Register("/series", ListResources<ResourceType_Series>);
4076 Register("/studies", ListResources<ResourceType_Study>); 4372 Register("/studies", ListResources<ResourceType_Study>);
4077 4373
4078 Register("/instances/{id}", DeleteSingleResource<ResourceType_Instance>); 4374 if (!context_.IsReadOnly())
4375 {
4376 Register("/instances/{id}", DeleteSingleResource<ResourceType_Instance>);
4377 Register("/patients/{id}", DeleteSingleResource<ResourceType_Patient>);
4378 Register("/series/{id}", DeleteSingleResource<ResourceType_Series>);
4379 Register("/studies/{id}", DeleteSingleResource<ResourceType_Study>);
4380
4381 Register("/tools/bulk-delete", BulkDelete);
4382 }
4383 else
4384 {
4385 LOG(WARNING) << "READ-ONLY SYSTEM: DELETE routes are not available";
4386 }
4387
4079 Register("/instances/{id}", GetSingleResource<ResourceType_Instance>); 4388 Register("/instances/{id}", GetSingleResource<ResourceType_Instance>);
4080 Register("/patients/{id}", DeleteSingleResource<ResourceType_Patient>);
4081 Register("/patients/{id}", GetSingleResource<ResourceType_Patient>); 4389 Register("/patients/{id}", GetSingleResource<ResourceType_Patient>);
4082 Register("/series/{id}", DeleteSingleResource<ResourceType_Series>);
4083 Register("/series/{id}", GetSingleResource<ResourceType_Series>); 4390 Register("/series/{id}", GetSingleResource<ResourceType_Series>);
4084 Register("/studies/{id}", DeleteSingleResource<ResourceType_Study>);
4085 Register("/studies/{id}", GetSingleResource<ResourceType_Study>); 4391 Register("/studies/{id}", GetSingleResource<ResourceType_Study>);
4086 4392
4087 Register("/instances/{id}/statistics", GetResourceStatistics); 4393 Register("/instances/{id}/statistics", GetResourceStatistics);
4088 Register("/patients/{id}/statistics", GetResourceStatistics); 4394 Register("/patients/{id}/statistics", GetResourceStatistics);
4089 Register("/studies/{id}/statistics", GetResourceStatistics); 4395 Register("/studies/{id}/statistics", GetResourceStatistics);
4124 Register("/instances/{id}/matlab", GetMatlabImage); 4430 Register("/instances/{id}/matlab", GetMatlabImage);
4125 Register("/instances/{id}/header", GetInstanceHeader); 4431 Register("/instances/{id}/header", GetInstanceHeader);
4126 Register("/instances/{id}/numpy", GetNumpyInstance); // New in Orthanc 1.10.0 4432 Register("/instances/{id}/numpy", GetNumpyInstance); // New in Orthanc 1.10.0
4127 4433
4128 Register("/patients/{id}/protected", IsProtectedPatient); 4434 Register("/patients/{id}/protected", IsProtectedPatient);
4129 Register("/patients/{id}/protected", SetPatientProtection); 4435
4436 if (!context_.IsReadOnly())
4437 {
4438 Register("/patients/{id}/protected", SetPatientProtection);
4439 }
4440 else
4441 {
4442 LOG(WARNING) << "READ-ONLY SYSTEM: PUT /patients/{id}/protected route is not available";
4443 }
4444
4130 4445
4131 std::vector<std::string> resourceTypes; 4446 std::vector<std::string> resourceTypes;
4132 resourceTypes.push_back("patients"); 4447 resourceTypes.push_back("patients");
4133 resourceTypes.push_back("studies"); 4448 resourceTypes.push_back("studies");
4134 resourceTypes.push_back("series"); 4449 resourceTypes.push_back("series");
4142 Register("/" + resourceTypes[i] + "/{id}/metadata/{name}", SetMetadata); 4457 Register("/" + resourceTypes[i] + "/{id}/metadata/{name}", SetMetadata);
4143 4458
4144 // New in Orthanc 1.12.0 4459 // New in Orthanc 1.12.0
4145 Register("/" + resourceTypes[i] + "/{id}/labels", ListLabels); 4460 Register("/" + resourceTypes[i] + "/{id}/labels", ListLabels);
4146 Register("/" + resourceTypes[i] + "/{id}/labels/{label}", GetLabel); 4461 Register("/" + resourceTypes[i] + "/{id}/labels/{label}", GetLabel);
4147 Register("/" + resourceTypes[i] + "/{id}/labels/{label}", RemoveLabel); 4462
4148 Register("/" + resourceTypes[i] + "/{id}/labels/{label}", AddLabel); 4463 if (!context_.IsReadOnly())
4464 {
4465 Register("/" + resourceTypes[i] + "/{id}/labels/{label}", RemoveLabel);
4466 Register("/" + resourceTypes[i] + "/{id}/labels/{label}", AddLabel);
4467 }
4149 4468
4150 Register("/" + resourceTypes[i] + "/{id}/attachments", ListAttachments); 4469 Register("/" + resourceTypes[i] + "/{id}/attachments", ListAttachments);
4151 Register("/" + resourceTypes[i] + "/{id}/attachments/{name}", DeleteAttachment);
4152 Register("/" + resourceTypes[i] + "/{id}/attachments/{name}", GetAttachmentOperations); 4470 Register("/" + resourceTypes[i] + "/{id}/attachments/{name}", GetAttachmentOperations);
4153 Register("/" + resourceTypes[i] + "/{id}/attachments/{name}", UploadAttachment);
4154 Register("/" + resourceTypes[i] + "/{id}/attachments/{name}/compress", ChangeAttachmentCompression<CompressionType_ZlibWithSize>);
4155 Register("/" + resourceTypes[i] + "/{id}/attachments/{name}/compressed-data", GetAttachmentData<0>); 4471 Register("/" + resourceTypes[i] + "/{id}/attachments/{name}/compressed-data", GetAttachmentData<0>);
4156 Register("/" + resourceTypes[i] + "/{id}/attachments/{name}/compressed-md5", GetAttachmentCompressedMD5); 4472 Register("/" + resourceTypes[i] + "/{id}/attachments/{name}/compressed-md5", GetAttachmentCompressedMD5);
4157 Register("/" + resourceTypes[i] + "/{id}/attachments/{name}/compressed-size", GetAttachmentCompressedSize); 4473 Register("/" + resourceTypes[i] + "/{id}/attachments/{name}/compressed-size", GetAttachmentCompressedSize);
4158 Register("/" + resourceTypes[i] + "/{id}/attachments/{name}/data", GetAttachmentData<1>); 4474 Register("/" + resourceTypes[i] + "/{id}/attachments/{name}/data", GetAttachmentData<1>);
4159 Register("/" + resourceTypes[i] + "/{id}/attachments/{name}/is-compressed", IsAttachmentCompressed); 4475 Register("/" + resourceTypes[i] + "/{id}/attachments/{name}/is-compressed", IsAttachmentCompressed);
4160 Register("/" + resourceTypes[i] + "/{id}/attachments/{name}/md5", GetAttachmentMD5); 4476 Register("/" + resourceTypes[i] + "/{id}/attachments/{name}/md5", GetAttachmentMD5);
4161 Register("/" + resourceTypes[i] + "/{id}/attachments/{name}/size", GetAttachmentSize); 4477 Register("/" + resourceTypes[i] + "/{id}/attachments/{name}/size", GetAttachmentSize);
4162 Register("/" + resourceTypes[i] + "/{id}/attachments/{name}/uncompress", ChangeAttachmentCompression<CompressionType_None>);
4163 Register("/" + resourceTypes[i] + "/{id}/attachments/{name}/info", GetAttachmentInfo); 4478 Register("/" + resourceTypes[i] + "/{id}/attachments/{name}/info", GetAttachmentInfo);
4164 Register("/" + resourceTypes[i] + "/{id}/attachments/{name}/verify-md5", VerifyAttachment); 4479 Register("/" + resourceTypes[i] + "/{id}/attachments/{name}/verify-md5", VerifyAttachment);
4165 } 4480
4166 4481 if (!context_.IsReadOnly())
4167 Register("/tools/invalidate-tags", InvalidateTags); 4482 {
4483 Register("/" + resourceTypes[i] + "/{id}/attachments/{name}", DeleteAttachment);
4484 Register("/" + resourceTypes[i] + "/{id}/attachments/{name}", UploadAttachment);
4485 Register("/" + resourceTypes[i] + "/{id}/attachments/{name}/compress", ChangeAttachmentCompression<CompressionType_ZlibWithSize>);
4486 Register("/" + resourceTypes[i] + "/{id}/attachments/{name}/uncompress", ChangeAttachmentCompression<CompressionType_None>);
4487 }
4488 }
4489
4490 if (context_.IsReadOnly())
4491 {
4492 LOG(WARNING) << "READ-ONLY SYSTEM: deactivating PUT, POST and DELETE attachments routes";
4493 LOG(WARNING) << "READ-ONLY SYSTEM: deactivating PUT and DELETE labels routes";
4494 }
4495
4496 if (!context_.IsReadOnly())
4497 {
4498 Register("/tools/invalidate-tags", InvalidateTags);
4499 }
4500
4168 Register("/tools/lookup", Lookup); 4501 Register("/tools/lookup", Lookup);
4169 Register("/tools/find", Find); 4502 Register("/tools/find", Find);
4170 4503
4171 Register("/patients/{id}/studies", GetChildResources<ResourceType_Patient, ResourceType_Study>); 4504 Register("/patients/{id}/studies", GetChildResources<ResourceType_Patient, ResourceType_Study>);
4172 Register("/patients/{id}/series", GetChildResources<ResourceType_Patient, ResourceType_Series>); 4505 Register("/patients/{id}/series", GetChildResources<ResourceType_Patient, ResourceType_Series>);
4189 Register("/instances/{id}/content/*", GetRawContent); 4522 Register("/instances/{id}/content/*", GetRawContent);
4190 4523
4191 Register("/series/{id}/ordered-slices", OrderSlices); 4524 Register("/series/{id}/ordered-slices", OrderSlices);
4192 Register("/series/{id}/numpy", GetNumpySeries); // New in Orthanc 1.10.0 4525 Register("/series/{id}/numpy", GetNumpySeries); // New in Orthanc 1.10.0
4193 4526
4194 Register("/patients/{id}/reconstruct", ReconstructResource<ResourceType_Patient>); 4527 if (!context_.IsReadOnly())
4195 Register("/studies/{id}/reconstruct", ReconstructResource<ResourceType_Study>); 4528 {
4196 Register("/series/{id}/reconstruct", ReconstructResource<ResourceType_Series>); 4529 Register("/patients/{id}/reconstruct", ReconstructResource<ResourceType_Patient>);
4197 Register("/instances/{id}/reconstruct", ReconstructResource<ResourceType_Instance>); 4530 Register("/studies/{id}/reconstruct", ReconstructResource<ResourceType_Study>);
4198 Register("/tools/reconstruct", ReconstructAllResources); 4531 Register("/series/{id}/reconstruct", ReconstructResource<ResourceType_Series>);
4532 Register("/instances/{id}/reconstruct", ReconstructResource<ResourceType_Instance>);
4533 Register("/tools/reconstruct", ReconstructAllResources);
4534 }
4535 else
4536 {
4537 LOG(WARNING) << "READ-ONLY SYSTEM: deactivating /reconstruct routes";
4538 }
4199 4539
4200 Register("/tools/bulk-content", BulkContent); 4540 Register("/tools/bulk-content", BulkContent);
4201 Register("/tools/bulk-delete", BulkDelete); 4541 }
4202 }
4203 } 4542 }