comparison OrthancServer/Plugins/Samples/MultitenantDicom/PluginToolbox.cpp @ 5273:7cb1b851f5c8

Added a sample plugin bringing multitenant DICOM support through labels
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 14 Apr 2023 11:49:24 +0200
parents
children a8385880902f
comparison
equal deleted inserted replaced
5272:a45e8b6115f6 5273:7cb1b851f5c8
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-2023 Osimis S.A., Belgium
6 * Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
7 *
8 * This program is free software: you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * as published by the Free Software Foundation, either version 3 of
11 * the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this program. If not, see
20 * <http://www.gnu.org/licenses/>.
21 **/
22
23
24 #include "PluginToolbox.h"
25
26 #include "../../../../OrthancFramework/Sources/OrthancException.h"
27 #include "../../../../OrthancFramework/Sources/SerializationToolbox.h"
28 #include "../../../../OrthancFramework/Sources/Toolbox.h"
29
30 #include "../Common/OrthancPluginCppWrapper.h"
31
32
33 namespace PluginToolbox
34 {
35 bool IsValidLabel(const std::string& label)
36 {
37 if (label.empty())
38 {
39 return false;
40 }
41
42 if (label.size() > 64)
43 {
44 // This limitation is for MySQL, which cannot use a TEXT
45 // column of undefined length as a primary key
46 return false;
47 }
48
49 for (size_t i = 0; i < label.size(); i++)
50 {
51 if (!(label[i] == '_' ||
52 label[i] == '-' ||
53 (label[i] >= 'a' && label[i] <= 'z') ||
54 (label[i] >= 'A' && label[i] <= 'Z') ||
55 (label[i] >= '0' && label[i] <= '9')))
56 {
57 return false;
58 }
59 }
60
61 return true;
62 }
63
64
65 Orthanc::ResourceType ParseQueryRetrieveLevel(const std::string& level)
66 {
67 if (level == "PATIENT")
68 {
69 return Orthanc::ResourceType_Patient;
70 }
71 else if (level == "STUDY")
72 {
73 return Orthanc::ResourceType_Study;
74 }
75 else if (level == "SERIES")
76 {
77 return Orthanc::ResourceType_Series;
78 }
79 else if (level == "INSTANCE")
80 {
81 return Orthanc::ResourceType_Instance;
82 }
83 else
84 {
85 throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol, "Bad value for QueryRetrieveLevel in DICOM C-FIND: " + level);
86 }
87 }
88
89
90 bool IsSameAETitle(bool isStrict,
91 const std::string& aet1,
92 const std::string& aet2)
93 {
94 if (isStrict)
95 {
96 // Case-sensitive matching
97 return aet1 == aet2;
98 }
99 else
100 {
101 // Case-insensitive matching (default)
102 std::string tmp1, tmp2;
103 Orthanc::Toolbox::ToLowerCase(tmp1, aet1);
104 Orthanc::Toolbox::ToLowerCase(tmp2, aet2);
105 return tmp1 == tmp2;
106 }
107 }
108
109
110 bool LookupAETitle(std::string& name,
111 Orthanc::RemoteModalityParameters& parameters,
112 bool isStrict,
113 const std::string& aet)
114 {
115 Json::Value modalities;
116 if (!OrthancPlugins::RestApiGet(modalities, "/modalities?expand", false))
117 {
118 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Unable to obtain the list of the remote modalities");
119 }
120
121 std::vector<std::string> names = modalities.getMemberNames();
122 for (size_t i = 0; i < names.size(); i++)
123 {
124 parameters = Orthanc::RemoteModalityParameters(modalities[names[i]]);
125
126 if (IsSameAETitle(isStrict, parameters.GetApplicationEntityTitle(), aet))
127 {
128 name = names[i];
129 return true;
130 }
131 }
132
133 return false;
134 }
135
136
137 void ParseLabels(std::set<std::string>& targetLabels,
138 LabelsConstraint& targetConstraint,
139 const Json::Value& serverConfig)
140 {
141 Orthanc::SerializationToolbox::ReadSetOfStrings(targetLabels, serverConfig, KEY_LABELS);
142
143 for (std::set<std::string>::const_iterator it = targetLabels.begin(); it != targetLabels.end(); ++it)
144 {
145 if (!PluginToolbox::IsValidLabel(*it))
146 {
147 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange, "Invalid label: " + *it);
148 }
149 }
150
151 std::string s = Orthanc::SerializationToolbox::ReadString(serverConfig, KEY_LABELS_CONSTRAINT, KEY_ALL);
152 targetConstraint = PluginToolbox::StringToLabelsConstraint(s);
153 }
154
155
156 void AddLabelsToFindRequest(Json::Value& request,
157 const std::set<std::string>& labels,
158 LabelsConstraint constraint)
159 {
160 Json::Value items = Json::arrayValue;
161 for (std::set<std::string>::const_iterator it = labels.begin(); it != labels.end(); ++it)
162 {
163 items.append(*it);
164 }
165
166 request[KEY_LABELS] = items;
167
168 switch (constraint)
169 {
170 case LabelsConstraint_All:
171 request[KEY_LABELS_CONSTRAINT] = KEY_ALL;
172 break;
173
174 case LabelsConstraint_Any:
175 request[KEY_LABELS_CONSTRAINT] = KEY_ANY;
176 break;
177
178 case LabelsConstraint_None:
179 request[KEY_LABELS_CONSTRAINT] = KEY_NONE;
180 break;
181
182 default:
183 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
184 }
185 }
186
187
188 LabelsConstraint StringToLabelsConstraint(const std::string& s)
189 {
190 if (s == KEY_ALL)
191 {
192 return LabelsConstraint_All;
193 }
194 else if (s == KEY_ANY)
195 {
196 return LabelsConstraint_Any;
197 }
198 else if (s == KEY_NONE)
199 {
200 return LabelsConstraint_None;
201 }
202 else
203 {
204 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange, "Bad value for constraint of labels: " + s);
205 }
206 }
207 }