comparison OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp @ 5851:6a4e47163b3c find-refactoring

cleaning OrthancRestResources.cpp
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 29 Oct 2024 15:21:15 +0000
parents 1980354c8113
children
comparison
equal deleted inserted replaced
5850:1980354c8113 5851:6a4e47163b3c
66 static const char* const ARG_WHOLE = "whole"; 66 static const char* const ARG_WHOLE = "whole";
67 67
68 68
69 namespace Orthanc 69 namespace Orthanc
70 { 70 {
71 static ResourceType GetResourceTypeFromUri(const RestApiCall& call)
72 {
73 assert(!call.GetFullUri().empty());
74 const std::string resourceType = call.GetFullUri() [0];
75 return StringToResourceType(resourceType.c_str());
76 }
77
78
71 static std::string GetDocumentationSampleResource(ResourceType type) 79 static std::string GetDocumentationSampleResource(ResourceType type)
72 { 80 {
73 switch (type) 81 switch (type)
74 { 82 {
75 case Orthanc::ResourceType_Instance: 83 case Orthanc::ResourceType_Instance:
1574 } 1582 }
1575 1583
1576 1584
1577 static void GetResourceStatistics(RestApiGetCall& call) 1585 static void GetResourceStatistics(RestApiGetCall& call)
1578 { 1586 {
1579 if (call.IsDocumentation()) 1587 const ResourceType level = GetResourceTypeFromUri(call);
1580 { 1588
1581 ResourceType t = StringToResourceType(call.GetFullUri()[0].c_str()); 1589 if (call.IsDocumentation())
1582 std::string r = GetResourceTypeText(t, false /* plural */, false /* upper case */); 1590 {
1583 call.GetDocumentation() 1591 std::string r = GetResourceTypeText(level, false /* plural */, false /* upper case */);
1584 .SetTag(GetResourceTypeText(t, true /* plural */, true /* upper case */)) 1592 call.GetDocumentation()
1593 .SetTag(GetResourceTypeText(level, true /* plural */, true /* upper case */))
1585 .SetSummary("Get " + r + " statistics") 1594 .SetSummary("Get " + r + " statistics")
1586 .SetDescription("Get statistics about the given " + r) 1595 .SetDescription("Get statistics about the given " + r)
1587 .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest") 1596 .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest")
1588 .SetAnswerField("DiskSize", RestApiCallDocumentation::Type_String, 1597 .SetAnswerField("DiskSize", RestApiCallDocumentation::Type_String,
1589 "Size of the " + r + " on the disk in bytes, expressed as a string for 64bit compatibility with JSON") 1598 "Size of the " + r + " on the disk in bytes, expressed as a string for 64bit compatibility with JSON")
1600 "Size on the disk of the DICOM instances associated with the " + r + ", expressed in megabytes (MB)") 1609 "Size on the disk of the DICOM instances associated with the " + r + ", expressed in megabytes (MB)")
1601 .SetAnswerField("DicomUncompressedSize", RestApiCallDocumentation::Type_String, 1610 .SetAnswerField("DicomUncompressedSize", RestApiCallDocumentation::Type_String,
1602 "Size on the disk of the uncompressed DICOM instances associated with the " + r + ", expressed in bytes") 1611 "Size on the disk of the uncompressed DICOM instances associated with the " + r + ", expressed in bytes")
1603 .SetAnswerField("DicomUncompressedSizeMB", RestApiCallDocumentation::Type_Number, 1612 .SetAnswerField("DicomUncompressedSizeMB", RestApiCallDocumentation::Type_Number,
1604 "Size on the disk of the uncompressed DICOM instances associated with the " + r + ", expressed in megabytes (MB)") 1613 "Size on the disk of the uncompressed DICOM instances associated with the " + r + ", expressed in megabytes (MB)")
1605 .SetHttpGetSample(GetDocumentationSampleResource(t) + "/statistics", true); 1614 .SetHttpGetSample(GetDocumentationSampleResource(level) + "/statistics", true);
1606 1615
1607 switch (t) 1616 switch (level)
1608 { 1617 {
1609 // Do NOT add "break" below this point! 1618 // Do NOT add "break" below this point!
1610 case ResourceType_Patient: 1619 case ResourceType_Patient:
1611 call.GetDocumentation().SetAnswerField("CountStudies", RestApiCallDocumentation::Type_Number, 1620 call.GetDocumentation().SetAnswerField("CountStudies", RestApiCallDocumentation::Type_Number,
1612 "Number of child studies within this " + r); 1621 "Number of child studies within this " + r);
1671 1680
1672 1681
1673 1682
1674 // Handling of metadata ----------------------------------------------------- 1683 // Handling of metadata -----------------------------------------------------
1675 1684
1676 static ResourceType GetResourceTypeFromUri(const RestApiCall& call)
1677 {
1678 assert(!call.GetFullUri().empty());
1679 const std::string resourceType = call.GetFullUri() [0];
1680 return StringToResourceType(resourceType.c_str());
1681 }
1682
1683
1684 static void CheckValidResourceType(const RestApiCall& call)
1685 {
1686 GetResourceTypeFromUri(call);
1687 }
1688
1689
1690 static void ListMetadata(RestApiGetCall& call) 1685 static void ListMetadata(RestApiGetCall& call)
1691 { 1686 {
1692 if (call.IsDocumentation()) 1687 const ResourceType level = GetResourceTypeFromUri(call);
1693 { 1688
1694 ResourceType t = StringToResourceType(call.GetFullUri()[0].c_str()); 1689 if (call.IsDocumentation())
1695 std::string r = GetResourceTypeText(t, false /* plural */, false /* upper case */); 1690 {
1696 call.GetDocumentation() 1691 std::string r = GetResourceTypeText(level, false /* plural */, false /* upper case */);
1697 .SetTag(GetResourceTypeText(t, true /* plural */, true /* upper case */)) 1692 call.GetDocumentation()
1693 .SetTag(GetResourceTypeText(level, true /* plural */, true /* upper case */))
1698 .SetSummary("List metadata") 1694 .SetSummary("List metadata")
1699 .SetDescription("Get the list of metadata that are associated with the given " + r) 1695 .SetDescription("Get the list of metadata that are associated with the given " + r)
1700 .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest") 1696 .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest")
1701 .SetHttpGetArgument("expand", RestApiCallDocumentation::Type_String, 1697 .SetHttpGetArgument("expand", RestApiCallDocumentation::Type_String,
1702 "If present, also retrieve the value of the individual metadata", false) 1698 "If present, also retrieve the value of the individual metadata", false)
1703 .SetHttpGetArgument("numeric", RestApiCallDocumentation::Type_String, 1699 .SetHttpGetArgument("numeric", RestApiCallDocumentation::Type_String,
1704 "If present, use the numeric identifier of the metadata instead of its symbolic name", false) 1700 "If present, use the numeric identifier of the metadata instead of its symbolic name", false)
1705 .AddAnswerType(MimeType_Json, "JSON array containing the names of the available metadata, " 1701 .AddAnswerType(MimeType_Json, "JSON array containing the names of the available metadata, "
1706 "or JSON associative array mapping metadata to their values (if `expand` argument is provided)") 1702 "or JSON associative array mapping metadata to their values (if `expand` argument is provided)")
1707 .SetHttpGetSample(GetDocumentationSampleResource(t) + "/metadata", true); 1703 .SetHttpGetSample(GetDocumentationSampleResource(level) + "/metadata", true);
1708 return; 1704 return;
1709 } 1705 }
1710 1706
1711 assert(!call.GetFullUri().empty()); 1707 assert(!call.GetFullUri().empty());
1712 const std::string publicId = call.GetUriComponent("id", ""); 1708 const std::string publicId = call.GetUriComponent("id", "");
1713 ResourceType level = StringToResourceType(call.GetFullUri() [0].c_str());
1714 1709
1715 typedef std::map<MetadataType, std::string> Metadata; 1710 typedef std::map<MetadataType, std::string> Metadata;
1716 1711
1717 Metadata metadata; 1712 Metadata metadata;
1718 OrthancRestApi::GetIndex(call).GetAllMetadata(metadata, publicId, level); 1713 OrthancRestApi::GetIndex(call).GetAllMetadata(metadata, publicId, level);
1840 } 1835 }
1841 1836
1842 1837
1843 static void GetMetadata(RestApiGetCall& call) 1838 static void GetMetadata(RestApiGetCall& call)
1844 { 1839 {
1845 if (call.IsDocumentation()) 1840 const ResourceType level = GetResourceTypeFromUri(call);
1846 { 1841
1847 ResourceType t = StringToResourceType(call.GetFullUri()[0].c_str()); 1842 if (call.IsDocumentation())
1848 std::string r = GetResourceTypeText(t, false /* plural */, false /* upper case */); 1843 {
1849 call.GetDocumentation() 1844 std::string r = GetResourceTypeText(level, false /* plural */, false /* upper case */);
1850 .SetTag(GetResourceTypeText(t, true /* plural */, true /* upper case */)) 1845 call.GetDocumentation()
1846 .SetTag(GetResourceTypeText(level, true /* plural */, true /* upper case */))
1851 .SetSummary("Get metadata") 1847 .SetSummary("Get metadata")
1852 .SetDescription("Get the value of a metadata that is associated with the given " + r) 1848 .SetDescription("Get the value of a metadata that is associated with the given " + r)
1853 .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest") 1849 .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest")
1854 .SetUriArgument("name", "The name of the metadata, or its index (cf. `UserMetadata` configuration option)") 1850 .SetUriArgument("name", "The name of the metadata, or its index (cf. `UserMetadata` configuration option)")
1855 .AddAnswerType(MimeType_PlainText, "Value of the metadata") 1851 .AddAnswerType(MimeType_PlainText, "Value of the metadata")
1858 return; 1854 return;
1859 } 1855 }
1860 1856
1861 assert(!call.GetFullUri().empty()); 1857 assert(!call.GetFullUri().empty());
1862 const std::string publicId = call.GetUriComponent("id", ""); 1858 const std::string publicId = call.GetUriComponent("id", "");
1863 const ResourceType level = StringToResourceType(call.GetFullUri() [0].c_str());
1864 1859
1865 std::string name = call.GetUriComponent("name", ""); 1860 std::string name = call.GetUriComponent("name", "");
1866 MetadataType metadata = StringToMetadata(name); 1861 MetadataType metadata = StringToMetadata(name);
1867 1862
1868 std::string value; 1863 std::string value;
1887 } 1882 }
1888 1883
1889 1884
1890 static void DeleteMetadata(RestApiDeleteCall& call) 1885 static void DeleteMetadata(RestApiDeleteCall& call)
1891 { 1886 {
1892 if (call.IsDocumentation()) 1887 const ResourceType level = GetResourceTypeFromUri(call);
1893 { 1888
1894 ResourceType t = StringToResourceType(call.GetFullUri()[0].c_str()); 1889 if (call.IsDocumentation())
1895 std::string r = GetResourceTypeText(t, false /* plural */, false /* upper case */); 1890 {
1896 call.GetDocumentation() 1891 std::string r = GetResourceTypeText(level, false /* plural */, false /* upper case */);
1897 .SetTag(GetResourceTypeText(t, true /* plural */, true /* upper case */)) 1892 call.GetDocumentation()
1893 .SetTag(GetResourceTypeText(level, true /* plural */, true /* upper case */))
1898 .SetSummary("Delete metadata") 1894 .SetSummary("Delete metadata")
1899 .SetDescription("Delete some metadata associated with the given DICOM " + r + 1895 .SetDescription("Delete some metadata associated with the given DICOM " + r +
1900 ". This call will fail if trying to delete a system metadata (i.e. whose index is < 1024).") 1896 ". This call will fail if trying to delete a system metadata (i.e. whose index is < 1024).")
1901 .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest") 1897 .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest")
1902 .SetUriArgument("name", "The name of the metadata, or its index (cf. `UserMetadata` configuration option)") 1898 .SetUriArgument("name", "The name of the metadata, or its index (cf. `UserMetadata` configuration option)")
1903 .SetHttpHeader("If-Match", "Revision of the metadata, to check if its content has not changed and can " 1899 .SetHttpHeader("If-Match", "Revision of the metadata, to check if its content has not changed and can "
1904 "be deleted. This header is mandatory if `CheckRevisions` option is `true`."); 1900 "be deleted. This header is mandatory if `CheckRevisions` option is `true`.");
1905 return; 1901 return;
1906 } 1902 }
1907 1903
1908 CheckValidResourceType(call);
1909 const std::string publicId = call.GetUriComponent("id", ""); 1904 const std::string publicId = call.GetUriComponent("id", "");
1910 1905
1911 std::string name = call.GetUriComponent("name", ""); 1906 std::string name = call.GetUriComponent("name", "");
1912 MetadataType metadata = StringToMetadata(name); 1907 MetadataType metadata = StringToMetadata(name);
1913 1908
1951 } 1946 }
1952 1947
1953 1948
1954 static void SetMetadata(RestApiPutCall& call) 1949 static void SetMetadata(RestApiPutCall& call)
1955 { 1950 {
1956 if (call.IsDocumentation()) 1951 const ResourceType level = GetResourceTypeFromUri(call);
1957 { 1952
1958 ResourceType t = StringToResourceType(call.GetFullUri()[0].c_str()); 1953 if (call.IsDocumentation())
1959 std::string r = GetResourceTypeText(t, false /* plural */, false /* upper case */); 1954 {
1960 call.GetDocumentation() 1955 std::string r = GetResourceTypeText(level, false /* plural */, false /* upper case */);
1961 .SetTag(GetResourceTypeText(t, true /* plural */, true /* upper case */)) 1956 call.GetDocumentation()
1957 .SetTag(GetResourceTypeText(level, true /* plural */, true /* upper case */))
1962 .SetSummary("Set metadata") 1958 .SetSummary("Set metadata")
1963 .SetDescription("Set the value of some metadata in the given DICOM " + r + 1959 .SetDescription("Set the value of some metadata in the given DICOM " + r +
1964 ". This call will fail if trying to modify a system metadata (i.e. whose index is < 1024).") 1960 ". This call will fail if trying to modify a system metadata (i.e. whose index is < 1024).")
1965 .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest") 1961 .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest")
1966 .SetUriArgument("name", "The name of the metadata, or its index (cf. `UserMetadata` configuration option)") 1962 .SetUriArgument("name", "The name of the metadata, or its index (cf. `UserMetadata` configuration option)")
1967 .AddRequestType(MimeType_PlainText, "String value of the metadata") 1963 .AddRequestType(MimeType_PlainText, "String value of the metadata")
1968 .SetHttpHeader("If-Match", "Revision of the metadata, if this is not the first time this metadata is set."); 1964 .SetHttpHeader("If-Match", "Revision of the metadata, if this is not the first time this metadata is set.");
1969 return; 1965 return;
1970 } 1966 }
1971 1967
1972 CheckValidResourceType(call);
1973
1974 std::string publicId = call.GetUriComponent("id", ""); 1968 std::string publicId = call.GetUriComponent("id", "");
1975 std::string name = call.GetUriComponent("name", ""); 1969 std::string name = call.GetUriComponent("name", "");
1976 MetadataType metadata = StringToMetadata(name); 1970 MetadataType metadata = StringToMetadata(name);
1977 1971
1978 std::string value; 1972 std::string value;
2016 2010
2017 // Handling of labels ------------------------------------------------------- 2011 // Handling of labels -------------------------------------------------------
2018 2012
2019 static void ListLabels(RestApiGetCall& call) 2013 static void ListLabels(RestApiGetCall& call)
2020 { 2014 {
2021 if (call.IsDocumentation()) 2015 const ResourceType level = GetResourceTypeFromUri(call);
2022 { 2016
2023 ResourceType t = StringToResourceType(call.GetFullUri()[0].c_str()); 2017 if (call.IsDocumentation())
2024 std::string r = GetResourceTypeText(t, false /* plural */, false /* upper case */); 2018 {
2025 call.GetDocumentation() 2019 std::string r = GetResourceTypeText(level, false /* plural */, false /* upper case */);
2026 .SetTag(GetResourceTypeText(t, true /* plural */, true /* upper case */)) 2020 call.GetDocumentation()
2021 .SetTag(GetResourceTypeText(level, true /* plural */, true /* upper case */))
2027 .SetSummary("List labels") 2022 .SetSummary("List labels")
2028 .SetDescription("Get the labels that are associated with the given " + r + " (new in Orthanc 1.12.0)") 2023 .SetDescription("Get the labels that are associated with the given " + r + " (new in Orthanc 1.12.0)")
2029 .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest") 2024 .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest")
2030 .AddAnswerType(MimeType_Json, "JSON array containing the names of the labels") 2025 .AddAnswerType(MimeType_Json, "JSON array containing the names of the labels")
2031 .SetHttpGetSample(GetDocumentationSampleResource(t) + "/labels", true); 2026 .SetHttpGetSample(GetDocumentationSampleResource(level) + "/labels", true);
2032 return; 2027 return;
2033 } 2028 }
2034 2029
2035 assert(!call.GetFullUri().empty());
2036 const std::string publicId = call.GetUriComponent("id", ""); 2030 const std::string publicId = call.GetUriComponent("id", "");
2037 ResourceType level = StringToResourceType(call.GetFullUri() [0].c_str());
2038 2031
2039 std::set<std::string> labels; 2032 std::set<std::string> labels;
2040 OrthancRestApi::GetIndex(call).ListLabels(labels, publicId, level); 2033 OrthancRestApi::GetIndex(call).ListLabels(labels, publicId, level);
2041 2034
2042 Json::Value result = Json::arrayValue; 2035 Json::Value result = Json::arrayValue;
2050 } 2043 }
2051 2044
2052 2045
2053 static void GetLabel(RestApiGetCall& call) 2046 static void GetLabel(RestApiGetCall& call)
2054 { 2047 {
2055 if (call.IsDocumentation()) 2048 const ResourceType level = GetResourceTypeFromUri(call);
2056 { 2049
2057 ResourceType t = StringToResourceType(call.GetFullUri()[0].c_str()); 2050 if (call.IsDocumentation())
2058 std::string r = GetResourceTypeText(t, false /* plural */, false /* upper case */); 2051 {
2059 call.GetDocumentation() 2052 std::string r = GetResourceTypeText(level, false /* plural */, false /* upper case */);
2060 .SetTag(GetResourceTypeText(t, true /* plural */, true /* upper case */)) 2053 call.GetDocumentation()
2054 .SetTag(GetResourceTypeText(level, true /* plural */, true /* upper case */))
2061 .SetSummary("Test label") 2055 .SetSummary("Test label")
2062 .SetDescription("Test whether the " + r + " is associated with the given label") 2056 .SetDescription("Test whether the " + r + " is associated with the given label")
2063 .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest") 2057 .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest")
2064 .SetUriArgument("label", "The label of interest") 2058 .SetUriArgument("label", "The label of interest")
2065 .AddAnswerType(MimeType_PlainText, "Empty string is returned in the case of presence, error 404 in the case of absence"); 2059 .AddAnswerType(MimeType_PlainText, "Empty string is returned in the case of presence, error 404 in the case of absence");
2066 return; 2060 return;
2067 } 2061 }
2068 2062
2069 CheckValidResourceType(call);
2070
2071 assert(!call.GetFullUri().empty());
2072 const std::string publicId = call.GetUriComponent("id", ""); 2063 const std::string publicId = call.GetUriComponent("id", "");
2073 const ResourceType level = StringToResourceType(call.GetFullUri() [0].c_str());
2074 2064
2075 std::string label = call.GetUriComponent("label", ""); 2065 std::string label = call.GetUriComponent("label", "");
2076 2066
2077 std::set<std::string> labels; 2067 std::set<std::string> labels;
2078 OrthancRestApi::GetIndex(call).ListLabels(labels, publicId, level); 2068 OrthancRestApi::GetIndex(call).ListLabels(labels, publicId, level);
2084 } 2074 }
2085 2075
2086 2076
2087 static void AddLabel(RestApiPutCall& call) 2077 static void AddLabel(RestApiPutCall& call)
2088 { 2078 {
2089 if (call.IsDocumentation()) 2079 const ResourceType level = GetResourceTypeFromUri(call);
2090 { 2080
2091 ResourceType t = StringToResourceType(call.GetFullUri()[0].c_str()); 2081 if (call.IsDocumentation())
2092 std::string r = GetResourceTypeText(t, false /* plural */, false /* upper case */); 2082 {
2093 call.GetDocumentation() 2083 std::string r = GetResourceTypeText(level, false /* plural */, false /* upper case */);
2094 .SetTag(GetResourceTypeText(t, true /* plural */, true /* upper case */)) 2084 call.GetDocumentation()
2085 .SetTag(GetResourceTypeText(level, true /* plural */, true /* upper case */))
2095 .SetSummary("Add label") 2086 .SetSummary("Add label")
2096 .SetDescription("Associate a label with a " + r) 2087 .SetDescription("Associate a label with a " + r)
2097 .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest") 2088 .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest")
2098 .SetUriArgument("label", "The label to be added"); 2089 .SetUriArgument("label", "The label to be added");
2099 return; 2090 return;
2100 } 2091 }
2101 2092
2102 CheckValidResourceType(call);
2103
2104 std::string publicId = call.GetUriComponent("id", ""); 2093 std::string publicId = call.GetUriComponent("id", "");
2105 const ResourceType level = StringToResourceType(call.GetFullUri() [0].c_str());
2106 2094
2107 std::string label = call.GetUriComponent("label", ""); 2095 std::string label = call.GetUriComponent("label", "");
2108 OrthancRestApi::GetIndex(call).ModifyLabel(publicId, level, label, StatelessDatabaseOperations::LabelOperation_Add); 2096 OrthancRestApi::GetIndex(call).ModifyLabel(publicId, level, label, StatelessDatabaseOperations::LabelOperation_Add);
2109 2097
2110 call.GetOutput().AnswerBuffer("", MimeType_PlainText); 2098 call.GetOutput().AnswerBuffer("", MimeType_PlainText);
2111 } 2099 }
2112 2100
2113 2101
2114 static void RemoveLabel(RestApiDeleteCall& call) 2102 static void RemoveLabel(RestApiDeleteCall& call)
2115 { 2103 {
2116 if (call.IsDocumentation()) 2104 const ResourceType level = GetResourceTypeFromUri(call);
2117 { 2105
2118 ResourceType t = StringToResourceType(call.GetFullUri()[0].c_str()); 2106 if (call.IsDocumentation())
2119 std::string r = GetResourceTypeText(t, false /* plural */, false /* upper case */); 2107 {
2120 call.GetDocumentation() 2108 std::string r = GetResourceTypeText(level, false /* plural */, false /* upper case */);
2121 .SetTag(GetResourceTypeText(t, true /* plural */, true /* upper case */)) 2109 call.GetDocumentation()
2110 .SetTag(GetResourceTypeText(level, true /* plural */, true /* upper case */))
2122 .SetSummary("Remove label") 2111 .SetSummary("Remove label")
2123 .SetDescription("Remove a label associated with a " + r) 2112 .SetDescription("Remove a label associated with a " + r)
2124 .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest") 2113 .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest")
2125 .SetUriArgument("label", "The label to be removed"); 2114 .SetUriArgument("label", "The label to be removed");
2126 return; 2115 return;
2127 } 2116 }
2128 2117
2129 CheckValidResourceType(call);
2130
2131 std::string publicId = call.GetUriComponent("id", ""); 2118 std::string publicId = call.GetUriComponent("id", "");
2132 const ResourceType level = StringToResourceType(call.GetFullUri() [0].c_str());
2133 2119
2134 std::string label = call.GetUriComponent("label", ""); 2120 std::string label = call.GetUriComponent("label", "");
2135 OrthancRestApi::GetIndex(call).ModifyLabel(publicId, level, label, StatelessDatabaseOperations::LabelOperation_Remove); 2121 OrthancRestApi::GetIndex(call).ModifyLabel(publicId, level, label, StatelessDatabaseOperations::LabelOperation_Remove);
2136 2122
2137 call.GetOutput().AnswerBuffer("", MimeType_PlainText); 2123 call.GetOutput().AnswerBuffer("", MimeType_PlainText);
2140 2126
2141 // Handling of attached files ----------------------------------------------- 2127 // Handling of attached files -----------------------------------------------
2142 2128
2143 static void ListAttachments(RestApiGetCall& call) 2129 static void ListAttachments(RestApiGetCall& call)
2144 { 2130 {
2145 if (call.IsDocumentation()) 2131 const ResourceType level = GetResourceTypeFromUri(call);
2146 { 2132
2147 ResourceType t = StringToResourceType(call.GetFullUri()[0].c_str()); 2133 if (call.IsDocumentation())
2148 std::string r = GetResourceTypeText(t, false /* plural */, false /* upper case */); 2134 {
2149 call.GetDocumentation() 2135 std::string r = GetResourceTypeText(level, false /* plural */, false /* upper case */);
2150 .SetTag(GetResourceTypeText(t, true /* plural */, true /* upper case */)) 2136 call.GetDocumentation()
2137 .SetTag(GetResourceTypeText(level, true /* plural */, true /* upper case */))
2151 .SetSummary("List attachments") 2138 .SetSummary("List attachments")
2152 .SetDescription("Get the list of attachments that are associated with the given " + r) 2139 .SetDescription("Get the list of attachments that are associated with the given " + r)
2153 .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest") 2140 .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest")
2154 .SetHttpGetArgument("full", RestApiCallDocumentation::Type_String, 2141 .SetHttpGetArgument("full", RestApiCallDocumentation::Type_String,
2155 "If present, retrieve the attachments list and their numerical ids", false) 2142 "If present, retrieve the attachments list and their numerical ids", false)
2156 .AddAnswerType(MimeType_Json, "JSON array containing the names of the attachments") 2143 .AddAnswerType(MimeType_Json, "JSON array containing the names of the attachments")
2157 .SetHttpGetSample(GetDocumentationSampleResource(t) + "/attachments", true); 2144 .SetHttpGetSample(GetDocumentationSampleResource(level) + "/attachments", true);
2158 return; 2145 return;
2159 } 2146 }
2160 2147
2161 const std::string resourceType = call.GetFullUri() [0];
2162 const std::string publicId = call.GetUriComponent("id", ""); 2148 const std::string publicId = call.GetUriComponent("id", "");
2163 std::set<FileContentType> attachments; 2149 std::set<FileContentType> attachments;
2164 OrthancRestApi::GetIndex(call).ListAvailableAttachments(attachments, publicId, StringToResourceType(resourceType.c_str())); 2150 OrthancRestApi::GetIndex(call).ListAvailableAttachments(attachments, publicId, level);
2165 2151
2166 Json::Value result; 2152 Json::Value result;
2167 2153
2168 if (call.HasArgument("full")) 2154 if (call.HasArgument("full"))
2169 { 2155 {
2170 result = Json::objectValue; 2156 result = Json::objectValue;
2171 2157
2172 for (std::set<FileContentType>::const_iterator 2158 for (std::set<FileContentType>::const_iterator
2173 it = attachments.begin(); it != attachments.end(); ++it) 2159 it = attachments.begin(); it != attachments.end(); ++it)
2174 { 2160 {
2175 std::string key = EnumerationToString(*it); 2161 std::string key = EnumerationToString(*it);
2176 result[key] = static_cast<uint16_t>(*it); 2162 result[key] = static_cast<uint16_t>(*it);
2177 } 2163 }
2178 } 2164 }
2179 else 2165 else
2180 { 2166 {
2181 result = Json::arrayValue; 2167 result = Json::arrayValue;
2182 2168
2183 for (std::set<FileContentType>::const_iterator 2169 for (std::set<FileContentType>::const_iterator
2184 it = attachments.begin(); it != attachments.end(); ++it) 2170 it = attachments.begin(); it != attachments.end(); ++it)
2185 { 2171 {
2186 result.append(EnumerationToString(*it)); 2172 result.append(EnumerationToString(*it));
2187 } 2173 }
2188 } 2174 }
2189 2175
2204 2190
2205 static bool GetAttachmentInfo(FileInfo& info, 2191 static bool GetAttachmentInfo(FileInfo& info,
2206 ResourceType level, 2192 ResourceType level,
2207 RestApiGetCall& call) 2193 RestApiGetCall& call)
2208 { 2194 {
2209 CheckValidResourceType(call);
2210
2211 const std::string publicId = call.GetUriComponent("id", ""); 2195 const std::string publicId = call.GetUriComponent("id", "");
2212 const std::string name = call.GetUriComponent("name", ""); 2196 const std::string name = call.GetUriComponent("name", "");
2213 FileContentType contentType = StringToContentType(name); 2197 FileContentType contentType = StringToContentType(name);
2214 2198
2215 int64_t revision; 2199 int64_t revision;
2238 } 2222 }
2239 2223
2240 2224
2241 static void GetAttachmentOperations(RestApiGetCall& call) 2225 static void GetAttachmentOperations(RestApiGetCall& call)
2242 { 2226 {
2243 const ResourceType t = GetResourceTypeFromUri(call); 2227 const ResourceType level = GetResourceTypeFromUri(call);
2244 2228
2245 if (call.IsDocumentation()) 2229 if (call.IsDocumentation())
2246 { 2230 {
2247 ResourceType t = StringToResourceType(call.GetFullUri()[0].c_str()); 2231 std::string r = GetResourceTypeText(level, false /* plural */, false /* upper case */);
2248 std::string r = GetResourceTypeText(t, false /* plural */, false /* upper case */);
2249 AddAttachmentDocumentation(call, r); 2232 AddAttachmentDocumentation(call, r);
2250 call.GetDocumentation() 2233 call.GetDocumentation()
2251 .SetTag("Other") 2234 .SetTag("Other")
2252 .SetSummary("List operations on attachments") 2235 .SetSummary("List operations on attachments")
2253 .SetDescription("Get the list of the operations that are available for attachments associated with the given " + r) 2236 .SetDescription("Get the list of the operations that are available for attachments associated with the given " + r)
2255 .SetHttpGetSample("https://orthanc.uclouvain.be/demo/instances/6582b1c0-292ad5ab-ba0f088f-f7a1766f-9a29a54f/attachments/dicom", true); 2238 .SetHttpGetSample("https://orthanc.uclouvain.be/demo/instances/6582b1c0-292ad5ab-ba0f088f-f7a1766f-9a29a54f/attachments/dicom", true);
2256 return; 2239 return;
2257 } 2240 }
2258 2241
2259 FileInfo info; 2242 FileInfo info;
2260 if (GetAttachmentInfo(info, t, call)) 2243 if (GetAttachmentInfo(info, level, call))
2261 { 2244 {
2262 Json::Value operations = Json::arrayValue; 2245 Json::Value operations = Json::arrayValue;
2263 2246
2264 operations.append("compress"); 2247 operations.append("compress");
2265 operations.append("compressed-data"); 2248 operations.append("compressed-data");
2294 2277
2295 2278
2296 template <int uncompress> 2279 template <int uncompress>
2297 static void GetAttachmentData(RestApiGetCall& call) 2280 static void GetAttachmentData(RestApiGetCall& call)
2298 { 2281 {
2299 const ResourceType t = GetResourceTypeFromUri(call); 2282 const ResourceType level = GetResourceTypeFromUri(call);
2300 2283
2301 if (call.IsDocumentation()) 2284 if (call.IsDocumentation())
2302 { 2285 {
2303 std::string r = GetResourceTypeText(t, false /* plural */, false /* upper case */); 2286 std::string r = GetResourceTypeText(level, false /* plural */, false /* upper case */);
2304 call.GetDocumentation() 2287 call.GetDocumentation()
2305 .SetTag(GetResourceTypeText(t, true /* plural */, true /* upper case */)) 2288 .SetTag(GetResourceTypeText(level, true /* plural */, true /* upper case */))
2306 .SetSummary("Get attachment" + std::string(uncompress ? "" : " (no decompression)")) 2289 .SetSummary("Get attachment" + std::string(uncompress ? "" : " (no decompression)"))
2307 .SetDescription("Get the (binary) content of one attachment associated with the given " + r + 2290 .SetDescription("Get the (binary) content of one attachment associated with the given " + r +
2308 std::string(uncompress ? "" : ". The attachment will not be decompressed if `StorageCompression` is `true`.")) 2291 std::string(uncompress ? "" : ". The attachment will not be decompressed if `StorageCompression` is `true`."))
2309 .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest") 2292 .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest")
2310 .SetUriArgument("name", "The name of the attachment, or its index (cf. `UserContentType` configuration option)") 2293 .SetUriArgument("name", "The name of the attachment, or its index (cf. `UserContentType` configuration option)")
2314 return; 2297 return;
2315 } 2298 }
2316 2299
2317 ServerContext& context = OrthancRestApi::GetContext(call); 2300 ServerContext& context = OrthancRestApi::GetContext(call);
2318 2301
2319 CheckValidResourceType(call);
2320
2321 std::string publicId = call.GetUriComponent("id", ""); 2302 std::string publicId = call.GetUriComponent("id", "");
2322 FileContentType type = StringToContentType(call.GetUriComponent("name", "")); 2303 FileContentType type = StringToContentType(call.GetUriComponent("name", ""));
2323 2304
2324 FileInfo info; 2305 FileInfo info;
2325 if (GetAttachmentInfo(info, t, call)) 2306 if (GetAttachmentInfo(info, level, call))
2326 { 2307 {
2327 // NB: "SetAttachmentETag()" is already invoked by "GetAttachmentInfo()" 2308 // NB: "SetAttachmentETag()" is already invoked by "GetAttachmentInfo()"
2328 2309
2329 if (uncompress) 2310 if (uncompress)
2330 { 2311 {
2331 context.AnswerAttachment(call.GetOutput(), t, publicId, type); 2312 context.AnswerAttachment(call.GetOutput(), level, publicId, type);
2332 } 2313 }
2333 else 2314 else
2334 { 2315 {
2335 // Return the raw data (possibly compressed), as stored on the filesystem 2316 // Return the raw data (possibly compressed), as stored on the filesystem
2336 std::string content; 2317 std::string content;
2337 std::string attachmentId; 2318 std::string attachmentId;
2338 int64_t revision; 2319 int64_t revision;
2339 context.ReadAttachment(content, revision, attachmentId, t, publicId, type, false, true /* skipCache when you absolutely need the compressed data */); 2320 context.ReadAttachment(content, revision, attachmentId, level, publicId, type, false, true /* skipCache when you absolutely need the compressed data */);
2340 2321
2341 int64_t userRevision; 2322 int64_t userRevision;
2342 std::string userMD5; 2323 std::string userMD5;
2343 if (GetRevisionHeader(userRevision, userMD5, call, "If-None-Match") && 2324 if (GetRevisionHeader(userRevision, userMD5, call, "If-None-Match") &&
2344 revision == userRevision && 2325 revision == userRevision &&
2355 } 2336 }
2356 2337
2357 2338
2358 static void GetAttachmentSize(RestApiGetCall& call) 2339 static void GetAttachmentSize(RestApiGetCall& call)
2359 { 2340 {
2360 const ResourceType t = GetResourceTypeFromUri(call); 2341 const ResourceType level = GetResourceTypeFromUri(call);
2361 2342
2362 if (call.IsDocumentation()) 2343 if (call.IsDocumentation())
2363 { 2344 {
2364 std::string r = GetResourceTypeText(t, false /* plural */, false /* upper case */); 2345 std::string r = GetResourceTypeText(level, false /* plural */, false /* upper case */);
2365 AddAttachmentDocumentation(call, r); 2346 AddAttachmentDocumentation(call, r);
2366 call.GetDocumentation() 2347 call.GetDocumentation()
2367 .SetTag(GetResourceTypeText(t, true /* plural */, true /* upper case */)) 2348 .SetTag(GetResourceTypeText(level, true /* plural */, true /* upper case */))
2368 .SetSummary("Get size of attachment") 2349 .SetSummary("Get size of attachment")
2369 .SetDescription("Get the size of one attachment associated with the given " + r) 2350 .SetDescription("Get the size of one attachment associated with the given " + r)
2370 .AddAnswerType(MimeType_PlainText, "The size of the attachment"); 2351 .AddAnswerType(MimeType_PlainText, "The size of the attachment");
2371 return; 2352 return;
2372 } 2353 }
2373 2354
2374 FileInfo info; 2355 FileInfo info;
2375 if (GetAttachmentInfo(info, t, call)) 2356 if (GetAttachmentInfo(info, level, call))
2376 { 2357 {
2377 call.GetOutput().AnswerBuffer(boost::lexical_cast<std::string>(info.GetUncompressedSize()), MimeType_PlainText); 2358 call.GetOutput().AnswerBuffer(boost::lexical_cast<std::string>(info.GetUncompressedSize()), MimeType_PlainText);
2378 } 2359 }
2379 } 2360 }
2380 2361
2381 static void GetAttachmentInfo(RestApiGetCall& call) 2362 static void GetAttachmentInfo(RestApiGetCall& call)
2382 { 2363 {
2383 const ResourceType t = GetResourceTypeFromUri(call); 2364 const ResourceType level = GetResourceTypeFromUri(call);
2384 2365
2385 if (call.IsDocumentation()) 2366 if (call.IsDocumentation())
2386 { 2367 {
2387 std::string r = GetResourceTypeText(t, false /* plural */, false /* upper case */); 2368 std::string r = GetResourceTypeText(level, false /* plural */, false /* upper case */);
2388 AddAttachmentDocumentation(call, r); 2369 AddAttachmentDocumentation(call, r);
2389 call.GetDocumentation() 2370 call.GetDocumentation()
2390 .SetTag(GetResourceTypeText(t, true /* plural */, true /* upper case */)) 2371 .SetTag(GetResourceTypeText(level, true /* plural */, true /* upper case */))
2391 .SetSummary("Get info about the attachment") 2372 .SetSummary("Get info about the attachment")
2392 .SetDescription("Get all the information about the attachment associated with the given " + r) 2373 .SetDescription("Get all the information about the attachment associated with the given " + r)
2393 .AddAnswerType(MimeType_Json, "JSON object containing the information about the attachment") 2374 .AddAnswerType(MimeType_Json, "JSON object containing the information about the attachment")
2394 .SetHttpGetSample("https://orthanc.uclouvain.be/demo/instances/7c92ce8e-bbf67ed2-ffa3b8c1-a3b35d94-7ff3ae26/attachments/dicom/info", true); 2375 .SetHttpGetSample("https://orthanc.uclouvain.be/demo/instances/7c92ce8e-bbf67ed2-ffa3b8c1-a3b35d94-7ff3ae26/attachments/dicom/info", true);
2395 return; 2376 return;
2396 } 2377 }
2397 2378
2398 FileInfo info; 2379 FileInfo info;
2399 if (GetAttachmentInfo(info, t, call)) 2380 if (GetAttachmentInfo(info, level, call))
2400 { 2381 {
2401 Json::Value result = Json::objectValue; 2382 Json::Value result = Json::objectValue;
2402 result["Uuid"] = info.GetUuid(); 2383 result["Uuid"] = info.GetUuid();
2403 result["ContentType"] = info.GetContentType(); 2384 result["ContentType"] = info.GetContentType();
2404 result["UncompressedSize"] = Json::Value::UInt64(info.GetUncompressedSize()); 2385 result["UncompressedSize"] = Json::Value::UInt64(info.GetUncompressedSize());
2410 } 2391 }
2411 } 2392 }
2412 2393
2413 static void GetAttachmentCompressedSize(RestApiGetCall& call) 2394 static void GetAttachmentCompressedSize(RestApiGetCall& call)
2414 { 2395 {
2415 const ResourceType t = GetResourceTypeFromUri(call); 2396 const ResourceType level = GetResourceTypeFromUri(call);
2416 2397
2417 if (call.IsDocumentation()) 2398 if (call.IsDocumentation())
2418 { 2399 {
2419 std::string r = GetResourceTypeText(t, false /* plural */, false /* upper case */); 2400 std::string r = GetResourceTypeText(level, false /* plural */, false /* upper case */);
2420 AddAttachmentDocumentation(call, r); 2401 AddAttachmentDocumentation(call, r);
2421 call.GetDocumentation() 2402 call.GetDocumentation()
2422 .SetTag(GetResourceTypeText(t, true /* plural */, true /* upper case */)) 2403 .SetTag(GetResourceTypeText(level, true /* plural */, true /* upper case */))
2423 .SetSummary("Get size of attachment on disk") 2404 .SetSummary("Get size of attachment on disk")
2424 .SetDescription("Get the size of one attachment associated with the given " + r + ", as stored on the disk. " 2405 .SetDescription("Get the size of one attachment associated with the given " + r + ", as stored on the disk. "
2425 "This is different from `.../size` iff `EnableStorage` is `true`.") 2406 "This is different from `.../size` iff `EnableStorage` is `true`.")
2426 .AddAnswerType(MimeType_PlainText, "The size of the attachment, as stored on the disk"); 2407 .AddAnswerType(MimeType_PlainText, "The size of the attachment, as stored on the disk");
2427 return; 2408 return;
2428 } 2409 }
2429 2410
2430 FileInfo info; 2411 FileInfo info;
2431 if (GetAttachmentInfo(info, t, call)) 2412 if (GetAttachmentInfo(info, level, call))
2432 { 2413 {
2433 call.GetOutput().AnswerBuffer(boost::lexical_cast<std::string>(info.GetCompressedSize()), MimeType_PlainText); 2414 call.GetOutput().AnswerBuffer(boost::lexical_cast<std::string>(info.GetCompressedSize()), MimeType_PlainText);
2434 } 2415 }
2435 } 2416 }
2436 2417
2437 2418
2438 static void GetAttachmentMD5(RestApiGetCall& call) 2419 static void GetAttachmentMD5(RestApiGetCall& call)
2439 { 2420 {
2440 const ResourceType t = GetResourceTypeFromUri(call); 2421 const ResourceType level = GetResourceTypeFromUri(call);
2441 2422
2442 if (call.IsDocumentation()) 2423 if (call.IsDocumentation())
2443 { 2424 {
2444 std::string r = GetResourceTypeText(t, false /* plural */, false /* upper case */); 2425 std::string r = GetResourceTypeText(level, false /* plural */, false /* upper case */);
2445 AddAttachmentDocumentation(call, r); 2426 AddAttachmentDocumentation(call, r);
2446 call.GetDocumentation() 2427 call.GetDocumentation()
2447 .SetTag(GetResourceTypeText(t, true /* plural */, true /* upper case */)) 2428 .SetTag(GetResourceTypeText(level, true /* plural */, true /* upper case */))
2448 .SetSummary("Get MD5 of attachment") 2429 .SetSummary("Get MD5 of attachment")
2449 .SetDescription("Get the MD5 hash of one attachment associated with the given " + r) 2430 .SetDescription("Get the MD5 hash of one attachment associated with the given " + r)
2450 .AddAnswerType(MimeType_PlainText, "The MD5 of the attachment"); 2431 .AddAnswerType(MimeType_PlainText, "The MD5 of the attachment");
2451 return; 2432 return;
2452 } 2433 }
2453 2434
2454 FileInfo info; 2435 FileInfo info;
2455 if (GetAttachmentInfo(info, t, call) && 2436 if (GetAttachmentInfo(info, level, call) &&
2456 info.GetUncompressedMD5() != "") 2437 info.GetUncompressedMD5() != "")
2457 { 2438 {
2458 call.GetOutput().AnswerBuffer(boost::lexical_cast<std::string>(info.GetUncompressedMD5()), MimeType_PlainText); 2439 call.GetOutput().AnswerBuffer(boost::lexical_cast<std::string>(info.GetUncompressedMD5()), MimeType_PlainText);
2459 } 2440 }
2460 } 2441 }
2461 2442
2462 2443
2463 static void GetAttachmentCompressedMD5(RestApiGetCall& call) 2444 static void GetAttachmentCompressedMD5(RestApiGetCall& call)
2464 { 2445 {
2465 const ResourceType t = GetResourceTypeFromUri(call); 2446 const ResourceType level = GetResourceTypeFromUri(call);
2466 2447
2467 if (call.IsDocumentation()) 2448 if (call.IsDocumentation())
2468 { 2449 {
2469 std::string r = GetResourceTypeText(t, false /* plural */, false /* upper case */); 2450 std::string r = GetResourceTypeText(level, false /* plural */, false /* upper case */);
2470 AddAttachmentDocumentation(call, r); 2451 AddAttachmentDocumentation(call, r);
2471 call.GetDocumentation() 2452 call.GetDocumentation()
2472 .SetTag(GetResourceTypeText(t, true /* plural */, true /* upper case */)) 2453 .SetTag(GetResourceTypeText(level, true /* plural */, true /* upper case */))
2473 .SetSummary("Get MD5 of attachment on disk") 2454 .SetSummary("Get MD5 of attachment on disk")
2474 .SetDescription("Get the MD5 hash of one attachment associated with the given " + r + ", as stored on the disk. " 2455 .SetDescription("Get the MD5 hash of one attachment associated with the given " + r + ", as stored on the disk. "
2475 "This is different from `.../md5` iff `EnableStorage` is `true`.") 2456 "This is different from `.../md5` iff `EnableStorage` is `true`.")
2476 .AddAnswerType(MimeType_PlainText, "The MD5 of the attachment, as stored on the disk"); 2457 .AddAnswerType(MimeType_PlainText, "The MD5 of the attachment, as stored on the disk");
2477 return; 2458 return;
2478 } 2459 }
2479 2460
2480 FileInfo info; 2461 FileInfo info;
2481 if (GetAttachmentInfo(info, t, call) && 2462 if (GetAttachmentInfo(info, level, call) &&
2482 info.GetCompressedMD5() != "") 2463 info.GetCompressedMD5() != "")
2483 { 2464 {
2484 call.GetOutput().AnswerBuffer(boost::lexical_cast<std::string>(info.GetCompressedMD5()), MimeType_PlainText); 2465 call.GetOutput().AnswerBuffer(boost::lexical_cast<std::string>(info.GetCompressedMD5()), MimeType_PlainText);
2485 } 2466 }
2486 } 2467 }
2487 2468
2488 2469
2489 static void VerifyAttachment(RestApiPostCall& call) 2470 static void VerifyAttachment(RestApiPostCall& call)
2490 { 2471 {
2491 const ResourceType t = GetResourceTypeFromUri(call); 2472 const ResourceType level = GetResourceTypeFromUri(call);
2492 2473
2493 if (call.IsDocumentation()) 2474 if (call.IsDocumentation())
2494 { 2475 {
2495 std::string r = GetResourceTypeText(t, false /* plural */, false /* upper case */); 2476 std::string r = GetResourceTypeText(level, false /* plural */, false /* upper case */);
2496 call.GetDocumentation() 2477 call.GetDocumentation()
2497 .SetTag(GetResourceTypeText(t, true /* plural */, true /* upper case */)) 2478 .SetTag(GetResourceTypeText(level, true /* plural */, true /* upper case */))
2498 .SetSummary("Verify attachment") 2479 .SetSummary("Verify attachment")
2499 .SetDescription("Verify that the attachment is not corrupted, by validating its MD5 hash") 2480 .SetDescription("Verify that the attachment is not corrupted, by validating its MD5 hash")
2500 .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest") 2481 .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest")
2501 .SetUriArgument("name", "The name of the attachment, or its index (cf. `UserContentType` configuration option)") 2482 .SetUriArgument("name", "The name of the attachment, or its index (cf. `UserContentType` configuration option)")
2502 .AddAnswerType(MimeType_Json, "On success, a valid JSON object is returned"); 2483 .AddAnswerType(MimeType_Json, "On success, a valid JSON object is returned");
2503 return; 2484 return;
2504 } 2485 }
2505 2486
2506 ServerContext& context = OrthancRestApi::GetContext(call); 2487 ServerContext& context = OrthancRestApi::GetContext(call);
2507 CheckValidResourceType(call);
2508 2488
2509 std::string publicId = call.GetUriComponent("id", ""); 2489 std::string publicId = call.GetUriComponent("id", "");
2510 std::string name = call.GetUriComponent("name", ""); 2490 std::string name = call.GetUriComponent("name", "");
2511 FileContentType contentType = StringToContentType(name); 2491 FileContentType contentType = StringToContentType(name);
2512 2492
2513 FileInfo info; 2493 FileInfo info;
2514 int64_t revision; // Ignored 2494 int64_t revision; // Ignored
2515 if (!OrthancRestApi::GetIndex(call).LookupAttachment(info, revision, t, publicId, contentType) || 2495 if (!OrthancRestApi::GetIndex(call).LookupAttachment(info, revision, level, publicId, contentType) ||
2516 info.GetCompressedMD5() == "" || 2496 info.GetCompressedMD5() == "" ||
2517 info.GetUncompressedMD5() == "") 2497 info.GetUncompressedMD5() == "")
2518 { 2498 {
2519 // Inexistent resource, or no MD5 available 2499 // Inexistent resource, or no MD5 available
2520 return; 2500 return;
2524 2504
2525 // First check whether the compressed data is correctly stored in the disk 2505 // First check whether the compressed data is correctly stored in the disk
2526 std::string data; 2506 std::string data;
2527 std::string attachmentId; 2507 std::string attachmentId;
2528 2508
2529 context.ReadAttachment(data, revision, attachmentId, t, publicId, StringToContentType(name), false, true /* skipCache when you absolutely need the compressed data */); 2509 context.ReadAttachment(data, revision, attachmentId, level, publicId, StringToContentType(name), false, true /* skipCache when you absolutely need the compressed data */);
2530 2510
2531 std::string actualMD5; 2511 std::string actualMD5;
2532 Toolbox::ComputeMD5(actualMD5, data); 2512 Toolbox::ComputeMD5(actualMD5, data);
2533 2513
2534 if (actualMD5 == info.GetCompressedMD5()) 2514 if (actualMD5 == info.GetCompressedMD5())
2539 { 2519 {
2540 ok = true; 2520 ok = true;
2541 } 2521 }
2542 else 2522 else
2543 { 2523 {
2544 context.ReadAttachment(data, revision, attachmentId, t, publicId, StringToContentType(name), true, true /* skipCache when you absolutely need the compressed data */); 2524 context.ReadAttachment(data, revision, attachmentId, level, publicId, StringToContentType(name), true, true /* skipCache when you absolutely need the compressed data */);
2545 Toolbox::ComputeMD5(actualMD5, data); 2525 Toolbox::ComputeMD5(actualMD5, data);
2546 ok = (actualMD5 == info.GetUncompressedMD5()); 2526 ok = (actualMD5 == info.GetUncompressedMD5());
2547 } 2527 }
2548 } 2528 }
2549 2529
2559 } 2539 }
2560 2540
2561 2541
2562 static void UploadAttachment(RestApiPutCall& call) 2542 static void UploadAttachment(RestApiPutCall& call)
2563 { 2543 {
2564 if (call.IsDocumentation()) 2544 const ResourceType level = GetResourceTypeFromUri(call);
2565 { 2545
2566 ResourceType t = StringToResourceType(call.GetFullUri()[0].c_str()); 2546 if (call.IsDocumentation())
2567 std::string r = GetResourceTypeText(t, false /* plural */, false /* upper case */); 2547 {
2568 call.GetDocumentation() 2548 std::string r = GetResourceTypeText(level, false /* plural */, false /* upper case */);
2569 .SetTag(GetResourceTypeText(t, true /* plural */, true /* upper case */)) 2549 call.GetDocumentation()
2550 .SetTag(GetResourceTypeText(level, true /* plural */, true /* upper case */))
2570 .SetSummary("Set attachment") 2551 .SetSummary("Set attachment")
2571 .SetDescription("Attach a file to the given DICOM " + r + 2552 .SetDescription("Attach a file to the given DICOM " + r +
2572 ". This call will fail if trying to modify a system attachment (i.e. whose index is < 1024).") 2553 ". This call will fail if trying to modify a system attachment (i.e. whose index is < 1024).")
2573 .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest") 2554 .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest")
2574 .SetUriArgument("name", "The name of the attachment, or its index (cf. `UserContentType` configuration option)") 2555 .SetUriArgument("name", "The name of the attachment, or its index (cf. `UserContentType` configuration option)")
2577 .SetHttpHeader("If-Match", "Revision of the attachment, if this is not the first time this attachment is set."); 2558 .SetHttpHeader("If-Match", "Revision of the attachment, if this is not the first time this attachment is set.");
2578 return; 2559 return;
2579 } 2560 }
2580 2561
2581 ServerContext& context = OrthancRestApi::GetContext(call); 2562 ServerContext& context = OrthancRestApi::GetContext(call);
2582 CheckValidResourceType(call);
2583 2563
2584 std::string publicId = call.GetUriComponent("id", ""); 2564 std::string publicId = call.GetUriComponent("id", "");
2585 std::string name = call.GetUriComponent("name", ""); 2565 std::string name = call.GetUriComponent("name", "");
2586 2566
2587 FileContentType contentType = StringToContentType(name); 2567 FileContentType contentType = StringToContentType(name);
2620 } 2600 }
2621 2601
2622 2602
2623 static void DeleteAttachment(RestApiDeleteCall& call) 2603 static void DeleteAttachment(RestApiDeleteCall& call)
2624 { 2604 {
2625 if (call.IsDocumentation()) 2605 const ResourceType level = GetResourceTypeFromUri(call);
2626 { 2606
2627 ResourceType t = StringToResourceType(call.GetFullUri()[0].c_str()); 2607 if (call.IsDocumentation())
2628 std::string r = GetResourceTypeText(t, false /* plural */, false /* upper case */); 2608 {
2629 call.GetDocumentation() 2609 std::string r = GetResourceTypeText(level, false /* plural */, false /* upper case */);
2630 .SetTag(GetResourceTypeText(t, true /* plural */, true /* upper case */)) 2610 call.GetDocumentation()
2611 .SetTag(GetResourceTypeText(level, true /* plural */, true /* upper case */))
2631 .SetSummary("Delete attachment") 2612 .SetSummary("Delete attachment")
2632 .SetDescription("Delete an attachment associated with the given DICOM " + r + 2613 .SetDescription("Delete an attachment associated with the given DICOM " + r +
2633 ". This call will fail if trying to delete a system attachment (i.e. whose index is < 1024).") 2614 ". This call will fail if trying to delete a system attachment (i.e. whose index is < 1024).")
2634 .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest") 2615 .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest")
2635 .SetUriArgument("name", "The name of the attachment, or its index (cf. `UserContentType` configuration option)") 2616 .SetUriArgument("name", "The name of the attachment, or its index (cf. `UserContentType` configuration option)")
2636 .SetHttpHeader("If-Match", "Revision of the attachment, to check if its content has not changed and can " 2617 .SetHttpHeader("If-Match", "Revision of the attachment, to check if its content has not changed and can "
2637 "be deleted. This header is mandatory if `CheckRevisions` option is `true`."); 2618 "be deleted. This header is mandatory if `CheckRevisions` option is `true`.");
2638 return; 2619 return;
2639 } 2620 }
2640 2621
2641 CheckValidResourceType(call);
2642
2643 std::string publicId = call.GetUriComponent("id", ""); 2622 std::string publicId = call.GetUriComponent("id", "");
2644 std::string name = call.GetUriComponent("name", ""); 2623 std::string name = call.GetUriComponent("name", "");
2645 FileContentType contentType = StringToContentType(name); 2624 FileContentType contentType = StringToContentType(name);
2646 2625
2647 bool allowed; 2626 bool allowed;
2709 2688
2710 2689
2711 template <enum CompressionType compression> 2690 template <enum CompressionType compression>
2712 static void ChangeAttachmentCompression(RestApiPostCall& call) 2691 static void ChangeAttachmentCompression(RestApiPostCall& call)
2713 { 2692 {
2714 const ResourceType t = GetResourceTypeFromUri(call); 2693 const ResourceType level = GetResourceTypeFromUri(call);
2715 2694
2716 if (call.IsDocumentation()) 2695 if (call.IsDocumentation())
2717 { 2696 {
2718 std::string r = GetResourceTypeText(t, false /* plural */, false /* upper case */); 2697 std::string r = GetResourceTypeText(level, false /* plural */, false /* upper case */);
2719 call.GetDocumentation() 2698 call.GetDocumentation()
2720 .SetTag(GetResourceTypeText(t, true /* plural */, true /* upper case */)) 2699 .SetTag(GetResourceTypeText(level, true /* plural */, true /* upper case */))
2721 .SetSummary(compression == CompressionType_None ? "Uncompress attachment" : "Compress attachment") 2700 .SetSummary(compression == CompressionType_None ? "Uncompress attachment" : "Compress attachment")
2722 .SetDescription("Change the compression scheme that is used to store an attachment.") 2701 .SetDescription("Change the compression scheme that is used to store an attachment.")
2723 .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest") 2702 .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest")
2724 .SetUriArgument("name", "The name of the attachment, or its index (cf. `UserContentType` configuration option)"); 2703 .SetUriArgument("name", "The name of the attachment, or its index (cf. `UserContentType` configuration option)");
2725 return; 2704 return;
2726 } 2705 }
2727 2706
2728 CheckValidResourceType(call);
2729
2730 std::string publicId = call.GetUriComponent("id", ""); 2707 std::string publicId = call.GetUriComponent("id", "");
2731 std::string name = call.GetUriComponent("name", ""); 2708 std::string name = call.GetUriComponent("name", "");
2732 FileContentType contentType = StringToContentType(name); 2709 FileContentType contentType = StringToContentType(name);
2733 2710
2734 OrthancRestApi::GetContext(call).ChangeAttachmentCompression(t, publicId, contentType, compression); 2711 OrthancRestApi::GetContext(call).ChangeAttachmentCompression(level, publicId, contentType, compression);
2735 call.GetOutput().AnswerBuffer("{}", MimeType_Json); 2712 call.GetOutput().AnswerBuffer("{}", MimeType_Json);
2736 } 2713 }
2737 2714
2738 2715
2739 static void IsAttachmentCompressed(RestApiGetCall& call) 2716 static void IsAttachmentCompressed(RestApiGetCall& call)
2740 { 2717 {
2741 const ResourceType t = GetResourceTypeFromUri(call); 2718 const ResourceType level = GetResourceTypeFromUri(call);
2742 2719
2743 if (call.IsDocumentation()) 2720 if (call.IsDocumentation())
2744 { 2721 {
2745 std::string r = GetResourceTypeText(t, false /* plural */, false /* upper case */); 2722 std::string r = GetResourceTypeText(level, false /* plural */, false /* upper case */);
2746 AddAttachmentDocumentation(call, r); 2723 AddAttachmentDocumentation(call, r);
2747 call.GetDocumentation() 2724 call.GetDocumentation()
2748 .SetTag(GetResourceTypeText(t, true /* plural */, true /* upper case */)) 2725 .SetTag(GetResourceTypeText(level, true /* plural */, true /* upper case */))
2749 .SetSummary("Is attachment compressed?") 2726 .SetSummary("Is attachment compressed?")
2750 .SetDescription("Test whether the attachment has been stored as a compressed file on the disk.") 2727 .SetDescription("Test whether the attachment has been stored as a compressed file on the disk.")
2751 .AddAnswerType(MimeType_PlainText, "`0` if the attachment was stored uncompressed, `1` if it was compressed"); 2728 .AddAnswerType(MimeType_PlainText, "`0` if the attachment was stored uncompressed, `1` if it was compressed");
2752 return; 2729 return;
2753 } 2730 }
2754 2731
2755 FileInfo info; 2732 FileInfo info;
2756 if (GetAttachmentInfo(info, t, call)) 2733 if (GetAttachmentInfo(info, level, call))
2757 { 2734 {
2758 std::string answer = (info.GetCompressionType() == CompressionType_None) ? "0" : "1"; 2735 std::string answer = (info.GetCompressionType() == CompressionType_None) ? "0" : "1";
2759 call.GetOutput().AnswerBuffer(answer, MimeType_PlainText); 2736 call.GetOutput().AnswerBuffer(answer, MimeType_PlainText);
2760 } 2737 }
2761 } 2738 }
2864 } 2841 }
2865 2842
2866 2843
2867 static void GetSharedTags(RestApiGetCall& call) 2844 static void GetSharedTags(RestApiGetCall& call)
2868 { 2845 {
2846 const ResourceType level = GetResourceTypeFromUri(call);
2847
2869 if (call.IsDocumentation()) 2848 if (call.IsDocumentation())
2870 { 2849 {
2871 OrthancRestApi::DocumentDicomFormat(call, DicomToJsonFormat_Full); 2850 OrthancRestApi::DocumentDicomFormat(call, DicomToJsonFormat_Full);
2872 2851
2873 ResourceType t = StringToResourceType(call.GetFullUri()[0].c_str()); 2852 std::string r = GetResourceTypeText(level, false /* plural */, false /* upper case */);
2874 std::string r = GetResourceTypeText(t, false /* plural */, false /* upper case */); 2853 call.GetDocumentation()
2875 call.GetDocumentation() 2854 .SetTag(GetResourceTypeText(level, true /* plural */, true /* upper case */))
2876 .SetTag(GetResourceTypeText(t, true /* plural */, true /* upper case */))
2877 .SetSummary("Get shared tags") 2855 .SetSummary("Get shared tags")
2878 .SetDescription("Extract the DICOM tags whose value is constant across all the child instances of " 2856 .SetDescription("Extract the DICOM tags whose value is constant across all the child instances of "
2879 "the DICOM " + r + " whose Orthanc identifier is provided in the URL") 2857 "the DICOM " + r + " whose Orthanc identifier is provided in the URL")
2880 .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest") 2858 .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest")
2881 .AddAnswerType(MimeType_Json, "JSON object containing the values of the DICOM tags") 2859 .AddAnswerType(MimeType_Json, "JSON object containing the values of the DICOM tags")
2882 .SetTruncatedJsonHttpGetSample(GetDocumentationSampleResource(t) + "/shared-tags", 5); 2860 .SetTruncatedJsonHttpGetSample(GetDocumentationSampleResource(level) + "/shared-tags", 5);
2883 return; 2861 return;
2884 } 2862 }
2885 2863
2886 ServerContext& context = OrthancRestApi::GetContext(call); 2864 ServerContext& context = OrthancRestApi::GetContext(call);
2887 std::string publicId = call.GetUriComponent("id", ""); 2865 std::string publicId = call.GetUriComponent("id", "");
3574 } 3552 }
3575 3553
3576 3554
3577 static void GetChildInstancesTags(RestApiGetCall& call) 3555 static void GetChildInstancesTags(RestApiGetCall& call)
3578 { 3556 {
3557 const ResourceType level = GetResourceTypeFromUri(call);
3558
3579 if (call.IsDocumentation()) 3559 if (call.IsDocumentation())
3580 { 3560 {
3581 OrthancRestApi::DocumentDicomFormat(call, DicomToJsonFormat_Full); 3561 OrthancRestApi::DocumentDicomFormat(call, DicomToJsonFormat_Full);
3582 3562
3583 ResourceType t = StringToResourceType(call.GetFullUri()[0].c_str()); 3563 std::string r = GetResourceTypeText(level, false /* plural */, false /* upper case */);
3584 std::string r = GetResourceTypeText(t, false /* plural */, false /* upper case */); 3564 call.GetDocumentation()
3585 call.GetDocumentation() 3565 .SetTag(GetResourceTypeText(level, true /* plural */, true /* upper case */))
3586 .SetTag(GetResourceTypeText(t, true /* plural */, true /* upper case */))
3587 .SetSummary("Get tags of instances") 3566 .SetSummary("Get tags of instances")
3588 .SetDescription("Get the tags of all the child instances of the DICOM " + r + 3567 .SetDescription("Get the tags of all the child instances of the DICOM " + r +
3589 " whose Orthanc identifier is provided in the URL") 3568 " whose Orthanc identifier is provided in the URL")
3590 .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest") 3569 .SetUriArgument("id", "Orthanc identifier of the " + r + " of interest")
3591 .SetHttpGetArgument(IGNORE_LENGTH, RestApiCallDocumentation::Type_JsonListOfStrings, 3570 .SetHttpGetArgument(IGNORE_LENGTH, RestApiCallDocumentation::Type_JsonListOfStrings,
3592 "Also include the DICOM tags that are provided in this list, even if their associated value is long", false) 3571 "Also include the DICOM tags that are provided in this list, even if their associated value is long", false)
3593 .AddAnswerType(MimeType_Json, "JSON object associating the Orthanc identifiers of the instances, with the values of their DICOM tags") 3572 .AddAnswerType(MimeType_Json, "JSON object associating the Orthanc identifiers of the instances, with the values of their DICOM tags")
3594 .SetTruncatedJsonHttpGetSample(GetDocumentationSampleResource(t) + "/instances-tags", 5); 3573 .SetTruncatedJsonHttpGetSample(GetDocumentationSampleResource(level) + "/instances-tags", 5);
3595 return; 3574 return;
3596 } 3575 }
3597 3576
3598 ServerContext& context = OrthancRestApi::GetContext(call); 3577 ServerContext& context = OrthancRestApi::GetContext(call);
3599 std::string publicId = call.GetUriComponent("id", ""); 3578 std::string publicId = call.GetUriComponent("id", "");