140e9b92eSEd Tanous // SPDX-License-Identifier: Apache-2.0 240e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright OpenBMC Authors 3090ab8e1SEd Tanous #pragma once 4090ab8e1SEd Tanous 5090ab8e1SEd Tanous #include "app.hpp" 6090ab8e1SEd Tanous #include "async_resp.hpp" 7090ab8e1SEd Tanous #include "http_request.hpp" 8d7857201SEd Tanous #include "logging.hpp" 9090ab8e1SEd Tanous 10090ab8e1SEd Tanous #include <tinyxml2.h> 11090ab8e1SEd Tanous 12d7857201SEd Tanous #include <boost/beast/http/field.hpp> 13d7857201SEd Tanous #include <boost/beast/http/status.hpp> 14d7857201SEd Tanous #include <boost/beast/http/verb.hpp> 15d7857201SEd Tanous 16d7857201SEd Tanous #include <filesystem> 17d7857201SEd Tanous #include <format> 18d7857201SEd Tanous #include <functional> 19d7857201SEd Tanous #include <memory> 20d7857201SEd Tanous #include <string> 21d7857201SEd Tanous #include <system_error> 22d7857201SEd Tanous #include <utility> 23090ab8e1SEd Tanous 24090ab8e1SEd Tanous namespace redfish 25090ab8e1SEd Tanous { 26090ab8e1SEd Tanous 27*504af5a0SPatrick Williams inline std::string getMetadataPieceForFile( 28*504af5a0SPatrick Williams const std::filesystem::path& filename) 29090ab8e1SEd Tanous { 30090ab8e1SEd Tanous std::string xml; 31090ab8e1SEd Tanous tinyxml2::XMLDocument doc; 32090ab8e1SEd Tanous std::string pathStr = filename.string(); 33090ab8e1SEd Tanous if (doc.LoadFile(pathStr.c_str()) != tinyxml2::XML_SUCCESS) 34090ab8e1SEd Tanous { 35090ab8e1SEd Tanous BMCWEB_LOG_ERROR("Failed to open XML file {}", pathStr); 36090ab8e1SEd Tanous return ""; 37090ab8e1SEd Tanous } 38090ab8e1SEd Tanous xml += std::format(" <edmx:Reference Uri=\"/redfish/v1/schema/{}\">\n", 39090ab8e1SEd Tanous filename.filename().string()); 40090ab8e1SEd Tanous // std::string edmx = "{http://docs.oasis-open.org/odata/ns/edmx}"; 41090ab8e1SEd Tanous // std::string edm = "{http://docs.oasis-open.org/odata/ns/edm}"; 42090ab8e1SEd Tanous const char* edmx = "edmx:Edmx"; 43090ab8e1SEd Tanous for (tinyxml2::XMLElement* edmxNode = doc.FirstChildElement(edmx); 44090ab8e1SEd Tanous edmxNode != nullptr; edmxNode = edmxNode->NextSiblingElement(edmx)) 45090ab8e1SEd Tanous { 46090ab8e1SEd Tanous const char* dataServices = "edmx:DataServices"; 47090ab8e1SEd Tanous for (tinyxml2::XMLElement* node = 48090ab8e1SEd Tanous edmxNode->FirstChildElement(dataServices); 49090ab8e1SEd Tanous node != nullptr; node = node->NextSiblingElement(dataServices)) 50090ab8e1SEd Tanous { 51090ab8e1SEd Tanous BMCWEB_LOG_DEBUG("Got data service for {}", pathStr); 52090ab8e1SEd Tanous const char* schemaTag = "Schema"; 53090ab8e1SEd Tanous for (tinyxml2::XMLElement* schemaNode = 54090ab8e1SEd Tanous node->FirstChildElement(schemaTag); 55090ab8e1SEd Tanous schemaNode != nullptr; 56090ab8e1SEd Tanous schemaNode = schemaNode->NextSiblingElement(schemaTag)) 57090ab8e1SEd Tanous { 58090ab8e1SEd Tanous std::string ns = schemaNode->Attribute("Namespace"); 59090ab8e1SEd Tanous // BMCWEB_LOG_DEBUG("Found namespace {}", ns); 60090ab8e1SEd Tanous std::string alias; 61090ab8e1SEd Tanous if (std::string_view(ns).starts_with("RedfishExtensions")) 62090ab8e1SEd Tanous { 63090ab8e1SEd Tanous alias = " Alias=\"Redfish\""; 64090ab8e1SEd Tanous } 65090ab8e1SEd Tanous xml += std::format( 66090ab8e1SEd Tanous " <edmx:Include Namespace=\"{}\"{}/>\n", ns, alias); 67090ab8e1SEd Tanous } 68090ab8e1SEd Tanous } 69090ab8e1SEd Tanous } 70090ab8e1SEd Tanous xml += " </edmx:Reference>\n"; 71090ab8e1SEd Tanous return xml; 72090ab8e1SEd Tanous } 73090ab8e1SEd Tanous 74*504af5a0SPatrick Williams inline void handleMetadataGet( 75*504af5a0SPatrick Williams App& /*app*/, const crow::Request& /*req*/, 76090ab8e1SEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 77090ab8e1SEd Tanous { 78090ab8e1SEd Tanous std::filesystem::path schema("/usr/share/www/redfish/v1/schema"); 79090ab8e1SEd Tanous std::error_code ec; 80090ab8e1SEd Tanous auto iter = std::filesystem::directory_iterator(schema, ec); 81090ab8e1SEd Tanous if (ec) 82090ab8e1SEd Tanous { 83090ab8e1SEd Tanous BMCWEB_LOG_ERROR("Failed to open XML folder {}", schema.string()); 84090ab8e1SEd Tanous asyncResp->res.result( 85090ab8e1SEd Tanous boost::beast::http::status::internal_server_error); 86090ab8e1SEd Tanous return; 87090ab8e1SEd Tanous } 88090ab8e1SEd Tanous std::string xml; 89090ab8e1SEd Tanous 90090ab8e1SEd Tanous xml += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; 91090ab8e1SEd Tanous xml += 92090ab8e1SEd Tanous "<edmx:Edmx xmlns:edmx=\"http://docs.oasis-open.org/odata/ns/edmx\" Version=\"4.0\">\n"; 93090ab8e1SEd Tanous for (const auto& dirEntry : iter) 94090ab8e1SEd Tanous { 95090ab8e1SEd Tanous std::string path = dirEntry.path().filename(); 96090ab8e1SEd Tanous if (!std::string_view(path).ends_with("_v1.xml")) 97090ab8e1SEd Tanous { 98090ab8e1SEd Tanous continue; 99090ab8e1SEd Tanous } 100090ab8e1SEd Tanous std::string metadataPiece = getMetadataPieceForFile(dirEntry.path()); 101090ab8e1SEd Tanous if (metadataPiece.empty()) 102090ab8e1SEd Tanous { 103090ab8e1SEd Tanous asyncResp->res.result( 104090ab8e1SEd Tanous boost::beast::http::status::internal_server_error); 105090ab8e1SEd Tanous return; 106090ab8e1SEd Tanous } 107090ab8e1SEd Tanous xml += metadataPiece; 108090ab8e1SEd Tanous } 109090ab8e1SEd Tanous xml += " <edmx:DataServices>\n"; 110090ab8e1SEd Tanous xml += 111090ab8e1SEd Tanous " <Schema xmlns=\"http://docs.oasis-open.org/odata/ns/edm\" Namespace=\"Service\">\n"; 112090ab8e1SEd Tanous xml += 113090ab8e1SEd Tanous " <EntityContainer Name=\"Service\" Extends=\"ServiceRoot.v1_0_0.ServiceContainer\"/>\n"; 114090ab8e1SEd Tanous xml += " </Schema>\n"; 115090ab8e1SEd Tanous xml += " </edmx:DataServices>\n"; 116090ab8e1SEd Tanous xml += "</edmx:Edmx>\n"; 117090ab8e1SEd Tanous 118090ab8e1SEd Tanous asyncResp->res.addHeader(boost::beast::http::field::content_type, 119090ab8e1SEd Tanous "application/xml"); 120090ab8e1SEd Tanous asyncResp->res.write(std::move(xml)); 121090ab8e1SEd Tanous } 122090ab8e1SEd Tanous 123090ab8e1SEd Tanous inline void requestRoutesMetadata(App& app) 124090ab8e1SEd Tanous { 1259008975eSEd Tanous BMCWEB_ROUTE(app, "/redfish/v1/$metadata/") 126090ab8e1SEd Tanous .methods(boost::beast::http::verb::get)( 127090ab8e1SEd Tanous std::bind_front(handleMetadataGet, std::ref(app))); 128090ab8e1SEd Tanous } 129090ab8e1SEd Tanous 130090ab8e1SEd Tanous } // namespace redfish 131