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
getMetadataPieceForFile(const std::filesystem::path & filename)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
handleMetadataGet(App &,const crow::Request &,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)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
requestRoutesMetadata(App & app)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