2020-05-10 03:54:59 +08:00
|
|
|
# Oat++ 1.1.0
|
|
|
|
|
|
|
|
Oat++ `1.1.0` is introducing breaking changes.
|
|
|
|
Please read carefully to prepare for migration.
|
|
|
|
|
2020-05-10 04:15:41 +08:00
|
|
|
Feel free to ask questions - [Chat on Gitter](https://gitter.im/oatpp-framework/Lobby)
|
|
|
|
|
2020-05-10 04:02:24 +08:00
|
|
|
Contents:
|
2020-05-23 08:53:08 +08:00
|
|
|
- [No More ObjectWrapper](#no-more-objectwrapper)
|
2020-05-10 04:04:32 +08:00
|
|
|
- [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)
|
2020-05-14 06:12:00 +08:00
|
|
|
- [DTO - Hashcode & Equals](#dto-hashcode-and-equals)
|
2020-05-16 07:20:34 +08:00
|
|
|
- [DTO - Fields Annotation](#dto-fields-annotation)
|
2020-05-10 04:02:24 +08:00
|
|
|
|
2020-05-23 08:53:08 +08:00
|
|
|
## No More ObjectWrapper
|
2020-05-10 03:54:59 +08:00
|
|
|
|
2020-05-23 08:53:08 +08:00
|
|
|
`ObjectWrapper` is a base-class for oatpp core types (`oatpp/core/Types.hpp`).
|
|
|
|
The explicit `::ObjectWrapper` qualifier is removed.
|
2020-05-10 03:54:59 +08:00
|
|
|
|
2020-05-23 08:53:08 +08:00
|
|
|
### DTO:
|
2020-05-10 03:54:59 +08:00
|
|
|
|
|
|
|
```cpp
|
2020-05-23 08:53:08 +08:00
|
|
|
class MyDto : oatpp::DTO { // <--- Notice the 'oatpp::DTO' now. NOT the 'oatpp::Object'
|
2020-05-10 03:54:59 +08:00
|
|
|
|
2020-05-23 08:53:08 +08:00
|
|
|
DTO_INIT(MyDto, DTO)
|
2020-05-10 03:54:59 +08:00
|
|
|
|
2020-05-23 08:53:08 +08:00
|
|
|
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.
|
2020-05-10 03:54:59 +08:00
|
|
|
DTO_FIELD(Fields<List<String>>, mapOfLists);
|
|
|
|
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2020-05-23 08:53:08 +08:00
|
|
|
### ApiController:
|
2020-05-10 03:54:59 +08:00
|
|
|
|
|
|
|
```cpp
|
|
|
|
ENDPOINT("POST", "body-dto", postWithBody,
|
2020-05-23 08:53:08 +08:00
|
|
|
BODY_DTO(Object<MyDto>, body)) {
|
2020-05-10 03:54:59 +08:00
|
|
|
...
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2020-05-23 08:53:08 +08:00
|
|
|
### ApiClient:
|
2020-05-19 09:41:47 +08:00
|
|
|
|
2020-05-23 08:53:08 +08:00
|
|
|
```cpp
|
|
|
|
API_CALL("POST", "body-dto", postWithBody, BODY_DTO(Object<MyDto>, body))
|
|
|
|
```
|
2020-05-19 09:41:47 +08:00
|
|
|
|
2020-05-23 08:53:08 +08:00
|
|
|
### In Other Code-Blocks
|
2020-05-19 09:41:47 +08:00
|
|
|
|
|
|
|
```cpp
|
2020-05-23 08:53:08 +08:00
|
|
|
oatpp::Object<MyDto> myDto;
|
|
|
|
oatpp::List<oatpp::Object<MyDto>> listOfDtos;
|
2020-05-19 09:41:47 +08:00
|
|
|
|
2020-05-23 08:53:08 +08:00
|
|
|
...
|
2020-05-19 09:41:47 +08:00
|
|
|
|
2020-05-23 08:53:08 +08:00
|
|
|
auto myDto = objectMapper->readFromString<Object<MyDto>>(json);
|
|
|
|
auto listOfDtos = objectMapper->readFromString<List<Object<MyDto>>>(json);
|
2020-05-19 09:41:47 +08:00
|
|
|
```
|
|
|
|
|
2020-05-10 04:02:24 +08:00
|
|
|
## 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
|
2020-05-23 08:53:08 +08:00
|
|
|
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({});
|
2020-05-10 04:02:24 +08:00
|
|
|
|
|
|
|
oatpp::Vector<oatpp::String> vector = {"a", "b", "c"};
|
|
|
|
oatpp::List<oatpp::String> list = {"a", "b", "c"};
|
2020-05-10 13:18:20 +08:00
|
|
|
oatpp::UnorderedSet<oatpp::String> set = {"a", "b", "c"};
|
2020-05-10 04:02:24 +08:00
|
|
|
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");
|
|
|
|
|
2020-05-10 13:18:20 +08:00
|
|
|
bool contains = set["b"]; // <--- Complexity = O(1);
|
|
|
|
set->insert("z")
|
|
|
|
|
2020-05-17 08:25:08 +08:00
|
|
|
auto value = pairList.getValueByKey("k1"); // <--- Complexity = O(n); // note '.' here, not '->' !!!
|
2020-05-10 04:02:24 +08:00
|
|
|
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) {
|
|
|
|
...
|
|
|
|
}
|
|
|
|
|
2020-05-10 13:18:20 +08:00
|
|
|
for(auto& item : *set) {
|
|
|
|
...
|
|
|
|
}
|
|
|
|
|
2020-05-10 04:02:24 +08:00
|
|
|
for(auto& pair : *pairList) {
|
|
|
|
...
|
|
|
|
}
|
|
|
|
|
|
|
|
for(auto& pair : *hashMap) {
|
|
|
|
...
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
## Type oatpp::Any
|
2020-05-10 03:54:59 +08:00
|
|
|
|
|
|
|
The new Type Introduced - `oatpp::Any`.
|
|
|
|
|
|
|
|
Now it's possible to do like this:
|
|
|
|
|
|
|
|
```cpp
|
2020-05-26 01:39:19 +08:00
|
|
|
class MyDto : public oatpp::DTO {
|
2020-05-10 03:54:59 +08:00
|
|
|
|
2020-05-26 01:39:19 +08:00
|
|
|
DTO_INIT(MyDto, DTO)
|
2020-05-10 03:54:59 +08:00
|
|
|
|
|
|
|
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")
|
|
|
|
);
|
|
|
|
|
2020-05-26 01:39:19 +08:00
|
|
|
class MyDto : public oatpp::DTO {
|
2020-05-10 03:54:59 +08:00
|
|
|
|
2020-05-26 01:39:19 +08:00
|
|
|
DTO_INIT(MyDto, DTO)
|
2020-05-10 03:54:59 +08:00
|
|
|
|
|
|
|
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) {
|
|
|
|
...
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
2020-05-14 06:12:00 +08:00
|
|
|
|
|
|
|
## 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
|
2020-05-26 01:39:19 +08:00
|
|
|
class User : public oatpp::DTO {
|
2020-05-14 06:12:00 +08:00
|
|
|
|
2020-05-26 01:39:19 +08:00
|
|
|
DTO_INIT(User, DTO)
|
2020-05-14 06:12:00 +08:00
|
|
|
|
|
|
|
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.
|
2020-05-16 07:20:34 +08:00
|
|
|
|
|
|
|
## DTO Fields Annotation
|
|
|
|
|
|
|
|
Now it's possible to add a description for DTO fields, which will be automatically
|
|
|
|
displayed in swagger-UI.
|
|
|
|
|
|
|
|
```cpp
|
2020-05-26 01:39:19 +08:00
|
|
|
class MyDto : public oatpp::DTO {
|
2020-05-16 07:20:34 +08:00
|
|
|
|
2020-05-26 01:39:19 +08:00
|
|
|
DTO_INIT(MyDto, DTO)
|
2020-05-16 07:20:34 +08:00
|
|
|
|
|
|
|
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).*
|