Skip to content

Commit

Permalink
Change C++ application entry point to remove dependency on frontends (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
eddyashton authored Feb 21, 2022
1 parent d7e1277 commit 5b40ba7
Show file tree
Hide file tree
Showing 19 changed files with 126 additions and 167 deletions.
2 changes: 1 addition & 1 deletion .daily_canary
Original file line number Diff line number Diff line change
@@ -1 +1 @@
No no he's not dead, he's, he's restin'!
No no he's not dead, he's, he's resting!
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## Unreleased

### Changed

- The entry point for creation of C++ apps is now `make_user_endpoints()`. The old entry point `get_rpc_handler()` has been removed.

## [2.0.0-rc1]

### Added
Expand Down
4 changes: 2 additions & 2 deletions doc/build_apps/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ Developer API

A CCF application is composed of the following:

- The :ref:`Application Entry Point <build_apps/api:Application Entry Point>` which registers the application in CCF.
- The :ref:`Application Entry Point <build_apps/api:Application Entry Point>` which creates the application in CCF.
- A collection of :cpp:class:`endpoints <ccf::endpoints::Endpoint>` handling HTTP requests and grouped in a single :cpp:class:`registry <ccf::endpoints::EndpointRegistry>`. An :cpp:class:`endpoint <ccf::endpoints::Endpoint>` reads and writes to the key-value store via the :ref:`Key-Value Store API <build_apps/kv/api:Key-Value Store API>`.
- An optional set of :ref:`JavaScript FFI Plugins <build_apps/api:JavaScript FFI Plugins>` that can be registered to extend the built-in JavaScript API surface.

Application Entry Point
-----------------------

.. doxygenfunction:: ccfapp::get_rpc_handler
.. doxygenfunction:: ccfapp::make_user_endpoints
:project: CCF


Expand Down
15 changes: 8 additions & 7 deletions doc/build_apps/logging_cpp.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ The Logging application simply has:

.. note::

:cpp:class:`kv::Store` tables are essentially the only interface between CCF
and the application, and the sole mechanism for it to have state.
:cpp:class:`kv::Store` tables are the only interface between CCF and the replicated application, and the sole mechanism for it to have distributed state.

The Logging application keeps its state in a pair of tables, one containing private encrypted logs and the other containing public unencrypted logs. Their type is defined as:

Expand All @@ -44,10 +43,10 @@ The Logging application simply has:
:lines: 1
:dedent:

RPC Handler
-----------
Application Endpoints
---------------------

The type returned by :cpp:func:`ccfapp::get_rpc_handler()` should subclass :cpp:class:`ccf::RpcFrontend`, passing the base constructor a reference to an implementation of :cpp:class:`ccf::EndpointRegistry`:
The implementation of :cpp:func:`ccfapp::make_user_endpoints()` should return a subclass of :cpp:class:`ccf::endpoints::EndpointRegistry`, containing the endpoints that constitute the app.

.. literalinclude:: ../../samples/apps/logging/logging.cpp
:language: cpp
Expand Down Expand Up @@ -75,7 +74,9 @@ Each function is installed as the handler for a specific HTTP resource, defined

This example installs at ``"log/private", HTTP_POST``, so will be invoked for HTTP requests beginning :http:POST:`/app/log/private`.

The return value from ``make_endpoint`` is an ``Endpoint&`` object which can be used to alter how the handler is executed. For example, the handler for :http:POST:`/app/log/private` shown above sets a `schema` declaring the types of its request and response bodies. These will be used in calls to the :http:GET:`/app/api` endpoint to populate the relevant parts of the OpenAPI document. There are other endpoints installed for the URI path ``/app/log/private`` with different verbs, to handle :http:GET:`GET </app/log/private>` and :http:DELETE:`DELETE </app/log/private>` requests. Any other verbs, without an installed endpoint, will not be accepted - the framework will return a ``405 Method Not Allowed`` response.
The return value from ``make_endpoint`` is an ``Endpoint&`` object which can be used to alter how the handler is executed. For example, the handler for :http:POST:`/app/log/private` shown above sets a `schema` declaring the types of its request and response bodies. These will be used in calls to the :http:GET:`/app/api` endpoint to populate the relevant parts of the OpenAPI document. That OpenAPI document in turn is used to generate the entries in this documentation describing :http:POST:`/app/log/private`.

There are other endpoints installed for the URI path ``/app/log/private`` with different verbs, to handle :http:GET:`GET </app/log/private>` and :http:DELETE:`DELETE </app/log/private>` requests. Requests with those verbs will be executed by the appropriate handler. Any other verbs, without an installed endpoint, will not be accepted - the framework will return a ``405 Method Not Allowed`` response.

To process the raw body directly, a handler should use the general lambda signature which takes a single ``EndpointContext&`` parameter. Examples of this are also included in the logging sample app. For instance the ``log_record_text`` handler takes a raw string as the request body:

Expand Down Expand Up @@ -162,7 +163,7 @@ The final piece is the definition of the endpoint itself, which uses an instance
Default Endpoints
~~~~~~~~~~~~~~~~~

The logging app sample exposes several built-in endpoints which are provided by the framework for convenience, such as ``/app/tx``, ``/app/commit``, and ``/app/user_id``. It is also possible to write an app which does not expose these endpoints, either to build a minimal user-facing API or to re-wrap this common functionality in your own format or authentication. A sample of this is provided in ``samples/apps/nobuiltins``. Whereas the logging app declares a registry inheriting from :cpp:class:`ccf::CommonEndpointRegistry`, this app inherits from :cpp:class:`ccf::BaseEndpointRegistry` which does not install any default endpoints:
The logging app sample exposes several built-in endpoints which are provided by the framework for convenience, such as :http:GET:`/app/tx`, :http:GET:`/app/commit`, and :http:GET:`/app/receipt`. It is also possible to write an app which does not expose these endpoints, either to build a minimal user-facing API or to re-wrap this common functionality in your own format or authentication. A sample of this is provided in ``samples/apps/nobuiltins``. Whereas the logging app declares a registry inheriting from :cpp:class:`ccf::CommonEndpointRegistry`, this app inherits from :cpp:class:`ccf::BaseEndpointRegistry` which does not install any default endpoints:

.. literalinclude:: ../../samples/apps/nobuiltins/nobuiltins.cpp
:language: cpp
Expand Down
41 changes: 31 additions & 10 deletions include/ccf/app_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,57 @@
// Licensed under the Apache 2.0 License.
#pragma once

#include "ccf/ccf_deprecated.h"
#include "ccf/common_endpoint_registry.h"
#include "ccf/js_plugin.h"
#include "ccf/node_context.h"

#include <memory>
#include <vector>

// Forward declarations, can be removed with deprecation
namespace ccf
{
// Forward declarations
class RpcFrontend;
}

struct NetworkTables;
namespace kv
{
class Store;
}

namespace ccfapp
{
// Forward declaration
struct AbstractNodeContext;
CCF_DEPRECATED("Replace with make_user_endpoints")
std::shared_ptr<ccf::RpcFrontend> get_rpc_handler(
kv::Store& store, AbstractNodeContext& context);
}

namespace ccf
{
class UserEndpointRegistry : public CommonEndpointRegistry
{
public:
UserEndpointRegistry(ccfapp::AbstractNodeContext& context) :
CommonEndpointRegistry(get_actor_prefix(ActorsType::users), context)
{}
};
}

namespace ccfapp
{
// SNIPPET_START: app_interface
/** To be implemented by the application to be registered by CCF.
/** To be implemented by the application. Creates a collection of endpoints
* which will be exposed to callers under /app.
*
* @param network Access to the network's replicated tables
* @param context Access to node and host services
*
* @return Shared pointer to the application handler instance
* @return Unique pointer to the endpoint registry instance
*/
std::shared_ptr<ccf::RpcFrontend> get_rpc_handler(
ccf::NetworkTables& network, AbstractNodeContext& context);
std::unique_ptr<ccf::endpoints::EndpointRegistry> make_user_endpoints(
ccfapp::AbstractNodeContext& context);

/** To be implemented by the application to be registered by CCF.
/** To be implemented by the application.
*
* @return Vector of JavaScript FFI plugins
*/
Expand Down
32 changes: 0 additions & 32 deletions include/ccf/user_frontend.h

This file was deleted.

37 changes: 10 additions & 27 deletions samples/apps/logging/logging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
#include "ccf/historical_queries_adapter.h"
#include "ccf/http_query.h"
#include "ccf/indexing/strategies/seqnos_by_key_bucketed.h"
#include "ccf/user_frontend.h"
#include "ccf/version.h"
#include "node/tx_receipt.h"

Expand Down Expand Up @@ -166,6 +165,13 @@ namespace loggingapp
get_public_params_schema(nlohmann::json::parse(j_get_public_in)),
get_public_result_schema(nlohmann::json::parse(j_get_public_out))
{
openapi_info.title = "CCF Sample Logging App";
openapi_info.description =
"This CCF sample app implements a simple logging application, securely "
"recording messages at client-specified IDs. It demonstrates most of "
"the features available to CCF apps.";
openapi_info.document_version = "1.7.0";

index_per_public_key = std::make_shared<RecordsIndexingStrategy>(
PUBLIC_RECORDS, context.get_lfs_access(), 10000, 20);
context.get_indexing_strategies().install_strategy(index_per_public_key);
Expand Down Expand Up @@ -1482,38 +1488,15 @@ namespace loggingapp
ccf::UserEndpointRegistry::tick(elapsed, tx_count);
}
};

class Logger : public ccf::RpcFrontend
{
private:
LoggerHandlers logger_handlers;

public:
Logger(ccf::NetworkTables& network, ccfapp::AbstractNodeContext& context) :
ccf::RpcFrontend(*network.tables, logger_handlers),
logger_handlers(context)
{}

void open(std::optional<crypto::Pem*> identity = std::nullopt) override
{
ccf::RpcFrontend::open(identity);
logger_handlers.openapi_info.title = "CCF Sample Logging App";
logger_handlers.openapi_info.description =
"This CCF sample app implements a simple logging application, securely "
"recording messages at client-specified IDs. It demonstrates most of "
"the features available to CCF apps.";
logger_handlers.openapi_info.document_version = "1.7.0";
}
};
}

