feat(third_party): add oatpp,googltest,benchmark
All checks were successful
sm-rpc / build (Debug, aarch64-linux-gnu) (push) Successful in 1m7s
sm-rpc / build (Debug, arm-linux-gnueabihf) (push) Successful in 1m15s
sm-rpc / build (Debug, host.gcc) (push) Successful in 1m4s
sm-rpc / build (Debug, mipsel-linux-gnu) (push) Successful in 1m16s
sm-rpc / build (Release, aarch64-linux-gnu) (push) Successful in 1m34s
sm-rpc / build (Release, arm-linux-gnueabihf) (push) Successful in 1m33s
sm-rpc / build (Release, host.gcc) (push) Successful in 1m23s
sm-rpc / build (Release, mipsel-linux-gnu) (push) Successful in 1m30s

This commit is contained in:
tqcq
2025-08-19 17:18:53 +08:00
commit ea9d7b5f8c
867 changed files with 204066 additions and 0 deletions

341
third_party/oatpp/changelog/1.1.0.md vendored Normal file
View File

@@ -0,0 +1,341 @@
# Oat++ 1.1.0
Oat++ `1.1.0` is introducing breaking changes.
Please read carefully to prepare for migration.
Feel free to ask questions - [Chat on Gitter](https://gitter.im/oatpp-framework/Lobby)
Contents:
- [No More ObjectWrapper](#no-more-objectwrapper)
- [Object-Mapping Simplified Primitives](#object-mapping-simplified-primitives)
- [Object-Mapping std Collections](#object-mapping-std-collections)
- [Type oatpp::Any](#type-oatppany)
- [Type oatpp::Enum](#object-mapping-enum)
- [DTO - Hashcode & Equals](#dto-hashcode-and-equals)
- [DTO - Fields Annotation](#dto-fields-annotation)
## No More ObjectWrapper
`ObjectWrapper` is a base-class for oatpp core types (`oatpp/core/Types.hpp`).
The explicit `::ObjectWrapper` qualifier is removed.
### DTO:
```cpp
class MyDto : oatpp::DTO { // <--- Notice the 'oatpp::DTO' now. NOT the 'oatpp::Object'
DTO_INIT(MyDto, DTO)
DTO_FIELD(Object<MyDto>, nested); // <--- Notice the oatpp::Object<T> for Objects, instead of T::ObjectWrapper
DTO_FIELD(List<Any>, listOfAny); // <--- No '::ObjectWrapper' for collections.
DTO_FIELD(Fields<List<String>>, mapOfLists);
}
```
### ApiController:
```cpp
ENDPOINT("POST", "body-dto", postWithBody,
BODY_DTO(Object<MyDto>, body)) {
...
}
```
### ApiClient:
```cpp
API_CALL("POST", "body-dto", postWithBody, BODY_DTO(Object<MyDto>, body))
```
### In Other Code-Blocks
```cpp
oatpp::Object<MyDto> myDto;
oatpp::List<oatpp::Object<MyDto>> listOfDtos;
...
auto myDto = objectMapper->readFromString<Object<MyDto>>(json);
auto listOfDtos = objectMapper->readFromString<List<Object<MyDto>>>(json);
```
## Object-Mapping Simplified Primitives
No more `<primitive>->getValue()`.
```cpp
oatpp::Int32 objV = 32;
v_int32 v = objV; // You may check for nullptr before doing this
bool equals = v == objV; // <--- NO NEED to check for nullptr here
bool isNull = objV == nullptr;
bool isNull = !objV;
```
## Object-Mapping std Collections
Now `oatpp::<mapping-enabled-collections>` are based on `std::<collections>`
Example:
```cpp
oatpp::Vector<oatpp::String> vector({});
oatpp::List<oatpp::String> list({});
oatpp::UnorderedSet<oatpp::String> set({});
oatpp::Fields<oatpp::String> pairList({});
oatpp::UnorderedFields<oatpp::String> hashMap({});
oatpp::Vector<oatpp::String> vector = {"a", "b", "c"};
oatpp::List<oatpp::String> list = {"a", "b", "c"};
oatpp::UnorderedSet<oatpp::String> set = {"a", "b", "c"};
oatpp::Fields<oatpp::String> pairList = {{"k1", "v1"}, {"k2", "v2"}, {"k3", "v3"}};
oatpp::UnorderedFields<oatpp::String> hashMap = {{"k1", "v1"}, {"k2", "v2"}, {"k3", "v3"}};
vector[0] = "z"; // <--- Complexity = O(1);
vector->push_back("www");
list[0] = "z"; // <--- Complexity = O(n);
list->push_back("www");
bool contains = set["b"]; // <--- Complexity = O(1);
set->insert("z")
auto value = pairList.getValueByKey("k1"); // <--- Complexity = O(n); // note '.' here, not '->' !!!
pairList->push_back({"key_z", "z"});
hashMap["k1"] = "z" // <--- Complexity = O(1);
hashMap->insert({"key_z", "z"});
for(auto& item : *vector) {
...
}
for(auto& item : *list) {
...
}
for(auto& item : *set) {
...
}
for(auto& pair : *pairList) {
...
}
for(auto& pair : *hashMap) {
...
}
```
## Type oatpp::Any
The new Type Introduced - `oatpp::Any`.
Now it's possible to do like this:
```cpp
class MyDto : public oatpp::DTO {
DTO_INIT(MyDto, DTO)
DTO_FIELD(Any, any); // Put any oatpp::<Type> here
DTO_FIELD(List<Any>, listOfAny)
DTO_FIELD(Fields<Any>, mapOfAny);
};
```
### JSON Serializer
Will serialize Any depending on the value type it stores.
### JSON Deserializer
Will try to guess the type of the `Any`.
Currently, `Any` is deserialized as follows:
- JSON objects are deserialized to `Any` holding `oatpp::Fields<Any>`.
- JSON lists are deserialized to `Any` holding `oatpp::List<Any>`.
- JSON `null` is deserialized to `Any` holding `nullptr`.
- JSON `true`/`false` is deserialized to `Any` holding `oatpp::Boolean`.
- JSON `number` is deserialized to `Any` holding `oatpp::Float64`.
### Example
```cpp
oatpp::Fields<oatpp::Any> map = {
{"title", oatpp::String("Hello Any!")},
{"listOfAny",
oatpp::List<oatpp::Any>({
oatpp::Int32(32),
oatpp::Float32(0.32),
oatpp::Boolean(true)
})
}
};
auto json = mapper->writeToString(map);
```
**Output:**
```json
{
"title": "Hello Any!",
"listOfAny": [
32,
0.3199999928474426,
true
]
}
```
## Object-Mapping Enum
Enum is added to DTO codegen.
### Declaration
```cpp
#include OATPP_CODEGEN_BEGIN(DTO)
ENUM(Enum1, v_int32, // <-- type is mandatory
VALUE(V_1, 1), // <-- integral value is mandatory
VALUE(V_2, 2, "name-2"),
VALUE(V_3, 3, "name-3", "description_3")
)
#include OATPP_CODEGEN_END(DTO)
```
This will generate:
```cpp
enum class Enum1 : v_int32 {
V_1 = 1,
V_2 = 2,
V_3 = 3
}
... // PLUS Meta here
```
Current limitation - `ENUM` can not be declared nested in the class :(.
This may be fixed later.
### Usage
#### DTO
```cpp
ENUM(MyEnum, v_int32,
VALUE(V1, 10, "enum1-v1"),
VALUE(V2, 20, "enum1-v2"),
VALUE(V3, 30, "enum1-v3")
);
class MyDto : public oatpp::DTO {
DTO_INIT(MyDto, DTO)
DTO_FIELD(Enum<MyEnum>, enum1); // Default interpretation - AsString
DTO_FIELD(Enum<MyEnum>::NotNull, enum2); // NOT_NULL constraint for Ser/De
DTO_FIELD(Enum<MyEnum>::AsString, enum3); // Ser/De as string name
DTO_FIELD(Enum<MyEnum>::AsNumber, enum4); // Ser/De as a corresponding integral value
DTO_FIELD(Enum<MyEnum>::AsString::NotNull, enum5);
DTO_FIELD(Enum<MyEnum>::AsNumber::NotNull, enum6);
};
```
#### ApiController
```cpp
ENDPOINT("GET", "enum/as-string", testEnumString,
HEADER(Enum<AllowedHeaderValues>::AsString, enumValue, "X-MyEnum"))
{
return createResponse(Status::CODE_200, "OK");
}
ENDPOINT("GET", "enum/as-number", testEnumNumber,
HEADER(Enum<AllowedHeaderValues>::AsNumber, enumValue, "X-MyEnum"))
{
return createResponse(Status::CODE_200, "OK");
}
```
#### ApiClient
```cpp
API_CALL("GET", "enum/as-string", getWithEnumHeaderAsString, HEADER(Enum<AllowedHeaderValues>::AsString, enumValue, "X-MyEnum"))
API_CALL("GET", "enum/as-number", getWithEnumHeaderAsNumber, HEADER(Enum<AllowedHeaderValues>::AsNumber, enumValue, "X-MyEnum"))
```
### Meta functions
```cpp
{
auto entry = oatpp::Enum<MyEnum>::getEntryByName("<name>");
auto entry = oatpp::Enum<MyEnum>::getEntryByValue(MyEnum::VALUE);
auto entry = oatpp::Enum<MyEnum>::getEntryByUnderlyingValue(123);
auto entry = oatpp::Enum<MyEnum>::getEntryByIndex(0);
...
OATPP_LOGD("Entry", "%d, %s, %d, %d, %s", entry.index, entry.name, entry.value, entry.description)
}
{
const auto& entries = oatpp::Enum<MyEnum>::getEntries();
for(const auto& e : entries) {
...
}
}
```
## DTO Hashcode and Equals
Now DTOs can be used as a Key in `unordered_map` and `unordered_set`.
The convenience `DTO_HC_EQ` (DTO_HASHCODE_AND_EQUALS) macro has been added.
```cpp
class User : public oatpp::DTO {
DTO_INIT(User, DTO)
DTO_FIELD(String, firstName);
DTO_FIELD(String, lastName);
DTO_HC_EQ(firstName, lastName) // List key fields that count in std::hash and "==","!=" operators.
};
```
The `DTO_HC_EQ` macro works taking into account the `DTO_HC_EQ` declared in the parent DTO class.
If no `DTO_HC_EQ` is declared in none of the DTO's parent classes the default behavior is:
- `std::hash` - is `v_uint64` representation of object address.
- operators `==` and `!=` - is comparison of object addresses.
## DTO Fields Annotation
Now it's possible to add a description for DTO fields, which will be automatically
displayed in swagger-UI.
```cpp
class MyDto : public oatpp::DTO {
DTO_INIT(MyDto, DTO)
DTO_FIELD_INFO(id) {
info->description = "identifier";
}
DTO_FIELD(String, id);
};
```
*Note: The `description` is currently the only info you can add to the DTO field
(This may be extended later). In order to provide the list of possible values - use the
new Enum feature - [Type oatpp::Enum](#object-mapping-enum).*

234
third_party/oatpp/changelog/1.2.0.md vendored Normal file
View File

@@ -0,0 +1,234 @@
# Oat++ 1.2.0
Feel free to ask questions - [Chat on Gitter](https://gitter.im/oatpp-framework/Lobby)
Contents:
- [Continuous Multipart Streaming](#continuous-multipart-streaming)
- [Changes In ConnectionProvider Interface](#changes-in-connectionprovider-interface)
- [Object-Mapping - Type Interpretations](#type-interpretations)
- [The new Oat++ ORM](#orm)
- [Changes in oatpp::network Namespace](#changes-in-oatppnetwork-namespace)
- [New Modules](#new-modules)
## Continuous Multipart Streaming
Now it's possible to continuously stream multipart data to the client.
For example, you can implement `multipart/x-mixed-replace` video stream.
Both Simple-APIs and Async-APIs available.
```cpp
using namespace oatpp::web::mime::multipart;
class MPStream : public Multipart {
public:
MPStream()
: Multipart(generateRandomBoundary())
{}
std::shared_ptr<Part> readNextPart(async::Action& action /* For Simple-API action is ignored */) override {
std::this_thread::sleep_for(std::chrono::seconds(1)); // For Simple-API you may sleep just like that
oatpp::String frameData = "<frame-binary-jpeg-data>" // frame data. One jpeg image.
auto part = std::make_shared<Part>();
part->putHeader(Header::CONTENT_TYPE, "image/jpeg");
part->setDataInfo(std::make_shared<oatpp::data::stream::BufferInputStream>(frameData));
return part;
}
void writeNextPart(const std::shared_ptr<Part>& part, async::Action& action) override {
throw std::runtime_error("No writes here!!!");
}
};
ENDPOINT("GET", "multipart-stream", multipartStream) {
auto multipart = std::make_shared<MPStream>();
auto body = std::make_shared<oatpp::web::protocol::http::outgoing::MultipartBody>(
multipart,
"multipart/x-mixed-replace",
true /* flush parts */
);
return OutgoingResponse::createShared(Status::CODE_200, body);
}
```
## Changes In ConnectionProvider Interface
Both `oatpp::network::ServerConnectionProvider` and `oatpp::network::ClientConnectionProvider`
are now subclasses of a general `oatpp::provider::Provider<data::stream::IOStream>` class.
Renamed methods:
- `getConnection()` --> `get()`
- `getConnectionAsync()` --> `getAsync()`
- `invalidateConnection()` --> `invalidate()`
This was done to unify providers' APIs and to have same API for all resource providers and resource pools.
Connection Providers and Connection Pools of the new ORM database adaptors are implemented based on the same functionality.
More info on ConnectionProvider changes see here - [Changes in oatpp::network Namespace](#changes-in-oatppnetwork-namespace).
## Type Interpretations
To simplify the integration of custom types with oatpp Object-Mapping framework the the "Type Interpretation" feature was introduced.
Let's say you have some struct that is not part of oatpp object-mapping framework.
```cpp
struct VPoint {
v_int32 x;
v_int32 y;
v_int32 z;
};
```
To integrate it with oatpp object-mapping you can do the following:
```cpp
namespace __class {
class PointClass;
}
/* Declare ObjectWrapper for your type */
/* Mapping-Enabled Point */
typedef oatpp::data::mapping::type::Primitive<VPoint, __class::PointClass> Point;
namespace __class {
/**
* Type info
*/
class PointClass {
private:
/**
* Type interpretation
*/
class Inter : public oatpp::Type::Interpretation<Point, oatpp::UnorderedFields<oatpp::Int32>> {
public:
oatpp::UnorderedFields<oatpp::Int32> interpret(const Point& value) const override {
return {{"x", value->x}, {"y", value->y}, {"z", value->z}};
}
Point reproduce(const oatpp::UnorderedFields<oatpp::Int32> map) const override {
return Point({map["x"], map["y"], map["z"]});
}
};
public:
static const oatpp::ClassId CLASS_ID;
static oatpp::Type* getType(){
static Type type(
CLASS_ID, nullptr, nullptr,
{
{"my-types", new Inter()} //<-- Add type interpretation
}
);
return &type;
}
};
const oatpp::ClassId PointClass::CLASS_ID("my-types::Point");
}
```
Now the "Point" struct can be serialized/deserialized with object mappers.
```cpp
oatpp::parser::json::mapping::ObjectMapper mapper;
{
auto config = mapper.getSerializer()->getConfig();
config->enabledInterpretations = {"my-types"}; // Enable "my-types" for serializer
}
{
auto config = mapper.getDeserializer()->getConfig();
config->enabledInterpretations = {"my-types"}; // Enable "my-types" for deserializer
}
Point point ({1, 2, 3}); // Create mapping-enabled Point
auto json = mapper.writeToString(point); // Serialize Point
auto pointClone = mapper.readFromString<Point>(json); // Deserialize Point
```
## ORM
The main feature of the `1.2.0` release is the new ORM Framework.
For details see - [Object-Relational Mapping (ORM) framework](https://oatpp.io/docs/components/orm/)
### ORM Adaptors
- [oatpp-sqlite](https://github.com/oatpp/oatpp-sqlite) - SQLite adapter for Oat++ ORM. Full support.
- [oatpp-postgresql](https://github.com/oatpp/oatpp-postgresql) - Oat++ ORM adapter for PostgreSQL. Alpha version.
## Changes in oatpp::network Namespace
### Moved and Renamed
- `oatpp::network::server::SimpleTCPConnectionProvider` --> `oatpp::network::tcp::server::ConnectionProvider`
- `oatpp::network::client::SimpleTCPConnectionProvider` --> `oatpp::network::tcp::client::ConnectionProvider`
- `oatpp::network::server::ConnectionHandler` --> `atpp::network::ConnectionHandler`
- `oatpp::network::server::Server` --> `oatpp::network::Server`
- `oatpp::network::Connection` --> `oatpp::network::tcp::Connection`
### New Classes
- `oatpp::network::Address`
### New Constructors for Connection Providers
oatpp::network::tcp::server::ConnectionProvider
```cpp
oatpp::network::tcp::server::ConnectionProvider::createShared(
{"localhost", 8000} /* network::Address */
);
oatpp::network::tcp::server::ConnectionProvider::createShared(
{"localhost", 8000, oatpp::network::Address::IP_4} /* network::Address */
);
oatpp::network::tcp::server::ConnectionProvider::createShared(
{"localhost", 8000, oatpp::network::Address::IP_4} /* network::Address */,
true /* use extended connections */
);
```
oatpp::network::tcp::client::ConnectionProvider
```cpp
oatpp::network::tcp::client::ConnectionProvider::createShared(
{"localhost", 8000} /* network::Address */
);
oatpp::network::tcp::client::ConnectionProvider::createShared(
{"localhost", 8000, oatpp::network::Address::IP_4} /* network::Address */
);
oatpp::network::tcp::client::ConnectionProvider::createShared(
{"localhost", 8000, oatpp::network::Address::IP_4} /* network::Address */,
true /* use extended connections */
);
```
## New Modules
- [oatpp-sqlite](https://github.com/oatpp/oatpp-sqlite) - SQLite adapter for Oat++ ORM. Full support.
- [oatpp-postgresql](https://github.com/oatpp/oatpp-postgresql) - Oat++ ORM adapter for PostgreSQL. Alpha version.
- [oatpp-protobuf](https://github.com/oatpp/oatpp-protobuf) - Protobuf integration with oatpp object-mapping framework. Use
protobuf objects as regular oatpp DTOs. Serialize/Deserialize protobuf object to/from JSON, BSON and other oatpp supported formats.

222
third_party/oatpp/changelog/1.2.5.md vendored Normal file
View File

@@ -0,0 +1,222 @@
# Oat++ 1.2.5
Previous release - [1.2.0](1.2.0.md)
Feel free to ask questions - [Chat on Gitter!](https://gitter.im/oatpp-framework/Lobby)
Contents:
- [Introduce ResponseInterceptor](#introduce-responseinterceptor)
- [Enable Global CORS](#enable-global-cors)
- [Headers Multimap](#headers-multimap)
- [Better Router API](#better-router-api)
- [ORM Clean Section](#orm-clean-section)
- [ORM PostgreSQL - Arrays Support](#orm-postgresql---arrays-support)
- [Swagger-UI Example Values](#swagger-ui-example-values)
- [New Modules](#new-modules)
## Introduce ResponseInterceptor
### Declare Response Interceptor
```cpp
#include "oatpp/web/server/interceptor/ResponseInterceptor.hpp"
class MyResponseInterceptor : public ResponseInterceptor {
public:
std::shared_ptr<OutgoingResponse> intercept(const std::shared_ptr<IncomingRequest>& request,
const std::shared_ptr<OutgoingResponse>& response) override
{
// TODO modify response or create a new one
return response; // return modified response
// returning nullptr will result in an error
}
};
```
### Register global request interceptor
```cpp
OATPP_CREATE_COMPONENT(std::shared_ptr<oatpp::network::ConnectionHandler>, serverConnectionHandler)([] {
OATPP_COMPONENT(std::shared_ptr<oatpp::web::server::HttpRouter>, router);
auto connectionHandler = oatpp::web::server::HttpConnectionHandler::createShared(router);
/* Add MyResponseInterceptor */
connectionHandler->addResponseInterceptor(std::make_shared<MyResponseInterceptor>());
return connectionHandler;
}());
```
## Enable Global CORS
To enable global CORS for all endpoints:
- Add **Request** Interceptor - `oatpp::web::server::interceptor::AllowOptionsGlobal` to `ConnectionHandler`.
- Add **Response** Interceptor - `atpp::web::server::interceptor::AllowCorsGlobal` to `ConnectionHandler`.
```cpp
#include "oatpp/web/server/interceptor/AllowCorsGlobal.hpp"
...
OATPP_CREATE_COMPONENT(std::shared_ptr<oatpp::network::ConnectionHandler>, serverConnectionHandler)([] {
OATPP_COMPONENT(std::shared_ptr<oatpp::web::server::HttpRouter>, router); // get Router component
auto connectionHandler = oatpp::web::server::HttpConnectionHandler::createShared(router);
/* Add CORS-enabling interceptors */
connectionHandler->addRequestInterceptor(std::make_shared<oatpp::web::server::interceptor::AllowOptionsGlobal>());
connectionHandler->addResponseInterceptor(std::make_shared<oatpp::web::server::interceptor::AllowCorsGlobal>());
return connectionHandler;
}());
```
## Headers Multimap
Now headers are stored using [std::multimap](https://en.cppreference.com/w/cpp/container/multimap) and can store multiple entries with the same key.
Put multiple headers:
```cpp
auto response = createResponse(Status::CODE_200, "");
response->putHeader("Set-Cookie", "...");
response->putHeader("Set-Cookie", "...");
return response;
```
Log all "Set-Cookie" headers:
```cpp
const auto& map = headers.getAll();
auto itlow = map.lower_bound("Set-Cookie");
auto itup = map.upper_bound("Set-Cookie");
for(auto it = itlow; it != itup; it ++) {
oatpp::String value = it->second.toString();
OATPP_LOGD("Header", "Set-Cookie: %s", value->c_str())
}
```
## Better Router API
Now Router class is a template and can store any value-types and not only `RequestHandler`s.
Example use-case - check if endpoint should require authorization:
**Add Routs**
```cpp
oatpp::web::server::HttpRouterTemplate<bool> authEndpoints;
authEndpoint.route("POST", "login", false); // DO NOT require auth for /login path
authEndpoint.route("POST", "auth", false); // DO NOT require auth for /auth path
authEndpoint.route("GET", "*", true); // require auth for all GET
authEndpoint.route("POST", "*", true); // require auth for all POST
authEndpoint.route("OPTIONS", "*", false); // DO NOT require auth for OPTIONS
```
**Check Auth**
```cpp
auto r = authEndpoints.getRoute(request->getStartingLine().method, request->getStartingLine().path);
if(r && r.getEndpoint() == true) {
// check auth
}
```
## ORM Clean Section
For modules:
- [oatpp-sqlite](https://github.com/oatpp/oatpp-sqlite)
- [oatpp-postgresql](https://github.com/oatpp/oatpp-postgresql)
Now it's possible to declare a "clean section" - a section that is untouched by DSL processor.
Clean section begins with `<!!` and ends with `!!>`.
**Note:** `<!!` and `!!>` char sequences are ignored inside string.
### Example
Such query:
```cpp
QUERY(selectUserName,
"SELECT <!! name::varchar !!> FROM users WHERE userId=:userId",
PARAM(String, userId))
```
Will be processed as follows:
```sql
SELECT name::varchar FROM users WHERE userId="<user-id-value>"
```
Note: unlike the `:userId` the `:varchar` char-sequence wasn't interpreted as a template parameter (unlike the `:userId`).
## ORM PostgreSQL - Arrays Support
[oatpp-postgresql](https://github.com/oatpp/oatpp-postgresql) now supports arrays.
More about PostgreSQL arrays - read [here](https://www.postgresql.org/docs/13/arrays.html)
## Swagger-UI Example Values
Now it's possible to add example-values to `RequestBody`, `Response`, and `Parameters` (Path, Headers, Queries)
### Add Consumes Examples
```cpp
ENDPOINT_INFO(myEndpoint) {
info->addConsumes<Object<MyDto>>("application/json")
.addExample("example_1", MyDto::createShared(... /* params here */ ))
.addExample("example_2", MyDto::createShared(... /* params here */ ))
.addExample("example_3", MyDto::createShared(... /* params here */ ));
}
```
### Add Response Examples
```cpp
ENDPOINT_INFO(myEndpoint) {
info->addResponse<Object<MyDto>>(Status::CODE_200, "application/json")
.addExample("Successful Response_1", MyDto::createShared(... /* params */ ));
info->addResponse<Object<ErrorDto>>(Status::CODE_404, "application/json")
.addExample("Error - Not found", ErrorDto::createShared(404, "Not Found"));
info->addResponse<Object<ErrorDto>>(Status::CODE_500, "application/json")
.addExample("Error - DB Connection", ErrorDto::createShared(500, "Can't connect to DB"))
.addExample("Error - Unknown", ErrorDto::createShared(500, "Unknown Error"));
}
```
### Add Parameter Examples
```cpp
ENDPOINT_INFO(myEndpoint) {
info->pathParams["userRole"]
.addExample("Admin", oatpp::Enum<UserRole>(UserRole::ADMIN))
.addExample("Guest", oatpp::Enum<UserRole>(UserRole::GUEST));
}
```
## New Modules
- [oatpp-openssl](https://github.com/oatpp/oatpp-openssl) - TLS adaptor for OpenSSL (Recommended to use).

526
third_party/oatpp/changelog/1.3.0.md vendored Normal file
View File

@@ -0,0 +1,526 @@
# Oat++ 1.3.0
Previous release - [1.2.5](1.2.5.md)
Feel free to ask questions - [Chat on Gitter!](https://gitter.im/oatpp-framework/Lobby)
Contents:
- [The New oatpp::String](#the-new-oatppstring)
- [ConnectionPool::get() Timeout](#connectionpoolget-timeout)
- [JSON Serializer Escape Flags](#json-serializer-escape-flags)
- [Headers Stored In unordered_multimap](#headers-stored-in-unordered_multimap)
- [QueryParameters Stored In unordered_multimap](#queryparameters-stored-in-unordered_multimap)
- [Polymorphic DTO_FIELD](#polymorphic-dto_field)
- [ConnectionMonitor](#connectionmonitor)
- [Request Data Bundle](#request-data-bundle)
- [ConnectionProviderSwitch](#connectionproviderswitch)
- [Proper Server Stoppage](#proper-server-stoppage)
- [TemporaryFile](#temporaryfile)
- [Better Multipart](#better-multipart)
- [Response::getBody()](#responsegetbody)
- [data::stream::FIFOStream](#datastreamfifostream)
- [data::stream::BufferedInputStream](#datastreambufferedinputstream)
- [oatpp::parser::json::mapping::Serializer::Config::alwaysIncludeRequired](#oatppparserjsonmappingserializerconfigalwaysincluderequired)
## The New oatpp::String
Now it's much easier to use `oatpp::String` since `oatpp::String` is now wrapper over `std::string`
```cpp
{
std::string s1 = Hello;
oatpp::String s2 = s1;
}
{
oatpp::String s1 = "Hello";
std::string s2 = *s1; // *s1 returns a reference to the internal std::string object
}
{
oatpp::String s1 = "Hello";
std::string s2 = s1; // implicit cast
}
{
oatpp::String s1 = nullptr;
std::string s2 = s1; // implicit cast from null-value throws runtime_error
}
{
oatpp::String s1 = "Hello";
bool b = s1 == "Hello"; // compare s1 with const char*
assert(b);
}
{
oatpp::String s1 = "Hello";
std::stringg s2 = "Hello";
bool b = s1 == s2; // compare s1 with std::string
assert(b);
}
{
oatpp::String s1 = "Hello";
std::string s2 = "World";
oatpp::String s3 = s1 + " " + s2; // concat oatpp::String with const char* and std::string directly
OATPP_LOGD("TEST", "str='%s'", s3->c_str()) // prints 'Hello World'
}
{
oatpp::String s1 = nullptr;
oatpp::String s2 = "hello";
OATPP_ASSERT(s1.getValue("default") == "default")
OATPP_ASSERT(s2.getValue("default") == "hello")
}
```
## ConnectionPool::get() Timeout
[#408](https://github.com/oatpp/oatpp/issues/408)
```cpp
{
auto connectionProvider = oatpp::network::tcp::client::ConnectionProvider::createShared({"httpbin.org", 80});
auto pool = oatpp::network::ClientConnectionPool::createShared(connectionProvider,
1,
std::chrono::seconds(10),
std::chrono::seconds(5));
OATPP_LOGD("TEST", "start")
auto c1 = pool->get(); //<--- this one will succeed
OATPP_LOGD("TEST", "c1=%llu", c1.get())
auto c2 = pool->get(); //<--- this one will fail in 5 sec. Since Max-Resources is 1, Pool timeout is 5 sec. And c1 is not freed.
OATPP_LOGD("TEST", "c2=%llu", c2.get())
}
```
Output:
```
D |2021-08-04 01:32:56 1628029976986744| TEST:start
D |2021-08-04 01:32:57 1628029977126940| TEST:c1=140716915331208
D |2021-08-04 01:33:02 1628029982128324| TEST:c2=0
```
## JSON Serializer Escape Flags
[#381](https://github.com/oatpp/oatpp/issues/381)
Now you can control if solidus is escaped or not.
### Default Behavior
```cpp
oatpp::parser::json::mapping::ObjectMapper mapper;
// mapper.getSerializer()->getConfig()->escapeFlags = 0; // by default FLAG_ESCAPE_SOLIDUS is ON
auto res = mapper.writeToString(oatpp::String("https://oatpp.io/"));
OATPP_LOGD("TEST", "res='%s'", res->c_str())
```
Output:
```
res='"https:\/\/oatpp.io\/"' # by default, solidus is escaped
```
### Clear Escape Flags
```cpp
oatpp::parser::json::mapping::ObjectMapper mapper;
mapper.getSerializer()->getConfig()->escapeFlags = 0;
auto res = mapper.writeToString(oatpp::String("https://oatpp.io/"));
OATPP_LOGD("TEST", "res='%s'", res->c_str())
```
Output:
```
res='"https://oatpp.io/"' # solidus isn't escaped
```
## Headers Stored In unordered_multimap
Headers are now stored using [std::unordered_multimap](https://en.cppreference.com/w/cpp/container/unordered_multimap).
Put multiple headers:
```cpp
auto response = createResponse(Status::CODE_200, "");
response->putHeader("Set-Cookie", "...");
response->putHeader("Set-Cookie", "...");
return response;
```
Log all "Set-Cookie" headers:
```cpp
const auto& map = headers.getAll();
auto bucket = map.bucket("Set-Cookie");
auto bucketBegin = map.begin(bucket);
auto bucketEnd = map.end(bucket);
for(auto it = bucketBegin; it != bucketEnd; it ++) {
oatpp::String value = it->second.toString();
OATPP_LOGD("Header", "Set-Cookie: %s", value->c_str())
}
```
## QueryParameters Stored In unordered_multimap
QueryParameters are now stored using [std::unordered_multimap](https://en.cppreference.com/w/cpp/container/unordered_multimap).
Log all entries of "userId" query parameter:
```cpp
const auto& map = request->getQueryParameters().getAll();
auto bucket = map.bucket("userId");
auto bucketBegin = map.begin(bucket);
auto bucketEnd = map.end(bucket);
for(auto it = bucketBegin; it != bucketEnd; it ++) {
oatpp::String value = it->second.toString();
OATPP_LOGD("QueryParameter", "userId: %s", value->c_str())
}
```
## Polymorphic DTO_FIELD
Now, when used inside of a DTO, we can specify exact types that `oatpp::Any` can store by specifying `DTO_FIELD_TYPE_SELECTOR`:
```cpp
/* Possible type of a DTO_FIELD */
class ClassA : public oatpp::DTO {
DTO_INIT(ClassA, DTO)
DTO_FIELD(String, value);
};
/* Possible type of a DTO_FIELD */
class ClassB : public oatpp::DTO {
DTO_INIT(ClassB, DTO)
DTO_FIELD(Vector<String>, values);
};
/* enum of possible DTO_FIELD types */
ENUM(ClassType, v_int32,
VALUE(CLASS_TYPE_A, 0),
VALUE(CLASS_TYPE_B, 1)
)
/* our DTO */
class ResponseDto : public oatpp::DTO {
DTO_INIT(ResponseDto, DTO)
/* type control field */
DTO_FIELD(Enum<ClassType>::AsString, payloadType);
/* polymorphic field */
DTO_FIELD(Any, payload);
/* type selector */
DTO_FIELD_TYPE_SELECTOR(payload) {
if(!payloadType) return Void::Class::getType();
switch (*payloadType) {
case ClassType::CLASS_TYPE_A: return Object<ClassA>::Class::getType();
case ClassType::CLASS_TYPE_B: return Object<ClassB>::Class::getType();
}
}
};
...
/* send polymorphic payload to client */
ENDPOINT("GET", "payload", getPayload) {
auto payload = ClassB::createShared();
payload->values = {"value1", "value2", "value3"};
auto r = ResponseDto::createShared();
r->payloadType = ClassType::CLASS_TYPE_B;
r->payload = payload;
return createDtoResponse(Status::CODE_200, r);
}
/* receive polymorphic payload from client */
ENDPOINT("POST", "payload", postPayload,
BODY_DTO(oatpp::Object<ResponseDto>, r))
{
/* check type-control field and retrieve value of the corresponding type */
if(r->payloadType == ClassType::CLASS_TYPE_B) {
auto payload = r->payload.retrieve<oatpp::Object<ClassB>>();
for(auto& value : *payload->values) {
OATPP_LOGD("VALUE", "%s", value->c_str())
}
}
return createResponse(Status::CODE_200, "OK");
}
```
## ConnectionMonitor
`oatpp::network::monitor::ConnectionMonitor` is a middleman who's able to monitor provided connections and close those ones that not satisfy selected rules.
```cpp
OATPP_CREATE_COMPONENT(std::shared_ptr<oatpp::network::ServerConnectionProvider>, serverConnectionProvider)([] {
auto connectionProvider = oatpp::network::tcp::server::ConnectionProvider::createShared({"0.0.0.0", 8000, oatpp::network::Address::IP_4});
auto monitor = std::make_shared<oatpp::network::monitor::ConnectionMonitor>(connectionProvider);
/* close all connections that stay opened for more than 120 seconds */
monitor->addMetricsChecker(
std::make_shared<oatpp::network::monitor::ConnectionMaxAgeChecker>(
std::chrono::seconds(120)
)
);
/* close all connections that have had no successful reads and writes for longer than 5 seconds */
monitor->addMetricsChecker(
std::make_shared<oatpp::network::monitor::ConnectionInactivityChecker>(
std::chrono::seconds(5),
std::chrono::seconds(5),
)
);
return monitor;
}());
```
**Note:** `ConnectionMonitor` also works with `ClientConnectionProvider` as well.
## Request Data Bundle
Now there is a data bundle associated with the Request and the Response which makes it easy to pass data through middleware interceptors and endpoints.
Example:
```cpp
class MyAuthInterceptor : public oatpp::web::server::interceptor::RequestInterceptor {
public:
std::shared_ptr<OutgoingResponse> intercept(const std::shared_ptr<IncomingRequest>& request) override {
/* authorize request and get auth data */
oatpp::Object<AuthDto> authData = authorize(request);
if(!authData) {
return OutgoingResponse::createShared(Status::CODE_401, nullptr);
}
/* put auth data to bundle for later use at an endpoint */
request->putBundleData("auth", authData);
return nullptr; // continue processing
}
};
...
ENDPOINT("GET", "videos/{videoId}", getVideoById,
PATH(String, videoId),
BUNDLE(oatpp::Object<AuthDto>, authData, "auth"))
{
...
}
```
## ConnectionProviderSwitch
[#483](https://github.com/oatpp/oatpp/issues/483)
`oatpp::network::ConnectionProviderSwitch` can be used to change connection providers on the go, ex.: when you want to reload an SSL certificate without stopping the server.
```cpp
/* create server connection provider component */
/* use ConnectionProviderSwitch instead of a regular ServerConnectionProvider */
OATPP_CREATE_COMPONENT(std::shared_ptr<oatpp::network::ConnectionProviderSwitch>, serverConnectionProvider)([this] {
/* create SSL provider */
auto sslProvider = oatpp::libressl::server::ConnectionProvider::createShared(...);
/* create oatpp::network::ConnectionProviderSwitch*/
return std::make_shared<oatpp::network::ConnectionProviderSwitch>(sslProvider /* current provider */);
}());
...
void reloadCert() {
/* get server connection provider component */
OATPP_COMPONENT(std::shared_ptr<oatpp::network::ConnectionProviderSwitch>, providerSwitch);
/* create new SSL provider with new cert */
auto sslProvider = oatpp::libressl::server::ConnectionProvider::createShared(...);
/* set new provider */
providerSwitch->resetProvider(sslProvider);
}
```
Additionally, resource invalidation is no longer supported by ConnectionProvider.
Please use either ResourceHandleTemplate::invalidate() or Invalidator::invalidate(resource) directly.
## Proper Server Stoppage
Fix to [#476](https://github.com/oatpp/oatpp/issues/476), [#269](https://github.com/oatpp/oatpp/issues/269)
Now call to `HttpConnectionHandler::stop()`, `AsyncHttpConnectionHandler::stop()` will shutdown all opened connections and will wait until all request handlers exit.
## TemporaryFile
Introduce `oatpp::data::resource::TemporaryFile`.
Use-case:
Temporary file resolves concurrency issues during file uploads.
Also, a temporary file ensures that partially uploaded (due to errors/exceptions) resources will be automatically deleted at the end of the block.
```cpp
#include "oatpp/core/data/resource/TemporaryFile.hpp"
...
ENDPOINT("POST", "/upload", upload,
REQUEST(std::shared_ptr<IncomingRequest>, request))
{
/* create random file in '/tmp' folder */
oatpp::data::resource::TemporaryFile tmp("/tmp");
/* transfer body to temporary file */
request->transferBody(tmp.openOutputStream());
/* move file to permanent storage */
OATPP_ASSERT_HTTP(tmp.moveFile("/path/to/permanent/storage/avatar.png"), Status::CODE_500, "Failed to save file")
/* return 200 */
return createResponse(Status::CODE_200, "OK");
}
```
## Better Multipart
Multipart API has been changed and improved.
Now it's possible to upload multiple files using `TemporaryFile` and keep track of
all parts and their corresponding data resources.
```cpp
#include "oatpp/web/mime/multipart/TemporaryFileProvider.hpp"
#include "oatpp/web/mime/multipart/Reader.hpp"
#include "oatpp/web/mime/multipart/PartList.hpp"
...
namespace multipart = oatpp::web::mime::multipart;
...
ENDPOINT("POST", "upload", upload,
REQUEST(std::shared_ptr<IncomingRequest>, request))
{
/* create multipart object */
multipart::PartList multipart(request->getHeaders());
/* create multipart reader */
multipart::Reader multipartReader(&multipart);
/* setup reader to stream parts to a temporary files by default */
multipartReader.setDefaultPartReader(multipart::createTemporaryFilePartReader("/tmp" /* /tmp directory */));
/* upload multipart data */
request->transferBody(&multipartReader);
/* list all parts and locations to corresponding temporary files */
auto parts = multipart.getAllParts();
for(auto& p : parts) {
OATPP_LOGD("part", "name=%s, location=%s", p->getName()->c_str(), p->getPayload()->getLocation()->c_str())
}
/* return 200 */
return createResponse(Status::CODE_200, "OK");
}
```
## Response::getBody()
`oatpp::web::protocol::http::outgoing::Response` has a new method `getBody()` to retrieve the body of the response.
This is handy for response interceptors.
## data::stream::FIFOStream
The new `FIFOStream` stream is a buffered
[`InputStream`](https://oatpp.io/api/latest/oatpp/core/data/stream/Stream/#inputstream) with an
[`WriteCallback`](https://oatpp.io/api/latest/oatpp/core/data/stream/Stream/#writecallback).
Check the corresponding documentation on how to use these interfaces.
Instead of using a static buffer like `BufferInputStream` it is build upon `data::buffer::FIFOBuffer` and is able to
dynamically grow when data is written to it that would surpass its capacity.
It is especially useful if you need to buffer data from a stream upfront or have multiple data sources that should be
buffered in a single stream.
However, it is not synchronized, so be careful when using `FIFOStream` in a multithreaded manner.
You need to implement your own locking.
## data::stream::BufferedInputStream
`FIFOStream` also introduced a new interface
[`BufferedInputStream`](https://oatpp.io/api/latest/oatpp/core/data/stream/Stream/#bufferedinputstream) which unifies
the buffered-stream-interface all existing buffered streams (`InputStreamBufferedProxy`, `BufferInputStream`,
`FIFOStream`) to allow for generalisation.
## oatpp::parser::json::mapping::Serializer::Config::alwaysIncludeRequired
If `oatpp::parser::json::mapping::Serializer::Config::includeNullFields == false` there might still be the requirement
to include some fields even if they are `nullptr`, because they are required by the deserializing end.
Consider the following DTO and endpoint-snippet.
```c++
class StatusDto : public oatpp::DTO {
DTO_INIT(StatusDto, DTO)
DTO_FIELD_INFO(status) {
info->required = true;
}
DTO_FIELD(String, status);
DTO_FIELD(Int32, code);
DTO_FIELD(String, message);
};
// endpoint code:
ENDPOINT("GET", "/status", status) {
auto dto = StatusDto::createShared();
dto->code = 200;
return createDtoResponse(Status::CODE_200, dto);
}
```
With a serializer with its config set to `Serializer::Config::includeNullFields = false`, the snippet would just yield `{"code":200}`.
However, `status` is a required field.
Now, one can set `Serializer::Config::alwaysIncludeRequired = true`.
With `alwaysIncludeRequired == true`, the same snippet would yield `{"status":null,"code":200}`, even with `includeNullFields == false`.

49
third_party/oatpp/changelog/1.4.0.md vendored Normal file
View File

@@ -0,0 +1,49 @@
# Oat++ 1.4.0
Previous release - [1.3.0](1.3.0.md)
Feel free to ask questions - [Chat on Gitter!](https://gitter.im/oatpp-framework/Lobby)
Contents:
- [URL Encoder And Decoder](#url-encoder-and-decoder)
- [Introduce async::ConditionVariable](#async-condition-variable)
## URL Encoder And Decoder
```cpp
#include "oatpp/encoding/Url.hpp"
...
oatpp::String data = "Hello URL-Encoder!!!";
oatpp::encoding::Url::Config config;
auto encoded = oatpp::encoding::Url::encode(data, config);
auto decoded = oatpp::encoding::Url::decode(encoded);
OATPP_ASSERT(decoded == data)
```
**Note**: Oat++ does NOT automatically decode URL and its parameters on endpoint hit.
## Async Condition Variable
```cpp
#include "oatpp/core/async/ConditionVariable.hpp"
...
oatpp::async::Lock* m_lock;
oatpp::async::ConditionVariable* m_cv;
...
Action act() override {
return m_cv->waitFor(m_lock, // async::Lock
[this]{return m_resource->counter == 100;}, // condition
std::chrono::seconds(5)) // timeout
.next(finish());
}
...
```