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
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:
341
third_party/oatpp/changelog/1.1.0.md
vendored
Normal file
341
third_party/oatpp/changelog/1.1.0.md
vendored
Normal 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
234
third_party/oatpp/changelog/1.2.0.md
vendored
Normal 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
222
third_party/oatpp/changelog/1.2.5.md
vendored
Normal 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
526
third_party/oatpp/changelog/1.3.0.md
vendored
Normal 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
49
third_party/oatpp/changelog/1.4.0.md
vendored
Normal 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());
|
||||
}
|
||||
...
|
||||
```
|
Reference in New Issue
Block a user