Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/openassetio-core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ target_sources(
src/hostApi/Manager.cpp
src/hostApi/ManagerFactory.cpp
src/hostApi/ManagerImplementationFactoryInterface.cpp
src/hostApi/EntityReferencePager.cpp
src/log/ConsoleLogger.cpp
src/log/LoggerInterface.cpp
src/log/SeverityFilter.cpp
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2013-2023 The Foundry Visionmongers Ltd

#pragma once

#include <memory>

#include <openassetio/managerApi/EntityReferencePagerInterface.hpp>
#include <openassetio/typedefs.hpp>

OPENASSETIO_FWD_DECLARE(managerApi, HostSession)

namespace openassetio {
inline namespace OPENASSETIO_CORE_ABI_VERSION {
namespace hostApi {

OPENASSETIO_DECLARE_PTR(EntityReferencePager)

/**
* The EntityReferencePager is the Host facing representation of a
* @fqref{managerApi.PagerInterface} implementation.
* The Manager class shouldn't be directly constructed by the host.
* The EntityReferencePager allows for the retrieval and traversal of large datasets
* in a paginated manner.
*
* Due to the variance of backends, construction, `hasNext`, `get` and
* `next` may all reasonably need to perform non trivial, networked
* operations, and thus performance characteristics should not be
* assumed.singleEntityReferencePager<Elem>
*
* This object falling out of scope is a signal to the manager that the
* connection query is finished. For this reason you should avoid
* keeping hold of this object for longer than necessary.
*/
class OPENASSETIO_CORE_EXPORT EntityReferencePager {
public:
OPENASSETIO_ALIAS_PTR(EntityReferencePager)
using Page = managerApi::EntityReferencePagerInterface::Page;

[[nodiscard]] static EntityReferencePager::Ptr make(
managerApi::EntityReferencePagerInterface::Ptr pagerInterface,
managerApi::HostSessionPtr hostSession);

/**
* EntityReferencePager cannot be copied, as each object represents a single
* paginated Query.
* Destruction of this object is tantamount to closing the query.
*/
EntityReferencePager(const EntityReferencePager&) = delete;
EntityReferencePager& operator=(const EntityReferencePager&) = delete;
EntityReferencePager(EntityReferencePager&&) noexcept = default;
EntityReferencePager& operator=(EntityReferencePager&&) noexcept = default;
~EntityReferencePager() = default;

/**
* Return whether or not there is more data accessible by advancing
* the page.
*/
bool hasNext();

/**
* Return the current page data.
*/
Page get();

/**
* Advance the page.
*/
void next();

private:
EntityReferencePager(managerApi::EntityReferencePagerInterface::Ptr pagerInterface,
managerApi::HostSessionPtr hostSession);

managerApi::EntityReferencePagerInterface::Ptr pagerInterface_;
managerApi::HostSessionPtr hostSession_;
};
} // namespace hostApi
} // namespace OPENASSETIO_CORE_ABI_VERSION
} // namespace openassetio
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2013-2023 The Foundry Visionmongers Ltd

#pragma once

#include <memory>
#include <vector>

#include <openassetio/export.h>
#include <openassetio/typedefs.hpp>

OPENASSETIO_FWD_DECLARE(managerApi, HostSession)
OPENASSETIO_FWD_DECLARE(Context)

namespace openassetio {
inline namespace OPENASSETIO_CORE_ABI_VERSION {
namespace managerApi {

OPENASSETIO_DECLARE_PTR(EntityReferencePagerInterface)

/**
* Deals with the retrieval of paginated data from the backend at the
* behest of the host.
*
* The manager is expected to extend this type, and store data
* data necessary to perform the paging operations on the extended
* object, utilizing caching when possible to reduce redundant
* queries.
*
* This object does not time out until the host gives up ownership. A
* manager should implement the destructor if they wish to close any
* open connections in response to this.
*
* To support as wide array of possible backends as possible,
* OpenAssetIO places no restraints on the behaviour of this type
* concerning performance, however, it is considered friendly to
* document the performance characteristics of your Pager implementation.
*/
class OPENASSETIO_CORE_EXPORT EntityReferencePagerInterface {
public:
OPENASSETIO_ALIAS_PTR(EntityReferencePagerInterface)
using Page = std::vector<EntityReference>;

// Explicitly disallow copying.
EntityReferencePagerInterface() = default;
explicit EntityReferencePagerInterface(const EntityReferencePagerInterface&) = delete;
EntityReferencePagerInterface& operator=(const EntityReferencePagerInterface&) = delete;
// Allow moving.
EntityReferencePagerInterface(EntityReferencePagerInterface&&) noexcept = default;
EntityReferencePagerInterface& operator=(EntityReferencePagerInterface&&) noexcept = default;

/**
* Manager should override destructor to be notified when query has
* finished.
*/
virtual ~EntityReferencePagerInterface() = default;

/**
* Returns whether or not there is more data accessible by advancing
* the page. The mechanism to acquire this information is variable,
* and left up to the specifics of the backend implementation.
*/
virtual bool hasNext(const HostSessionPtr&) = 0;

/**
* Return the current page data.
*/
virtual Page get(const HostSessionPtr&) = 0;

/**
* Advance the page.
*/
virtual void next(const HostSessionPtr&) = 0;
};
} // namespace managerApi
} // namespace OPENASSETIO_CORE_ABI_VERSION
} // namespace openassetio
33 changes: 33 additions & 0 deletions src/openassetio-core/src/hostApi/EntityReferencePager.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2013-2023 The Foundry Visionmongers Ltd

#include <openassetio/EntityReference.hpp>
#include <openassetio/hostApi/EntityReferencePager.hpp>

namespace openassetio {
inline namespace OPENASSETIO_CORE_ABI_VERSION {
namespace hostApi {

typename EntityReferencePager::Ptr EntityReferencePager::make(
managerApi::EntityReferencePagerInterfacePtr pagerInterface,
managerApi::HostSessionPtr hostSession) {
return std::shared_ptr<EntityReferencePager>(
new EntityReferencePager(std::move(pagerInterface), std::move(hostSession)));
}

EntityReferencePager::EntityReferencePager(
managerApi::EntityReferencePagerInterfacePtr pagerInterface,
managerApi::HostSessionPtr hostSession)
: pagerInterface_(std::move(pagerInterface)), hostSession_(std::move(hostSession)) {}

bool EntityReferencePager::hasNext() { return pagerInterface_->hasNext(hostSession_); }

typename EntityReferencePager::Page EntityReferencePager::get() {
return pagerInterface_->get(hostSession_);
}

void EntityReferencePager::next() { pagerInterface_->next(hostSession_); }

} // namespace hostApi
} // namespace OPENASSETIO_CORE_ABI_VERSION
} // namespace openassetio
1 change: 1 addition & 0 deletions src/openassetio-core/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ target_sources(openassetio-core-cpp-test-exe
ContextTest.cpp
TraitsDataTest.cpp
hostApi/ManagerTest.cpp
hostApi/PagerTest.cpp
managerApi/HostTest.cpp
managerApi/HostSessionTest.cpp
managerApi/ManagerStateBaseTest.cpp
Expand Down
134 changes: 134 additions & 0 deletions src/openassetio-core/tests/hostApi/PagerTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2022 The Foundry Visionmongers Ltd
#include <type_traits>
#include <variant>

#include <openassetio/export.h>

#include <catch2/catch.hpp>
#include <catch2/trompeloeil.hpp>

#include <openassetio/hostApi/HostInterface.hpp>
#include <openassetio/log/LoggerInterface.hpp>
#include <openassetio/managerApi/Host.hpp>
#include <openassetio/managerApi/HostSession.hpp>
#include <openassetio/managerApi/ManagerInterface.hpp>

#include <openassetio/hostApi/EntityReferencePager.hpp>
#include <openassetio/managerApi/EntityReferencePagerInterface.hpp>
namespace openassetio {
inline namespace OPENASSETIO_CORE_ABI_VERSION {
namespace {
/**
* Mock implementation of a ManagerInterface.
*
* Used as constructor parameter to the Manager under test.
*/
struct MockManagerInterface : trompeloeil::mock_interface<managerApi::ManagerInterface> {
IMPLEMENT_CONST_MOCK0(identifier);
IMPLEMENT_CONST_MOCK0(displayName);
IMPLEMENT_CONST_MOCK0(info);
IMPLEMENT_MOCK2(initialize);
IMPLEMENT_CONST_MOCK3(managementPolicy);
IMPLEMENT_CONST_MOCK2(isEntityReferenceString);
IMPLEMENT_MOCK6(resolve);
IMPLEMENT_MOCK6(preflight);
IMPLEMENT_MOCK6(register_); // NOLINT(readability-identifier-naming)
};
/**
* Mock implementation of a HostInterface.
*
* Used as constructor parameter to Host classes required as part of these tests
*/
struct MockHostInterface : trompeloeil::mock_interface<hostApi::HostInterface> {
IMPLEMENT_CONST_MOCK0(identifier);
IMPLEMENT_CONST_MOCK0(displayName);
IMPLEMENT_CONST_MOCK0(info);
};
/**
* Mock implementation of a LoggerInterface
*
* Used as constructor parameter to Host classes required as part of these tests
*/
struct MockLoggerInterface : trompeloeil::mock_interface<log::LoggerInterface> {
IMPLEMENT_MOCK2(log);
};

/**
* Fixture providing a Manager instance injected with mock dependencies.
*/
struct ManagerFixture {
const std::shared_ptr<managerApi::ManagerInterface> managerInterface =
std::make_shared<openassetio::MockManagerInterface>();

// For convenience, to avoid casting all the time in tests.
MockManagerInterface& mockManagerInterface =
static_cast<openassetio::MockManagerInterface&>(*managerInterface);

// Create a HostSession with our mock HostInterface
const managerApi::HostSessionPtr hostSession = managerApi::HostSession::make(
managerApi::Host::make(std::make_shared<openassetio::MockHostInterface>()),
std::make_shared<openassetio::MockLoggerInterface>());
};

} // namespace
} // namespace OPENASSETIO_CORE_ABI_VERSION
} // namespace openassetio

namespace {
struct MockEntityReferencePagerInterface
: trompeloeil::mock_interface<openassetio::managerApi::EntityReferencePagerInterface> {
IMPLEMENT_MOCK1(hasNext);
IMPLEMENT_MOCK1(get);
IMPLEMENT_MOCK1(next);
};

} // namespace

SCENARIO("Using an EntityReferencePager") {
using trompeloeil::_;

GIVEN("a configured EntityReferencePager") {
const openassetio::trait::TraitSet traits = {"fakeTrait", "secondFakeTrait"};
const openassetio::ManagerFixture fixture;

std::shared_ptr<MockEntityReferencePagerInterface> mockEntityReferencePagerInterface =
std::make_shared<MockEntityReferencePagerInterface>();

openassetio::hostApi::EntityReferencePager::Ptr pager =
openassetio::hostApi::EntityReferencePager::make(mockEntityReferencePagerInterface,
fixture.hostSession);

AND_GIVEN("pagerInterface hasMore expects to be called and returns false") {
REQUIRE_CALL(*mockEntityReferencePagerInterface, hasNext(fixture.hostSession)).RETURN(false);
WHEN("pager hasMore is called") {
bool hasMoreReturn = pager->hasNext();
THEN("the value is false") { CHECK(hasMoreReturn == false); }
}
}
AND_GIVEN("pagerInterface hasMore expects to be called and returns true") {
REQUIRE_CALL(*mockEntityReferencePagerInterface, hasNext(fixture.hostSession)).RETURN(true);
WHEN("pager hasMore is called") {
bool hasMoreReturn = pager->hasNext();
THEN("the value is true") { CHECK(hasMoreReturn == true); }
}
}
AND_GIVEN("pagerInterface next expects to be called") {
REQUIRE_CALL(*mockEntityReferencePagerInterface, next(fixture.hostSession));
WHEN("pager next is called") {
pager->next();
THEN("pagerInterface next was called") {}
}
}
AND_GIVEN("pagerInterface get expects to be called and returns false") {
const std::vector<openassetio::EntityReference> testEntRefs = {
openassetio::EntityReference("One!"), openassetio::EntityReference("Two!")};
REQUIRE_CALL(*mockEntityReferencePagerInterface, get(fixture.hostSession))
.RETURN(testEntRefs);
WHEN("pager get is called") {
const auto getReturn = pager->get();
THEN("the value is as expected") { CHECK(getReturn == testEntRefs); }
}
}
}
}