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