File: openapi_test.cpp

package info (click to toggle)
glaze 6.5.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 7,948 kB
  • sloc: cpp: 121,839; sh: 99; ansic: 26; makefile: 13
file content (208 lines) | stat: -rw-r--r-- 6,434 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
#include <chrono>
#include <thread>

#include "glaze/glaze.hpp"
#include "glaze/net/http_client.hpp"
#include "glaze/net/http_server.hpp"
#include "glaze/rpc/registry.hpp"
#include "ut/ut.hpp"

using namespace ut;

struct User
{
   int id{};
   std::string name{};
   std::string email{};
};

struct UserIdRequest
{
   int id{};
};

struct CreateUserRequest
{
   std::string name{};
   std::string email{};
};

struct ErrorResponse
{
   std::string error{};
   int code{};
};

// Define the UserService that will be exposed via REST
struct UserService
{
   // State
   std::unordered_map<int, User> users = {{1, {1, "Alice", "alice@example.com"}},
                                          {2, {2, "Bob", "bob@example.com"}},
                                          {3, {3, "Charlie", "charlie@example.com"}}};
   int next_id = 4;

   // Methods that will be exposed as REST endpoints

   // Get all users
   std::vector<User> getAllUsers()
   {
      std::vector<User> user_list;
      for (const auto& [id, user] : users) {
         user_list.push_back(user);
      }
      return user_list;
   }

   // Get user by ID
   User getUserById(const UserIdRequest& request)
   {
      auto it = users.find(request.id);
      if (it != users.end()) {
         return it->second;
      }
      // Return empty user if not found
      return User{};
   }

   // Create a new user
   User createUser(CreateUserRequest&& request)
   {
      User user{next_id++, request.name, request.email};
      users[user.id] = user;
      return user;
   }

   // Delete a user
   bool deleteUser(const UserIdRequest& request)
   {
      auto it = users.find(request.id);
      if (it != users.end()) {
         users.erase(it);
         return true;
      }
      return false;
   }
};

template <>
struct glz::meta<UserService>
{
   using T = UserService;
   static constexpr auto value = object(&T::getAllUsers, &T::getUserById, &T::createUser, &T::deleteUser);
};

int main()
{
   "openapi_test"_test = [] {
      glz::http_server server{};

      UserService userService;

      // Create a REST registry
      glz::registry<glz::opts{}, glz::REST> registry;

      // Register the UserService with the registry using server.on pattern
      registry.on(userService);

      // Mount the registry endpoints to the server
      server.mount("/api", registry.endpoints);

      // Add some custom GET endpoints
      server.get("/health", [](const glz::request&, glz::response& res) {
         res.content_type("application/json").body(R"({"status": "healthy", "timestamp": "2025-01-01T00:00:00Z"})");
      });

      server.get("/version", [](const glz::request&, glz::response& res) {
         res.content_type("application/json")
            .body(R"({"version": "1.0.0", "service": "user-management", "build": "dev"})");
      });

      // Add some custom PUT endpoints
      server.put("/settings", [](const glz::request&, glz::response& res) {
         res.content_type("application/json").body(R"({"message": "Settings updated successfully"})");
      });

      server.put("/config/database", [](const glz::request&, glz::response& res) {
         res.content_type("application/json").body(R"({"message": "Database configuration updated", "applied": true})");
      });

      // Add a custom POST endpoint as well
      server.post("/auth/login", [](const glz::request&, glz::response& res) {
         res.content_type("application/json").body(R"({"token": "abc123", "expires_in": 3600, "user_id": 1})");
      });

      // Enable the OpenAPI specification endpoint
      server.enable_openapi_spec("/openapi.json", // The path for the spec
                                 "User Management API", // The title of the API
                                 "1.0.0" // The version of the API
      );

      // Test that the registry has registered the endpoints
      expect(registry.endpoints.routes.size() > 0);

      // Basic validation that endpoints were registered
      // The registry should have registered endpoints for:
      // - GET /getAllUsers
      // - POST /getUserById
      // - POST /createUser
      // - POST /deleteUser

      std::cout << "OpenAPI test completed successfully!" << std::endl;
      std::cout << "Registry has " << registry.endpoints.routes.size() << " endpoints registered" << std::endl;

      // Print registered endpoints for inspection
      for (const auto& [path, methods] : registry.endpoints.routes) {
         for (const auto& [method, entry] : methods) {
            std::cout << "  - " << glz::to_string(method) << " " << path << std::endl;
         }
      }

      // Start the server in a separate thread
      std::thread server_thread([&]() {
         server.bind("127.0.0.1", 8080);
         server.start();
      });

      // Give the server time to start
      std::this_thread::sleep_for(std::chrono::milliseconds(100));

      // Make HTTP request to get OpenAPI spec
      try {
         glz::http_client client{};
         auto response_result = client.get("http://127.0.0.1:8080/openapi.json");

         if (response_result.has_value()) {
            const auto& response = response_result.value();

            std::cout << "\n" << std::string(80, '=') << std::endl;
            std::cout << "OpenAPI Specification from /openapi.json:" << std::endl;
            std::cout << std::string(80, '=') << std::endl;
            std::cout << glz::prettify_json(response.response_body) << std::endl;
            std::cout << std::string(80, '=') << std::endl;

            // Basic validation that we got a valid OpenAPI response
            expect(response.status_code == 200);
            expect(response.response_body.find("openapi") != std::string::npos);
            expect(response.response_body.find("User Management API") != std::string::npos);
            expect(response.response_body.find("paths") != std::string::npos);
         }
         else {
            std::cout << "HTTP request failed with error code: " << response_result.error().value() << std::endl;
            expect(false) << "Failed to get OpenAPI spec";
         }
      }
      catch (const std::exception& e) {
         std::cout << "Error making HTTP request: " << e.what() << std::endl;
         expect(false) << "Failed to get OpenAPI spec";
      }

      // Stop the server
      server.stop();
      if (server_thread.joinable()) {
         server_thread.join();
      }
   };

   return 0;
}