Skip to content

Commit

Permalink
First move towards OpenAPI 3.1.0 compliance
Browse files Browse the repository at this point in the history
  • Loading branch information
lindkvis committed Mar 2, 2024
1 parent 4782c8a commit dfcac91
Show file tree
Hide file tree
Showing 25 changed files with 1,581 additions and 654 deletions.
51 changes: 44 additions & 7 deletions Core/cafJsonSerializer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,25 +114,46 @@ void JsonSerializer::writeObjectToJson( const ObjectHandle* object, nlohmann::js

if ( this->serializationType() == Serializer::SerializationType::SCHEMA )
{
jsonObject["type"] = "object";
std::set<std::string> parentalFields;
std::set<std::string> parentalMethods;

auto parentClassKeyword = object->parentClassKeyword();

auto parentClassInstance = DefaultObjectFactory::instance()->create( parentClassKeyword );
if ( parentClassInstance )
{
for ( auto field : parentClassInstance->fields() )
{
parentalFields.insert( field->keyword() );
}
for ( auto method : parentClassInstance->methods() )
{
parentalMethods.insert( method->keyword() );
}
}

auto jsonClass = nlohmann::json::object();

jsonClass["type"] = "object";
auto jsonProperties = nlohmann::json::object();

jsonProperties["keyword"] = { { "type", "string" } };
jsonProperties["uuid"] = { { "type", "string" } };
jsonObject["$schema"] = "https://json-schema.org/draft/2020-12/schema";
jsonObject["$id"] = "/schemas/" + object->classKeyword();
jsonObject["title"] = object->classKeyword();
jsonObject["$id"] = "/components/object_schemas/" + object->classKeyword();
// jsonObject["title"] = object->classKeyword();

if ( !object->classDocumentation().empty() )
{
jsonObject["description"] = object->classDocumentation();
jsonClass["description"] = object->classDocumentation();
}

for ( auto field : object->fields() )
{
if ( this->fieldSelector() && !this->fieldSelector()( field ) ) continue;

auto keyword = field->keyword();
if ( parentalFields.contains( keyword ) ) continue;

const FieldJsonCapability* ioCapability = field->capability<FieldJsonCapability>();
if ( ioCapability && keyword != "uuid" && ( field->isReadable() || field->isWritable() ) )
Expand All @@ -146,7 +167,10 @@ void JsonSerializer::writeObjectToJson( const ObjectHandle* object, nlohmann::js
auto methods = nlohmann::json::object();
for ( auto method : object->methods() )
{
methods[method->keyword()] = method->jsonSchema();
auto keyword = method->keyword();
if ( parentalMethods.contains( keyword ) ) continue;

methods[keyword] = method->jsonSchema();
}
if ( !methods.empty() )
{
Expand All @@ -156,8 +180,21 @@ void JsonSerializer::writeObjectToJson( const ObjectHandle* object, nlohmann::js
jsonProperties["methods"] = methodsObject;
}

jsonObject["properties"] = jsonProperties;
jsonObject["required"] = { "keyword" };
jsonClass["properties"] = jsonProperties;
jsonClass["required"] = { "keyword", "uuid" };

if ( parentClassInstance )
{
auto jsonAllOf = nlohmann::json::array();
jsonAllOf.push_back( { { "$ref", "#/components/object_schemas/" + parentClassKeyword } } );

jsonAllOf.push_back( jsonClass );
jsonObject["allOf"] = jsonAllOf;
}
else
{
jsonObject = jsonClass;
}
}
else
{
Expand Down
4 changes: 2 additions & 2 deletions Core/cafObjectHandle.h
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ struct PortableDataType<DataType>
static nlohmann::json jsonType()
{
auto object = nlohmann::json::object();
object["$ref"] = std::string( "#/schemas/" ) + DataType::classKeywordStatic();
object["$ref"] = std::string( "#/components/object_schemas/" ) + DataType::classKeywordStatic();
return object;
}
};
Expand All @@ -257,7 +257,7 @@ struct PortableDataType<DataType>
static nlohmann::json jsonType()
{
auto object = nlohmann::json::object();
object["$ref"] = std::string( "#/schemas/" ) + DataType::element_type::classKeywordStatic();
object["$ref"] = std::string( "#/components/object_schemas/" ) + DataType::element_type::classKeywordStatic();
return object;
}
};
Expand Down
2 changes: 2 additions & 0 deletions Core/cafSerializer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ std::string Serializer::serializationTypeLabel( SerializationType type )
return "DATA_SKELETON";
case SerializationType::SCHEMA:
return "SCHEMA";
case SerializationType::PATH:
return "PATH";
}
CAFFA_ASSERT( false );
return "";
Expand Down
1 change: 1 addition & 0 deletions Core/cafSerializer.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class Serializer
DATA_FULL,
DATA_SKELETON,
SCHEMA,
PATH
};

using FieldSelector = std::function<bool( const FieldHandle* )>;
Expand Down
12 changes: 8 additions & 4 deletions RestInterface/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ set(PUBLIC_HEADERS
cafRestClientApplication.h
cafRestServiceInterface.h
cafRestAppService.h
cafRestDocumentService.h
cafRestObjectService.h
cafRestSessionService.h
cafRestSchemaService.h
cafRestRequest.h)
cafRestRequest.h
cafRestOpenApiService.h
cafRestSessionService.h)

