1/*
2 SPDX-FileCopyrightText: 2015 Aleix Pol Gonzalez <aleixpol@blue-systems.com>
3
4 SPDX-License-Identifier: LGPL-2.1-or-later
5*/
6
7#include "purpose/configuration.h"
8#include "externalprocess/processjob.h"
9#include <QFileInfo>
10
11#include <KPluginFactory>
12#include <KPluginMetaData>
13
14#include <QDebug>
15#include <QDir>
16#include <QStandardPaths>
17#include <qregularexpression.h>
18
19#include "helper.h"
20#include "pluginbase.h"
21
22using namespace Purpose;
23
24class Purpose::ConfigurationPrivate
25{
26public:
27 QJsonObject m_inputData;
28 QString m_pluginTypeName;
29 QJsonObject m_pluginType;
30 const KPluginMetaData m_pluginData;
31 bool m_useSeparateProcess;
32
33 static void checkJobFinish(KJob *job)
34 {
35 const QStringList outputArgs = job->property(name: "outputArgs").toStringList();
36 const auto argsSet = QSet<QString>{outputArgs.cbegin(), outputArgs.cend()};
37
38 const QStringList outputKeys = job->property(name: "output").toJsonObject().keys();
39 const auto keysSet = QSet<QString>{outputKeys.cbegin(), outputKeys.cend()};
40
41 if (!keysSet.contains(other: argsSet) && job->error() == 0) {
42 qWarning() << "missing output values for" << job->metaObject()->className() << ". Expected: " << outputArgs.join(QStringLiteral(", "))
43 << ". Got: " << outputKeys.join(QStringLiteral(", "));
44 }
45 }
46
47 Purpose::Job *internalCreateJob(QObject *parent) const
48 {
49 if (m_useSeparateProcess)
50 return new ProcessJob(m_pluginData.fileName(), m_pluginTypeName, m_inputData, parent);
51 else {
52 return createJob(parent);
53 }
54 }
55
56 Purpose::Job *createJob(QObject *parent) const
57 {
58 if (m_pluginData.fileName().contains(s: QLatin1String("contents/code/main."))) {
59 return new ProcessJob(m_pluginData.fileName(), m_pluginTypeName, m_inputData, parent);
60 } else {
61 auto pluginResult = KPluginFactory::instantiatePlugin<QObject>(data: m_pluginData, parent, args: QVariantList());
62
63 if (!pluginResult) {
64 qWarning() << "Couldn't load plugin:" << m_pluginData.fileName() << pluginResult.errorString;
65 return nullptr;
66 }
67
68 Purpose::PluginBase *plugin = dynamic_cast<Purpose::PluginBase *>(pluginResult.plugin);
69 return plugin->createJob();
70 }
71 }
72};
73
74Configuration::Configuration(const QJsonObject &inputData, const QString &pluginTypeName, const KPluginMetaData &pluginInformation, QObject *parent)
75 : Configuration(inputData, pluginTypeName, QJsonObject(), pluginInformation, parent)
76{
77}
78
79Configuration::Configuration(const QJsonObject &inputData,
80 const QString &pluginTypeName,
81 const QJsonObject &pluginType,
82 const KPluginMetaData &pluginInformation,
83 QObject *parent)
84 : QObject(parent)
85 , d_ptr(new ConfigurationPrivate{.m_inputData: inputData, .m_pluginTypeName: pluginTypeName, .m_pluginType: pluginType, .m_pluginData: pluginInformation, .m_useSeparateProcess: !qEnvironmentVariableIsSet(varName: "KDE_PURPOSE_LOCAL_JOBS")})
86{
87}
88
89Configuration::~Configuration()
90{
91 delete d_ptr;
92}
93
94void Configuration::setData(const QJsonObject &data)
95{
96 Q_D(Configuration);
97
98 // qDebug() << "datachanged" << data;
99 if (d->m_inputData != data) {
100 d->m_inputData = data;
101 Q_EMIT dataChanged();
102 }
103}
104
105QJsonObject Configuration::data() const
106{
107 Q_D(const Configuration);
108 return d->m_inputData;
109}
110
111bool Configuration::isReady() const
112{
113 Q_D(const Configuration);
114 bool ok = true;
115 const auto arguments = neededArguments();
116 for (const QJsonValue &arg : arguments) {
117 if (!d->m_inputData.contains(key: arg.toString())) {
118 qDebug() << "missing mandatory argument" << arg.toString();
119 ok = false;
120 }
121 }
122 return ok;
123}
124
125QJsonArray Configuration::neededArguments() const
126{
127 Q_D(const Configuration);
128 QJsonArray ret = d->m_pluginType.value(key: QLatin1String("X-Purpose-InboundArguments")).toArray();
129 const QJsonArray arr = d->m_pluginData.rawData().value(key: QLatin1String("X-Purpose-Configuration")).toArray();
130 for (const QJsonValue &val : arr)
131 ret += val;
132 return ret;
133}
134
135Purpose::Job *Configuration::createJob()
136{
137 if (!isReady())
138 return nullptr;
139
140 Q_D(const Configuration);
141
142 Purpose::Job *job = d->internalCreateJob(parent: this);
143 if (!job)
144 return job;
145
146 job->setData(d->m_inputData);
147 job->setProperty(name: "outputArgs", value: d->m_pluginType.value(key: QLatin1String("X-Purpose-OutboundArguments")));
148
149 connect(sender: job, signal: &Purpose::Job::finished, slot: &ConfigurationPrivate::checkJobFinish);
150 return job;
151}
152
153QUrl Configuration::configSourceCode() const
154{
155 Q_D(const Configuration);
156 if (d->m_pluginData.fileName().contains(s: QLatin1String("contents/code/main."))) {
157 const QFileInfo fi(d->m_pluginData.fileName());
158 QDir conentsDir = fi.dir();
159 conentsDir.cdUp();
160 return QUrl::fromLocalFile(localfile: conentsDir.filePath(QStringLiteral("config/config.qml")));
161 } else {
162 const QString configFile =
163 QStandardPaths::locate(type: QStandardPaths::GenericDataLocation, QStringLiteral("kf6/purpose/%1_config.qml").arg(a: d->m_pluginData.pluginId()));
164 if (configFile.isEmpty())
165 return QUrl();
166
167 return QUrl::fromLocalFile(localfile: configFile);
168 }
169}
170
171bool Configuration::useSeparateProcess() const
172{
173 Q_D(const Configuration);
174 return d->m_useSeparateProcess;
175}
176
177void Configuration::setUseSeparateProcess(bool use)
178{
179 Q_D(Configuration);
180 d->m_useSeparateProcess = use;
181}
182
183QString Configuration::pluginTypeName() const
184{
185 Q_D(const Configuration);
186 KPluginMetaData md(d->m_pluginType, {});
187 return md.name();
188}
189
190QString Configuration::pluginName() const
191{
192 Q_D(const Configuration);
193 return d->m_pluginData.name();
194}
195
196#include "moc_configuration.cpp"
197

source code of purpose/src/configuration.cpp