1 // SPDX-License-Identifier: Apache-2.0 2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors 3 #pragma once 4 5 #include "app.hpp" 6 #include "async_resp.hpp" 7 #include "http_request.hpp" 8 #include "logging.hpp" 9 10 #include <tinyxml2.h> 11 12 #include <boost/beast/http/field.hpp> 13 #include <boost/beast/http/status.hpp> 14 #include <boost/beast/http/verb.hpp> 15 16 #include <filesystem> 17 #include <format> 18 #include <functional> 19 #include <memory> 20 #include <string> 21 #include <system_error> 22 #include <utility> 23 24 namespace redfish 25 { 26 27 inline std::string getMetadataPieceForFile( 28 const std::filesystem::path& filename) 29 { 30 std::string xml; 31 tinyxml2::XMLDocument doc; 32 std::string pathStr = filename.string(); 33 if (doc.LoadFile(pathStr.c_str()) != tinyxml2::XML_SUCCESS) 34 { 35 BMCWEB_LOG_ERROR("Failed to open XML file {}", pathStr); 36 return ""; 37 } 38 xml += std::format(" <edmx:Reference Uri=\"/redfish/v1/schema/{}\">\n", 39 filename.filename().string()); 40 // std::string edmx = "{http://docs.oasis-open.org/odata/ns/edmx}"; 41 // std::string edm = "{http://docs.oasis-open.org/odata/ns/edm}"; 42 const char* edmx = "edmx:Edmx"; 43 for (tinyxml2::XMLElement* edmxNode = doc.FirstChildElement(edmx); 44 edmxNode != nullptr; edmxNode = edmxNode->NextSiblingElement(edmx)) 45 { 46 const char* dataServices = "edmx:DataServices"; 47 for (tinyxml2::XMLElement* node = 48 edmxNode->FirstChildElement(dataServices); 49 node != nullptr; node = node->NextSiblingElement(dataServices)) 50 { 51 BMCWEB_LOG_DEBUG("Got data service for {}", pathStr); 52 const char* schemaTag = "Schema"; 53 for (tinyxml2::XMLElement* schemaNode = 54 node->FirstChildElement(schemaTag); 55 schemaNode != nullptr; 56 schemaNode = schemaNode->NextSiblingElement(schemaTag)) 57 { 58 std::string ns = schemaNode->Attribute("Namespace"); 59 // BMCWEB_LOG_DEBUG("Found namespace {}", ns); 60 std::string alias; 61 if (std::string_view(ns).starts_with("RedfishExtensions")) 62 { 63 alias = " Alias=\"Redfish\""; 64 } 65 xml += std::format( 66 " <edmx:Include Namespace=\"{}\"{}/>\n", ns, alias); 67 } 68 } 69 } 70 xml += " </edmx:Reference>\n"; 71 return xml; 72 } 73 74 inline void handleMetadataGet( 75 App& /*app*/, const crow::Request& /*req*/, 76 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 77 { 78 std::filesystem::path schema("/usr/share/www/redfish/v1/schema"); 79 std::error_code ec; 80 auto iter = std::filesystem::directory_iterator(schema, ec); 81 if (ec) 82 { 83 BMCWEB_LOG_ERROR("Failed to open XML folder {}", schema.string()); 84 asyncResp->res.result( 85 boost::beast::http::status::internal_server_error); 86 return; 87 } 88 std::string xml; 89 90 xml += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; 91 xml += 92 "<edmx:Edmx xmlns:edmx=\"http://docs.oasis-open.org/odata/ns/edmx\" Version=\"4.0\">\n"; 93 for (const auto& dirEntry : iter) 94 { 95 std::string path = dirEntry.path().filename(); 96 if (!std::string_view(path).ends_with("_v1.xml")) 97 { 98 continue; 99 } 100 std::string metadataPiece = getMetadataPieceForFile(dirEntry.path()); 101 if (metadataPiece.empty()) 102 { 103 asyncResp->res.result( 104 boost::beast::http::status::internal_server_error); 105 return; 106 } 107 xml += metadataPiece; 108 } 109 xml += " <edmx:DataServices>\n"; 110 xml += 111 " <Schema xmlns=\"http://docs.oasis-open.org/odata/ns/edm\" Namespace=\"Service\">\n"; 112 xml += 113 " <EntityContainer Name=\"Service\" Extends=\"ServiceRoot.v1_0_0.ServiceContainer\"/>\n"; 114 xml += " </Schema>\n"; 115 xml += " </edmx:DataServices>\n"; 116 xml += "</edmx:Edmx>\n"; 117 118 asyncResp->res.addHeader(boost::beast::http::field::content_type, 119 "application/xml"); 120 asyncResp->res.write(std::move(xml)); 121 } 122 123 inline void requestRoutesMetadata(App& app) 124 { 125 BMCWEB_ROUTE(app, "/redfish/v1/$metadata/") 126 .methods(boost::beast::http::verb::get)( 127 std::bind_front(handleMetadataGet, std::ref(app))); 128 } 129 130 } // namespace redfish 131