set(PROJECT_FILES
${PUBLIC_HEADERS}
Expand All @@ -25,10 +27,12 @@ set(PROJECT_FILES
cafRestClientApplication.cpp
cafRestServiceInterface.cpp
cafRestAppService.cpp
cafRestDocumentService.cpp
cafRestObjectService.cpp
cafRestSessionService.cpp
cafRestSchemaService.cpp
cafRestRequest.cpp)
cafRestRequest.cpp
cafRestOpenApiService.cpp
cafRestSessionService.cpp)

if(CAFFA_BUILD_SHARED)
message(STATUS "Building ${PROJECT_NAME} shared")
Expand Down
137 changes: 69 additions & 68 deletions RestInterface/cafRestAppService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,101 +26,102 @@

using namespace caffa::rpc;

RestAppService::ServiceResponse RestAppService::perform( http::verb verb,
const std::list<std::string>& path,
const nlohmann::json& arguments,
const nlohmann::json& metaData )
RestAppService::ServiceResponse RestAppService::perform( http::verb verb,
std::list<std::string> path,
const nlohmann::json& queryParams,
const nlohmann::json& body )
{
auto allCallbacks = callbacks();

if ( path.empty() )
if ( verb == http::verb::get )
{
auto jsonArray = nlohmann::json::array();
for ( auto [name, callback] : allCallbacks )
{
jsonArray.push_back( name );
}
return std::make_tuple( http::status::ok, jsonArray.dump(), nullptr );
return info();
}
auto name = path.front();

auto it = allCallbacks.find( name );
if ( it != allCallbacks.end() )
else if ( verb == http::verb::delete_ )
{
return it->second( verb, arguments, metaData );
return quit();
}
return std::make_tuple( http::status::not_found, "No such method", nullptr );
return std::make_tuple( http::status::bad_request, "Only GET or DELETE makes any sense with app requests", nullptr );
}

