diff options
Diffstat (limited to 'framework/actions')
-rw-r--r-- | framework/actions/CMakeLists.txt | 4 | ||||
-rw-r--r-- | framework/actions/actionbroker.cpp | 5 | ||||
-rw-r--r-- | framework/actions/actionbroker.h | 1 | ||||
-rw-r--r-- | framework/actions/actionhandler.cpp | 17 | ||||
-rw-r--r-- | framework/actions/actionhandler.h | 55 | ||||
-rw-r--r-- | framework/actions/context.cpp | 34 | ||||
-rw-r--r-- | framework/actions/context.h | 27 | ||||
-rw-r--r-- | framework/actions/tests/CMakeLists.txt | 6 | ||||
-rw-r--r-- | framework/actions/tests/actiontest.cpp | 102 |
9 files changed, 238 insertions, 13 deletions
diff --git a/framework/actions/CMakeLists.txt b/framework/actions/CMakeLists.txt index 9cf0acd1..9fc43b9b 100644 --- a/framework/actions/CMakeLists.txt +++ b/framework/actions/CMakeLists.txt | |||
@@ -9,8 +9,10 @@ set(SRCS | |||
9 | 9 | ||
10 | add_library(actionplugin SHARED ${SRCS}) | 10 | add_library(actionplugin SHARED ${SRCS}) |
11 | 11 | ||
12 | target_link_libraries(actionplugin KF5::Async) | 12 | target_link_libraries(actionplugin KF5::Async sink) |
13 | qt5_use_modules(actionplugin Core Quick Qml) | 13 | qt5_use_modules(actionplugin Core Quick Qml) |
14 | 14 | ||
15 | install(TARGETS actionplugin DESTINATION ${QML_INSTALL_DIR}/org/kube/framework/actions) | 15 | install(TARGETS actionplugin DESTINATION ${QML_INSTALL_DIR}/org/kube/framework/actions) |
16 | install(FILES qmldir DESTINATION ${QML_INSTALL_DIR}/org/kube/framework/actions) | 16 | install(FILES qmldir DESTINATION ${QML_INSTALL_DIR}/org/kube/framework/actions) |
17 | |||
18 | add_subdirectory(tests) | ||
diff --git a/framework/actions/actionbroker.cpp b/framework/actions/actionbroker.cpp index 17145440..f6bfdd8e 100644 --- a/framework/actions/actionbroker.cpp +++ b/framework/actions/actionbroker.cpp | |||
@@ -94,3 +94,8 @@ void ActionBroker::registerHandler(const QByteArray &actionId, ActionHandler *ha | |||
94 | { | 94 | { |
95 | mHandler.insert(actionId, handler); | 95 | mHandler.insert(actionId, handler); |
96 | } | 96 | } |
97 | |||
98 | void ActionBroker::unregisterHandler(const QByteArray &actionId, ActionHandler *handler) | ||
99 | { | ||
100 | mHandler.remove(actionId, handler); | ||
101 | } | ||
diff --git a/framework/actions/actionbroker.h b/framework/actions/actionbroker.h index 84678c16..d893a3e7 100644 --- a/framework/actions/actionbroker.h +++ b/framework/actions/actionbroker.h | |||
@@ -36,6 +36,7 @@ public: | |||
36 | ActionResult executeAction(const QByteArray &actionId, Context *context, const QList<QPointer<ActionHandler>> &preHandler, const QList<QPointer<ActionHandler>> &postHandler); | 36 | ActionResult executeAction(const QByteArray &actionId, Context *context, const QList<QPointer<ActionHandler>> &preHandler, const QList<QPointer<ActionHandler>> &postHandler); |
37 | 37 | ||
38 | void registerHandler(const QByteArray &actionId, ActionHandler *handler); | 38 | void registerHandler(const QByteArray &actionId, ActionHandler *handler); |
39 | void unregisterHandler(const QByteArray &actionId, ActionHandler *handler); | ||
39 | 40 | ||
40 | Q_SIGNALS: | 41 | Q_SIGNALS: |
41 | void readyChanged(); | 42 | void readyChanged(); |
diff --git a/framework/actions/actionhandler.cpp b/framework/actions/actionhandler.cpp index dc9edeca..eb7b3224 100644 --- a/framework/actions/actionhandler.cpp +++ b/framework/actions/actionhandler.cpp | |||
@@ -31,6 +31,11 @@ ActionHandler::ActionHandler(QObject *parent) | |||
31 | 31 | ||
32 | } | 32 | } |
33 | 33 | ||
34 | ActionHandler::~ActionHandler() | ||
35 | { | ||
36 | ActionBroker::instance().unregisterHandler(mActionId, this); | ||
37 | } | ||
38 | |||
34 | bool ActionHandler::isActionReady(Context *context) | 39 | bool ActionHandler::isActionReady(Context *context) |
35 | { | 40 | { |
36 | if (context) { | 41 | if (context) { |
@@ -67,6 +72,8 @@ ActionResult ActionHandler::execute(Context *context) | |||
67 | 72 | ||
68 | void ActionHandler::setActionId(const QByteArray &actionId) | 73 | void ActionHandler::setActionId(const QByteArray &actionId) |
69 | { | 74 | { |
75 | //Reassigning the id is not supported | ||
76 | Q_ASSERT(mActionId.isEmpty()); | ||
70 | mActionId = actionId; | 77 | mActionId = actionId; |
71 | ActionBroker::instance().registerHandler(actionId, this); | 78 | ActionBroker::instance().registerHandler(actionId, this); |
72 | } | 79 | } |
@@ -76,6 +83,16 @@ QByteArray ActionHandler::actionId() const | |||
76 | return mActionId; | 83 | return mActionId; |
77 | } | 84 | } |
78 | 85 | ||
86 | void ActionHandler::setRequiredProperties(const QSet<QByteArray> &requiredProperties) | ||
87 | { | ||
88 | mRequiredProperties = requiredProperties; | ||
89 | } | ||
90 | |||
91 | QSet<QByteArray> ActionHandler::requiredProperties() const | ||
92 | { | ||
93 | return mRequiredProperties; | ||
94 | } | ||
95 | |||
79 | 96 | ||
80 | ActionHandlerHelper::ActionHandlerHelper(const Handler &handler) | 97 | ActionHandlerHelper::ActionHandlerHelper(const Handler &handler) |
81 | : ActionHandler(nullptr), | 98 | : ActionHandler(nullptr), |
diff --git a/framework/actions/actionhandler.h b/framework/actions/actionhandler.h index 09ed13c6..5ccf0ac7 100644 --- a/framework/actions/actionhandler.h +++ b/framework/actions/actionhandler.h | |||
@@ -24,9 +24,9 @@ | |||
24 | #include <Async/Async> | 24 | #include <Async/Async> |
25 | 25 | ||
26 | #include "actionresult.h" | 26 | #include "actionresult.h" |
27 | #include "context.h" | ||
27 | 28 | ||
28 | namespace Kube { | 29 | namespace Kube { |
29 | class Context; | ||
30 | 30 | ||
31 | class ActionHandler : public QObject | 31 | class ActionHandler : public QObject |
32 | { | 32 | { |
@@ -35,6 +35,7 @@ class ActionHandler : public QObject | |||
35 | 35 | ||
36 | public: | 36 | public: |
37 | ActionHandler(QObject *parent = 0); | 37 | ActionHandler(QObject *parent = 0); |
38 | virtual ~ActionHandler(); | ||
38 | 39 | ||
39 | virtual bool isActionReady(Context *context); | 40 | virtual bool isActionReady(Context *context); |
40 | 41 | ||
@@ -43,25 +44,65 @@ public: | |||
43 | void setActionId(const QByteArray &); | 44 | void setActionId(const QByteArray &); |
44 | QByteArray actionId() const; | 45 | QByteArray actionId() const; |
45 | 46 | ||
47 | void setRequiredProperties(const QSet<QByteArray> &requiredProperties); | ||
48 | QSet<QByteArray> requiredProperties() const; | ||
49 | |||
46 | private: | 50 | private: |
47 | QByteArray mActionId; | 51 | QByteArray mActionId; |
52 | QSet<QByteArray> mRequiredProperties; | ||
53 | }; | ||
54 | |||
55 | template <typename ContextType> | ||
56 | class ActionHandlerBase : public ActionHandler | ||
57 | { | ||
58 | public: | ||
59 | ActionHandlerBase(const QByteArray &actionId) | ||
60 | : ActionHandler{} | ||
61 | { | ||
62 | setActionId(actionId); | ||
63 | } | ||
64 | |||
65 | bool isActionReady(Context *c) Q_DECL_OVERRIDE | ||
66 | { | ||
67 | auto wrapper = ContextType{*c}; | ||
68 | return isActionReady(wrapper); | ||
69 | } | ||
70 | |||
71 | ActionResult execute(Context *c) Q_DECL_OVERRIDE | ||
72 | { | ||
73 | ActionResult result; | ||
74 | auto wrapper = ContextType{*c}; | ||
75 | execute(wrapper) | ||
76 | .template syncThen<void>([=](const KAsync::Error &error) { | ||
77 | auto modifyableResult = result; | ||
78 | if (error) { | ||
79 | qWarning() << "Job failed: " << error.errorCode << error.errorMessage; | ||
80 | modifyableResult.setError(1); | ||
81 | } | ||
82 | modifyableResult.setDone(); | ||
83 | }).exec(); | ||
84 | return result; | ||
85 | } | ||
86 | protected: | ||
87 | |||
88 | virtual bool isActionReady(ContextType &) { return true; } | ||
89 | virtual KAsync::Job<void> execute(ContextType &) = 0; | ||
48 | }; | 90 | }; |
49 | 91 | ||
50 | class ActionHandlerHelper : public ActionHandler | 92 | class ActionHandlerHelper : public ActionHandler |
51 | { | 93 | { |
52 | Q_OBJECT | ||
53 | public: | 94 | public: |
54 | typedef std::function<bool(Context*)> IsReadyFunction; | 95 | typedef std::function<bool(Context *)> IsReadyFunction; |
55 | typedef std::function<void(Context*)> Handler; | 96 | typedef std::function<void(Context *)> Handler; |
56 | typedef std::function<KAsync::Job<void>(Context*)> JobHandler; | 97 | typedef std::function<KAsync::Job<void>(Context *)> JobHandler; |
57 | 98 | ||
58 | ActionHandlerHelper(const Handler &); | 99 | ActionHandlerHelper(const Handler &); |
59 | ActionHandlerHelper(const IsReadyFunction &, const Handler &); | 100 | ActionHandlerHelper(const IsReadyFunction &, const Handler &); |
60 | ActionHandlerHelper(const QByteArray &actionId, const IsReadyFunction &, const Handler &); | 101 | ActionHandlerHelper(const QByteArray &actionId, const IsReadyFunction &, const Handler &); |
61 | ActionHandlerHelper(const QByteArray &actionId, const IsReadyFunction &, const JobHandler &); | 102 | ActionHandlerHelper(const QByteArray &actionId, const IsReadyFunction &, const JobHandler &); |
62 | 103 | ||
63 | bool isActionReady(Context *context) Q_DECL_OVERRIDE; | 104 | bool isActionReady(Context *) Q_DECL_OVERRIDE; |
64 | ActionResult execute(Context *context) Q_DECL_OVERRIDE; | 105 | ActionResult execute(Context *) Q_DECL_OVERRIDE; |
65 | private: | 106 | private: |
66 | const IsReadyFunction isReadyFunction; | 107 | const IsReadyFunction isReadyFunction; |
67 | const Handler handlerFunction; | 108 | const Handler handlerFunction; |
diff --git a/framework/actions/context.cpp b/framework/actions/context.cpp index 8f370a0b..45b660a9 100644 --- a/framework/actions/context.cpp +++ b/framework/actions/context.cpp | |||
@@ -29,6 +29,20 @@ Context::Context(QObject *parent) | |||
29 | 29 | ||
30 | } | 30 | } |
31 | 31 | ||
32 | Context::Context(const Context &other) | ||
33 | : QObject() | ||
34 | { | ||
35 | *this = other; | ||
36 | } | ||
37 | |||
38 | Context &Context::operator=(const Context &other) | ||
39 | { | ||
40 | for (const auto &p : other.availableProperties()) { | ||
41 | setProperty(p, other.property(p)); | ||
42 | } | ||
43 | return *this; | ||
44 | } | ||
45 | |||
32 | void Context::clear() | 46 | void Context::clear() |
33 | { | 47 | { |
34 | auto meta = metaObject(); | 48 | auto meta = metaObject(); |
@@ -41,6 +55,20 @@ void Context::clear() | |||
41 | } | 55 | } |
42 | } | 56 | } |
43 | 57 | ||
58 | QSet<QByteArray> Context::availableProperties() const | ||
59 | { | ||
60 | QSet<QByteArray> names; | ||
61 | auto meta = metaObject(); | ||
62 | for (auto i = meta->propertyOffset(); i < meta->propertyCount(); i++) { | ||
63 | auto property = meta->property(i); | ||
64 | names << property.name(); | ||
65 | } | ||
66 | for (const auto &p : dynamicPropertyNames()) { | ||
67 | names << p; | ||
68 | } | ||
69 | return names; | ||
70 | } | ||
71 | |||
44 | QDebug operator<<(QDebug dbg, const Kube::Context &context) | 72 | QDebug operator<<(QDebug dbg, const Kube::Context &context) |
45 | { | 73 | { |
46 | dbg << "Kube::Context {\n"; | 74 | dbg << "Kube::Context {\n"; |
@@ -55,3 +83,9 @@ QDebug operator<<(QDebug dbg, const Kube::Context &context) | |||
55 | dbg << "\n}"; | 83 | dbg << "\n}"; |
56 | return dbg; | 84 | return dbg; |
57 | } | 85 | } |
86 | |||
87 | QDebug operator<<(QDebug dbg, const Kube::ContextWrapper &context) | ||
88 | { | ||
89 | dbg << context.context; | ||
90 | return dbg; | ||
91 | } | ||
diff --git a/framework/actions/context.h b/framework/actions/context.h index 42ae3a93..4207fe12 100644 --- a/framework/actions/context.h +++ b/framework/actions/context.h | |||
@@ -19,17 +19,20 @@ | |||
19 | #pragma once | 19 | #pragma once |
20 | 20 | ||
21 | #include <QObject> | 21 | #include <QObject> |
22 | |||
23 | #define KUBE_CONTEXT_PROPERTY(TYPE, NAME, LOWERCASENAME) \ | 22 | #define KUBE_CONTEXT_PROPERTY(TYPE, NAME, LOWERCASENAME) \ |
24 | public: Q_PROPERTY(TYPE LOWERCASENAME MEMBER m##NAME NOTIFY LOWERCASENAME##Changed) \ | 23 | public: Q_PROPERTY(TYPE LOWERCASENAME MEMBER m##NAME NOTIFY LOWERCASENAME##Changed) \ |
24 | Q_SIGNALS: void LOWERCASENAME##Changed(); \ | ||
25 | private: TYPE m##NAME; | ||
26 | |||
27 | #define KUBE_CONTEXTWRAPPER_PROPERTY(TYPE, NAME, LOWERCASENAME) \ | ||
28 | public: \ | ||
25 | struct NAME { \ | 29 | struct NAME { \ |
26 | static constexpr const char *name = #LOWERCASENAME; \ | 30 | static constexpr const char *name = #LOWERCASENAME; \ |
27 | typedef TYPE Type; \ | 31 | typedef TYPE Type; \ |
28 | }; \ | 32 | }; \ |
29 | void set##NAME(const TYPE &value) { setProperty(NAME::name, QVariant::fromValue(value)); } \ | 33 | void set##NAME(const TYPE &value) { context.setProperty(NAME::name, QVariant::fromValue(value)); } \ |
30 | TYPE get##NAME() const { return m##NAME; } \ | 34 | void clear##NAME() { context.setProperty(NAME::name, QVariant{}); } \ |
31 | Q_SIGNALS: void LOWERCASENAME##Changed(); \ | 35 | TYPE get##NAME() const { return context.property(NAME::name).value<TYPE>(); } \ |
32 | private: TYPE m##NAME; | ||
33 | 36 | ||
34 | 37 | ||
35 | namespace Kube { | 38 | namespace Kube { |
@@ -38,13 +41,27 @@ class Context : public QObject { | |||
38 | Q_OBJECT | 41 | Q_OBJECT |
39 | public: | 42 | public: |
40 | Context(QObject *parent = 0); | 43 | Context(QObject *parent = 0); |
44 | Context(const Context &); | ||
45 | |||
41 | virtual ~Context(){}; | 46 | virtual ~Context(){}; |
47 | |||
48 | Context &operator=(const Context &); | ||
49 | |||
42 | virtual void clear(); | 50 | virtual void clear(); |
51 | |||
52 | QSet<QByteArray> availableProperties() const; | ||
53 | }; | ||
54 | |||
55 | class ContextWrapper { | ||
56 | public: | ||
57 | ContextWrapper(Context &c) : context{c} {} | ||
58 | Context &context; | ||
43 | }; | 59 | }; |
44 | 60 | ||
45 | } | 61 | } |
46 | 62 | ||
47 | QDebug operator<<(QDebug dbg, const Kube::Context &); | 63 | QDebug operator<<(QDebug dbg, const Kube::Context &); |
64 | QDebug operator<<(QDebug dbg, const Kube::ContextWrapper &); | ||
48 | 65 | ||
49 | Q_DECLARE_METATYPE(Kube::Context*); | 66 | Q_DECLARE_METATYPE(Kube::Context*); |
50 | 67 | ||
diff --git a/framework/actions/tests/CMakeLists.txt b/framework/actions/tests/CMakeLists.txt new file mode 100644 index 00000000..af872a3b --- /dev/null +++ b/framework/actions/tests/CMakeLists.txt | |||
@@ -0,0 +1,6 @@ | |||
1 | include_directories(${CMAKE_CURRENT_BINARY_DIR}) | ||
2 | cmake_policy(SET CMP0063 NEW) | ||
3 | add_executable(actiontest actiontest.cpp) | ||
4 | add_test(actiontest sinkactiontest) | ||
5 | qt5_use_modules(actiontest Core Test) | ||
6 | target_link_libraries(actiontest actionplugin) | ||
diff --git a/framework/actions/tests/actiontest.cpp b/framework/actions/tests/actiontest.cpp new file mode 100644 index 00000000..a4ec4432 --- /dev/null +++ b/framework/actions/tests/actiontest.cpp | |||
@@ -0,0 +1,102 @@ | |||
1 | #include <QTest> | ||
2 | #include <QDebug> | ||
3 | #include <QSignalSpy> | ||
4 | |||
5 | #include <actions/action.h> | ||
6 | #include <actions/context.h> | ||
7 | #include <actions/actionhandler.h> | ||
8 | |||
9 | #include <sink/log.h> | ||
10 | |||
11 | SINK_DEBUG_AREA("actiontest") | ||
12 | |||
13 | class HandlerContext : public Kube::Context { | ||
14 | Q_OBJECT | ||
15 | KUBE_CONTEXT_PROPERTY(QString, Property1, property1) | ||
16 | KUBE_CONTEXT_PROPERTY(QString, Property2, property2) | ||
17 | }; | ||
18 | |||
19 | class HandlerContextWrapper : public Kube::ContextWrapper { | ||
20 | using Kube::ContextWrapper::ContextWrapper; | ||
21 | KUBE_CONTEXTWRAPPER_PROPERTY(QString, Property1, property1) | ||
22 | KUBE_CONTEXTWRAPPER_PROPERTY(QString, Property2, property2) | ||
23 | }; | ||
24 | |||
25 | |||
26 | |||
27 | class Handler : public Kube::ActionHandlerBase<HandlerContextWrapper> | ||
28 | { | ||
29 | public: | ||
30 | Handler() : Kube::ActionHandlerBase<HandlerContextWrapper>{"org.kde.kube.test.action1"} | ||
31 | {} | ||
32 | |||
33 | //TODO default implementation checks that all defined properties are available in the context | ||
34 | // bool isReady() override { | ||
35 | // auto accountId = context->property("accountId").value<QByteArray>(); | ||
36 | // return !accountId.isEmpty(); | ||
37 | // } | ||
38 | |||
39 | KAsync::Job<void> execute(HandlerContextWrapper &context) | ||
40 | { | ||
41 | SinkLog() << "Executing action1"; | ||
42 | SinkLog() << context; | ||
43 | executions.append(context.context); | ||
44 | return KAsync::null<void>(); | ||
45 | } | ||
46 | mutable QList<Kube::Context> executions; | ||
47 | }; | ||
48 | |||
49 | class Context1 : public Kube::ContextWrapper { | ||
50 | using Kube::ContextWrapper::ContextWrapper; | ||
51 | KUBE_CONTEXTWRAPPER_PROPERTY(QString, Property1, property1) | ||
52 | KUBE_CONTEXTWRAPPER_PROPERTY(QByteArray, Property2, property2) | ||
53 | }; | ||
54 | |||
55 | class Context2 : public Kube::ContextWrapper { | ||
56 | using Kube::ContextWrapper::ContextWrapper; | ||
57 | KUBE_CONTEXTWRAPPER_PROPERTY(QByteArray, Property2, property2) | ||
58 | }; | ||
59 | |||
60 | |||
61 | class ActionTest : public QObject | ||
62 | { | ||
63 | Q_OBJECT | ||
64 | private slots: | ||
65 | |||
66 | void initTestCase() | ||
67 | { | ||
68 | } | ||
69 | |||
70 | void testActionExecution() | ||
71 | { | ||
72 | Handler actionHandler; | ||
73 | |||
74 | HandlerContext context; | ||
75 | //Kube::Context context; | ||
76 | HandlerContextWrapper{context}.setProperty1(QString("property1")); | ||
77 | context.setProperty("property2", QVariant::fromValue(QString("property2"))); | ||
78 | auto future = Kube::Action("org.kde.kube.test.action1", context).executeWithResult(); | ||
79 | |||
80 | QTRY_VERIFY(future.isDone()); | ||
81 | QVERIFY(!future.error()); | ||
82 | |||
83 | QCOMPARE(actionHandler.executions.size(), 1); | ||
84 | QCOMPARE(actionHandler.executions.first().availableProperties().size(), 2); | ||
85 | } | ||
86 | |||
87 | void testContextCasting() | ||
88 | { | ||
89 | Kube::Context c; | ||
90 | |||
91 | Context1 context1{c}; | ||
92 | context1.setProperty1("property1"); | ||
93 | context1.setProperty2("property2"); | ||
94 | |||
95 | auto context2 = Context2{c}; | ||
96 | QCOMPARE(context2.getProperty2(), QByteArray("property2")); | ||
97 | } | ||
98 | |||
99 | }; | ||
100 | |||
101 | QTEST_GUILESS_MAIN(ActionTest) | ||
102 | #include "actiontest.moc" | ||