namespace ccfapp
{
// SNIPPET_START: app_interface
std::shared_ptr<ccf::RpcFrontend> get_rpc_handler(
ccf::NetworkTables& nwt, ccfapp::AbstractNodeContext& context)
std::unique_ptr<ccf::endpoints::EndpointRegistry> make_user_endpoints(
ccfapp::AbstractNodeContext& context)
{
return make_shared<loggingapp::Logger>(nwt, context);
return std::make_unique<loggingapp::LoggerHandlers>(context);
}
// SNIPPET_END: app_interface
}
19 changes: 3 additions & 16 deletions samples/apps/nobuiltins/nobuiltins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -329,26 +329,13 @@ namespace nobuiltins
.install();
}
};

class NoBuiltinsFrontend : public ccf::RpcFrontend
{
private:
NoBuiltinsRegistry nbr;

public:
NoBuiltinsFrontend(
ccf::NetworkTables& network, ccfapp::AbstractNodeContext& context) :
ccf::RpcFrontend(*network.tables, nbr),
nbr(context)
{}
};
}

namespace ccfapp
{
std::shared_ptr<ccf::RpcFrontend> get_rpc_handler(
ccf::NetworkTables& nwt, ccfapp::AbstractNodeContext& context)
std::unique_ptr<ccf::endpoints::EndpointRegistry> make_user_endpoints(
ccfapp::AbstractNodeContext& context)
{
return std::make_shared<nobuiltins::NoBuiltinsFrontend>(nwt, context);
return std::make_unique<nobuiltins::NoBuiltinsRegistry>(context);
}
}
6 changes: 3 additions & 3 deletions src/apps/js_generic/js_generic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@

