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