//--------------------------------------------------------------------------------------------------
/// The app service uses session uuids to decide if it accepts the request or not
//--------------------------------------------------------------------------------------------------
bool RestAppService::requiresAuthentication( const std::list<std::string>& path ) const
bool RestAppService::requiresAuthentication( http::verb verb, const std::list<std::string>& path ) const
{
return false;
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RestAppService::ServiceResponse
RestAppService::info( http::verb verb, const nlohmann::json& arguments, const nlohmann::json& metaData )
bool RestAppService::requiresSession( http::verb verb, const std::list<std::string>& path ) const
{
CAFFA_TRACE( "Received appInfo request" );
return verb == http::verb::delete_;
}

if ( RestServiceInterface::refuseDueToTimeLimiter() )
{
return std::make_tuple( http::status::too_many_requests, "Too many unauthenticated requests", nullptr );
}
std::map<std::string, nlohmann::json> RestAppService::servicePathEntries() const
{
auto infoRequest = nlohmann::json::object();

if ( verb != http::verb::get )
{
return std::make_tuple( http::status::bad_request, "Only GET makes any sense with app/info", nullptr );
}
auto app = RestServerApplication::instance();
auto appInfo = app->appInfo();
auto getContent = nlohmann::json::object();
getContent["application/json"] = { { "schema", { { "$ref", "#/components/app_schemas/AppInfo" } } } };
auto getResponses = nlohmann::json::object();

nlohmann::json json = appInfo;
return std::make_tuple( http::status::ok, json.dump(), nullptr );
auto errorContent = nlohmann::json::object();
errorContent["text/plain"] = { { "schema", { { "$ref", "#/components/error_schemas/PlainError" } } } };

getResponses[std::to_string( static_cast<unsigned>( http::status::ok ) )] = { { "description",
"Application Information" },
{ "content", getContent } };
getResponses[std::to_string( static_cast<unsigned>( http::status::too_many_requests ) )] =
{ { "description", "Too many Requests Error Message" }, { "content", errorContent } };

infoRequest["get"] = { { "summary", "Get Application Information" },
{ "operationId", "info" },
{ "responses", getResponses },
{ "tags", { "app" } } };

auto quitRequest = nlohmann::json::object();
auto quitResponses = nlohmann::json::object();

quitResponses[std::to_string( static_cast<unsigned>( http::status::accepted ) )] = {
{ "description", "Success" },
};

quitResponses[std::to_string( static_cast<unsigned>( http::status::forbidden ) )] = { { "description",
"Quit Error Message" },
{ "content", errorContent } };

quitRequest["delete"] = { { "summary", "Quit Application" },
{ "operationId", "quit" },
{ "responses", quitResponses },
{ "tags", { "app" } } };

return { { "/app/info", infoRequest }, { "/app/quit", quitRequest } };
}

std::map<std::string, nlohmann::json> RestAppService::serviceComponentEntries() const
{
auto appInfo = AppInfo::jsonSchema();

return { { "app_schemas", { { "AppInfo", appInfo } } } };
};

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
RestAppService::ServiceResponse
RestAppService::quit( http::verb verb, const nlohmann::json& arguments, const nlohmann::json& metaData )
RestAppService::ServiceResponse RestAppService::info()
{
std::string session_uuid = "";
if ( arguments.contains( "session_uuid" ) )
{
session_uuid = arguments["session_uuid"].get<std::string>();
}
else if ( metaData.contains( "session_uuid" ) )
{
session_uuid = metaData["session_uuid"].get<std::string>();
}
CAFFA_TRACE( "Received info request" );

auto session = RestServerApplication::instance()->getExistingSession( session_uuid );
if ( !session && RestServerApplication::instance()->requiresValidSession() )
{
return std::make_tuple( http::status::forbidden, "Session '" + session_uuid + "' is not valid", nullptr );
}
else if ( RestServerApplication::instance()->requiresValidSession() && session->isExpired() )
{
return std::make_tuple( http::status::forbidden, "Session '" + session_uuid + "' is expired", nullptr );
}
auto app = RestServerApplication::instance();
auto appInfo = app->appInfo();

return std::make_tuple( http::status::ok, "", []() { RestServerApplication::instance()->quit(); } );
nlohmann::json json = appInfo;
return std::make_tuple( http::status::ok, json.dump(), nullptr );
}

//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::map<std::string, RestAppService::ServiceCallback> RestAppService::callbacks() const
RestAppService::ServiceResponse RestAppService::quit()
{
std::map<std::string, ServiceCallback> callbacks = {
{ "info", &RestAppService::info },
{ "quit", &RestAppService::quit },
};
return callbacks;
CAFFA_DEBUG( "Received quit request" );

return std::make_tuple( http::status::accepted,
"Told to quit. It will happen soon",
[]() { RestServerApplication::instance()->quit(); } );
}
20 changes: 11 additions & 9 deletions RestInterface/cafRestAppService.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,21 @@ namespace caffa::rpc
class RestAppService : public RestServiceInterface
{
public:
ServiceResponse perform( http::verb verb,
const std::list<std::string>& path,
const nlohmann::json& arguments,
const nlohmann::json& metaData ) override;
ServiceResponse perform( http::verb verb,
std::list<std::string> path,
const nlohmann::json& queryParams,
const nlohmann::json& body ) override;

bool requiresAuthentication( const std::list<std::string>& path ) const override;
bool requiresAuthentication( http::verb verb, const std::list<std::string>& path ) const override;
bool requiresSession( http::verb verb, const std::list<std::string>& path ) const override;

std::map<std::string, nlohmann::json> servicePathEntries() const override;
std::map<std::string, nlohmann::json> serviceComponentEntries() const override;

private:
using ServiceCallback = std::function<ServiceResponse( http::verb verb, const nlohmann::json&, const nlohmann::json& )>;

std::map<std::string, ServiceCallback> callbacks() const;

static ServiceResponse info( http::verb verb, const nlohmann::json& arguments, const nlohmann::json& metaData );
static ServiceResponse quit( http::verb verb, const nlohmann::json& arguments, const nlohmann::json& metaData );
static ServiceResponse info();
static ServiceResponse quit();
};
} // namespace caffa::rpc
Loading

0 comments on commit dfcac91

Please sign in to comment.