namespace ccfapp
{
std::shared_ptr<ccf::RpcFrontend> get_rpc_handler(
ccf::NetworkTables& network, ccfapp::AbstractNodeContext& context)
std::unique_ptr<ccf::endpoints::EndpointRegistry> make_user_endpoints(
ccfapp::AbstractNodeContext& context)
{
return get_rpc_handler_impl(network, context);
return make_user_endpoints_impl(context);
}

std::vector<ccf::js::FFIPlugin> get_js_plugins()
Expand Down
24 changes: 5 additions & 19 deletions src/apps/js_generic/js_generic_base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
#include "ccf/crypto/key_wrap.h"
#include "ccf/crypto/rsa_key_pair.h"
#include "ccf/historical_queries_adapter.h"
#include "ccf/user_frontend.h"
#include "ccf/version.h"
#include "js/wrap.h"
#include "kv/untyped_map.h"
Expand All @@ -32,7 +31,6 @@ namespace ccfapp
struct JSDynamicEndpoint : public ccf::endpoints::EndpointDefinition
{};

NetworkTables& network;
ccfapp::AbstractNodeContext& context;
metrics::Tracker metrics_tracker;

Expand Down Expand Up @@ -471,9 +469,8 @@ namespace ccfapp
}

public:
JSHandlers(NetworkTables& network, AbstractNodeContext& context) :
JSHandlers(AbstractNodeContext& context) :
UserEndpointRegistry(context),
network(network),
context(context)
{
metrics_tracker.install_endpoint(*this);
Expand Down Expand Up @@ -681,21 +678,10 @@ namespace ccfapp

#pragma clang diagnostic pop

class JS : public ccf::RpcFrontend
std::unique_ptr<ccf::endpoints::EndpointRegistry> make_user_endpoints_impl(
ccfapp::AbstractNodeContext& context)
{
private:
JSHandlers js_handlers;

public:
JS(NetworkTables& network, ccfapp::AbstractNodeContext& context) :
ccf::RpcFrontend(*network.tables, js_handlers),
js_handlers(network, context)
{}
};

std::shared_ptr<ccf::RpcFrontend> get_rpc_handler_impl(
NetworkTables& network, ccfapp::AbstractNodeContext& context)
{
return make_shared<JS>(network, context);
return std::make_unique<JSHandlers>(context);
}

} // namespace ccfapp
4 changes: 2 additions & 2 deletions src/apps/js_generic/js_generic_base.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@

namespace ccfapp
{
std::shared_ptr<ccf::RpcFrontend> get_rpc_handler_impl(
ccf::NetworkTables& network, ccfapp::AbstractNodeContext& context);
std::unique_ptr<ccf::endpoints::EndpointRegistry> make_user_endpoints_impl(
ccfapp::AbstractNodeContext& context);
}
6 changes: 3 additions & 3 deletions src/apps/js_v8/js_v8.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@

namespace ccfapp
{
std::shared_ptr<ccf::RpcFrontend> get_rpc_handler(
ccf::NetworkTables& network, ccfapp::AbstractNodeContext& context)
std::unique_ptr<ccf::endpoints::EndpointRegistry> make_user_endpoints(
ccfapp::AbstractNodeContext& context)
{
return get_rpc_handler_impl(network, context);
return make_user_endpoints_impl(context);
}

std::vector<ccf::js::FFIPlugin> get_js_plugins()
Expand Down
Loading

0 comments on commit 5b40ba7

Please sign in to comment.