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-2021 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 "../ServerContext.h"
38 
39 namespace Orthanc
40 {
41   // Changes API --------------------------------------------------------------
42 
GetSinceAndLimit(int64_t & since,unsigned int & limit,bool & last,const RestApiGetCall & call)43   static void GetSinceAndLimit(int64_t& since,
44                                unsigned int& limit,
45                                bool& last,
46                                const RestApiGetCall& call)
47   {
48     static const unsigned int DEFAULT_LIMIT = 100;
49 
50     if (call.HasArgument("last"))
51     {
52       last = true;
53       return;
54     }
55 
56     last = false;
57 
58     try
59     {
60       since = boost::lexical_cast<int64_t>(call.GetArgument("since", "0"));
61       limit = boost::lexical_cast<unsigned int>(call.GetArgument("limit", boost::lexical_cast<std::string>(DEFAULT_LIMIT)));
62     }
63     catch (boost::bad_lexical_cast&)
64     {
65       since = 0;
66       limit = DEFAULT_LIMIT;
67       return;
68     }
69   }
70 
GetChanges(RestApiGetCall & call)71   static void GetChanges(RestApiGetCall& call)
72   {
73     if (call.IsDocumentation())
74     {
75       call.GetDocumentation()
76         .SetTag("Tracking changes")
77         .SetSummary("List changes")
78         .SetDescription("Whenever Orthanc receives a new DICOM instance, this event is recorded in the so-called _Changes Log_. This enables remote scripts to react to the arrival of new DICOM resources. A typical application is auto-routing, where an external script waits for a new DICOM instance to arrive into Orthanc, then forward this instance to another modality.")
79         .SetHttpGetArgument("limit", RestApiCallDocumentation::Type_Number, "Limit the number of results", false)
80         .SetHttpGetArgument("since", RestApiCallDocumentation::Type_Number, "Show only the resources since the provided index", false)
81         .AddAnswerType(MimeType_Json, "The list of changes")
82         .SetAnswerField("Changes", RestApiCallDocumentation::Type_JsonListOfObjects, "The individual changes")
83         .SetAnswerField("Done", RestApiCallDocumentation::Type_Boolean,
84                         "Whether the last reported change is the last of the full history")
85         .SetAnswerField("Last", RestApiCallDocumentation::Type_Number,
86                         "The index of the last reported change, can be used for the `since` argument in subsequent calls to this route")
87         .SetHttpGetSample("https://demo.orthanc-server.com/changes?since=0&limit=2", true);
88       return;
89     }
90 
91     ServerContext& context = OrthancRestApi::GetContext(call);
92 
93     //std::string filter = GetArgument(getArguments, "filter", "");
94     int64_t since;
95     unsigned int limit;
96     bool last;
97     GetSinceAndLimit(since, limit, last, call);
98 
99     Json::Value result;
100     if (last)
101     {
102       context.GetIndex().GetLastChange(result);
103     }
104     else
105     {
106       context.GetIndex().GetChanges(result, since, limit);
107     }
108 
109     call.GetOutput().AnswerJson(result);
110   }
111 
112 
DeleteChanges(RestApiDeleteCall & call)113   static void DeleteChanges(RestApiDeleteCall& call)
114   {
115     if (call.IsDocumentation())
116     {
117       call.GetDocumentation()
118         .SetTag("Tracking changes")
119         .SetSummary("Clear changes")
120         .SetDescription("Clear the full history stored in the changes log");
121       return;
122     }
123 
124     OrthancRestApi::GetIndex(call).DeleteChanges();
125     call.GetOutput().AnswerBuffer("", MimeType_PlainText);
126   }
127 
128 
129   // Exports API --------------------------------------------------------------
130 
GetExports(RestApiGetCall & call)131   static void GetExports(RestApiGetCall& call)
132   {
133     if (call.IsDocumentation())
134     {
135       call.GetDocumentation()
136         .SetTag("Tracking changes")
137         .SetSummary("List exports")
138         .SetDescription("For medical traceability, Orthanc can be configured to store a log of all the resources "
139                         "that have been exported to remote modalities. In auto-routing scenarios, it is important "
140                         "to prevent this log to grow indefinitely as incoming instances are routed. You can either "
141                         "disable this logging by setting the option `LogExportedResources` to `false` in the "
142                         "configuration file, or periodically clear this log by `DELETE`-ing this URI. This route "
143                         "might be removed in future versions of Orthanc.")
144         .SetHttpGetArgument("limit", RestApiCallDocumentation::Type_Number, "Limit the number of results", false)
145         .SetHttpGetArgument("since", RestApiCallDocumentation::Type_Number, "Show only the resources since the provided index", false)
146         .AddAnswerType(MimeType_Json, "The list of exports");
147       return;
148     }
149 
150     ServerContext& context = OrthancRestApi::GetContext(call);
151 
152     int64_t since;
153     unsigned int limit;
154     bool last;
155     GetSinceAndLimit(since, limit, last, call);
156 
157     Json::Value result;
158     if (last)
159     {
160       context.GetIndex().GetLastExportedResource(result);
161     }
162     else
163     {
164       context.GetIndex().GetExportedResources(result, since, limit);
165     }
166 
167     call.GetOutput().AnswerJson(result);
168   }
169 
170 
DeleteExports(RestApiDeleteCall & call)171   static void DeleteExports(RestApiDeleteCall& call)
172   {
173     if (call.IsDocumentation())
174     {
175       call.GetDocumentation()
176         .SetTag("Tracking changes")
177         .SetSummary("Clear exports")
178         .SetDescription("Clear the full history stored in the exports log");
179       return;
180     }
181 
182     OrthancRestApi::GetIndex(call).DeleteExportedResources();
183     call.GetOutput().AnswerBuffer("", MimeType_PlainText);
184   }
185 
186 
RegisterChanges()187   void OrthancRestApi::RegisterChanges()
188   {
189     Register("/changes", GetChanges);
190     Register("/changes", DeleteChanges);
191     Register("/exports", GetExports);
192     Register("/exports", DeleteExports);
193   }
194 }
195