26constexpr std::size_t kMaxRootXmlBytes = 8 * 1024 * 1024;
28constexpr std::string_view kSuccessStatus =
29 "urn:oasis:names:tc:ebxml-regrep:ResponseStatusType:Success";
30constexpr std::string_view kPartialSuccessStatus =
31 "urn:oasis:names:tc:ebxml-regrep:ResponseStatusType:PartialSuccess";
40constexpr std::string_view kSchemeClassCode =
41 "urn:uuid:41a5887f-8865-4c09-adf7-e362475b143a";
42constexpr std::string_view kSchemeFormatCode =
43 "urn:uuid:a09d5840-386c-46f2-b5ad-9c3699a4309d";
44constexpr std::string_view kSchemeTypeCode =
45 "urn:uuid:f0306f51-975f-434e-a61b-a3f66f70e3dc";
49constexpr std::string_view kExtIdPatientId =
50 "urn:uuid:58a6f841-87b3-4a3e-92fd-a8ffeff98427";
51constexpr std::string_view kExtIdUniqueId =
52 "urn:uuid:2e82c1f6-a085-4c72-9da3-8640a32e42ab";
54std::string get_slot_value(pugi::xml_node extrinsic,
const char* slot_name) {
55 for (
auto slot : extrinsic.select_nodes(
"*[local-name()='Slot']")) {
56 auto node = slot.node();
57 if (std::string_view(node.attribute(
"name").as_string()) ==
59 auto value = node.select_node(
60 "*[local-name()='ValueList']/"
61 "*[local-name()='Value']").node();
62 return value.text().as_string();
68std::string get_classification_code(pugi::xml_node extrinsic,
69 std::string_view scheme) {
70 for (
auto c : extrinsic.select_nodes(
"*[local-name()='Classification']")) {
73 node.attribute(
"classificationScheme").as_string()) ==
75 return node.attribute(
"nodeRepresentation").as_string();
81std::string get_external_identifier(pugi::xml_node extrinsic,
82 std::string_view scheme) {
83 for (
auto ei : extrinsic.select_nodes(
84 "*[local-name()='ExternalIdentifier']")) {
85 auto node = ei.node();
87 node.attribute(
"identificationScheme").as_string()) ==
89 return node.attribute(
"value").as_string();
95std::string get_localized_name(pugi::xml_node extrinsic) {
100 auto ls = extrinsic.select_node(
101 "*[local-name()='Name']/"
102 "*[local-name()='LocalizedString']").node();
103 return ls.attribute(
"value").as_string();
106std::uint64_t parse_size_slot(pugi::xml_node extrinsic) {
107 const std::string s = get_slot_value(extrinsic,
"size");
108 if (s.empty())
return 0;
114 const auto v = std::strtoull(s.c_str(), &end, 10);
115 if (end == s.c_str())
return 0;
116 return static_cast<std::uint64_t
>(v);
119registry_document_entry extract_entry(pugi::xml_node extrinsic) {
120 registry_document_entry e;
121 e.entry_uuid = extrinsic.attribute(
"id").as_string();
122 e.mime_type = extrinsic.attribute(
"mimeType").as_string();
123 e.status = extrinsic.attribute(
"status").as_string();
124 e.home_community_id = extrinsic.attribute(
"home").as_string();
126 e.unique_id = get_external_identifier(extrinsic, kExtIdUniqueId);
127 e.patient_id = get_external_identifier(extrinsic, kExtIdPatientId);
129 e.creation_time = get_slot_value(extrinsic,
"creationTime");
130 e.repository_unique_id =
131 get_slot_value(extrinsic,
"repositoryUniqueId");
132 e.hash = get_slot_value(extrinsic,
"hash");
133 e.author_person = get_slot_value(extrinsic,
"authorPerson");
134 e.size_bytes = parse_size_slot(extrinsic);
136 e.title = get_localized_name(extrinsic);
137 e.class_code = get_classification_code(extrinsic, kSchemeClassCode);
138 e.format_code = get_classification_code(extrinsic, kSchemeFormatCode);
139 e.type_code = get_classification_code(extrinsic, kSchemeTypeCode);
147 const std::string& root_xml) {
148 if (root_xml.size() > kMaxRootXmlBytes) {
149 return kcenon::common::make_error<registry_query_result>(
151 "AdhocQueryResponse root part exceeds 8 MiB cap (" +
152 std::to_string(root_xml.size()) +
" bytes)",
156 pugi::xml_document doc;
157 auto parse = doc.load_buffer(root_xml.data(), root_xml.size(),
158 pugi::parse_default, pugi::encoding_utf8);
160 return kcenon::common::make_error<registry_query_result>(
162 std::string(
"failed to parse response envelope: ") +
171 auto adhoc_response =
173 "//*[local-name()='AdhocQueryResponse']").node();
174 if (!adhoc_response) {
175 return kcenon::common::make_error<registry_query_result>(
177 "response contains no AdhocQueryResponse element",
181 const std::string status =
182 adhoc_response.attribute(
"status").as_string();
183 if (status.empty()) {
184 return kcenon::common::make_error<registry_query_result>(
186 "AdhocQueryResponse is missing status attribute",
199 if (status != kSuccessStatus && status != kPartialSuccessStatus) {
200 return kcenon::common::make_error<registry_query_result>(
202 "registry reported non-success status: " + status,
207 out.registry_response_status = status;
215 for (
auto node : adhoc_response.select_nodes(
216 "*[local-name()='RegistryObjectList']/"
217 "*[local-name()='ExtrinsicObject']")) {
218 out.entries.push_back(extract_entry(node.node()));
kcenon::common::Result< registry_query_result > parse_query_response(const std::string &root_xml)
Parse an AdhocQueryResponse envelope into a registry_query_result.
constexpr std::string_view error_source
Short identifier used as the "source" field of make_error.
@ transport_invalid_response
@ registry_query_response_malformed
@ registry_failure_response
@ transport_response_too_large
Parser for IHE XDS.b ITI-18 AdhocQueryResponse.
ITI-18 RegistryStoredQueryResponse payload.