xref: /openbmc/bmcweb/redfish-core/lib/metadata.hpp (revision 504af5a0568171b72caf13234cc81380b261fa21)
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