#include "Client.h" #include <QDebug> #include <QJsonDocument> #include <QNetworkAccessManager> #include <QNetworkRequest> #include <QNetworkReply> #include <QtWebSockets/QtWebSockets> #include <assert.h> #include "../hws.h" namespace hws { //! \{ typedef QSharedPointer<QNetworkRequest> QNetworkRequestPtr; typedef QSharedPointer<QByteArray> QByteArrayPtr; // The URL passed in by a user should probably just indicate a server. // We typically place the API under this prefix path. QString DEFAULT_HWS_PREFIX_PATH = "/hws"; //! \{ class Client::Impl : public QObject { Q_OBJECT public: //! \{ Impl(Client *parent) : mClient(parent), mManager(parent), mUrl("http://example.com"), mSession(NULL), mHWSPrefixPath(DEFAULT_HWS_PREFIX_PATH), mConfig(), mIngoreSslErrors(false) { } QNetworkRequestPtr createRequest() { QNetworkRequestPtr request = QNetworkRequestPtr::create(); request->setRawHeader("User-Agent", (QString("helix_web_services_client %1.%2.%3") .arg(HELIX_WEB_SERVICES_MAJOR_VERSION) .arg(HELIX_WEB_SERVICES_MINOR_VERSION) .arg(HELIX_WEB_SERVICES_PATCH_VERSION)).toUtf8()); request->setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); request->setRawHeader("Accept", "application/json"); request->setRawHeader("Accept-Encoding", "gzip, deflate"); foreach(QString key, mConfig.keys()) { QString fullKey = QString("X-Perforce-Helix-Web-Services-%1").arg(key); request->setRawHeader(fullKey.toUtf8(), mConfig[key].toUtf8()); } return request; } // Configures the url on the path with the indicated final path part. // The path should not be encoded. // // Note: will not set the query parameter. void setRequestUrlWithPath(QNetworkRequestPtr request, QString path, const QSharedPointer<QStringHash> params = QSharedPointer<QStringHash>()) { QUrl url(mUrl); QString fullPath(mHWSPrefixPath); fullPath += path; url.setPath(fullPath); if (params) { QUrlQuery urlQuery(url); foreach (QString key, params->keys()) { QString value = (*params)[key]; urlQuery.addQueryItem(key, value); } url.setQuery(urlQuery); } qDebug() << "setting request url " << url; request->setUrl(url); } void setBasicAuthFromSession(QNetworkRequestPtr request); QByteArray createSessionPostBody(const QString & user, const QString & password) { QJsonObject obj; obj["user"] = user; obj["password"] = password; QJsonDocument doc(obj); return doc.toJson(QJsonDocument::Compact); } void logInFinished(QNetworkReply *reply, const QString & user); RequestErrorPtr fromNetworkError(QNetworkReply::NetworkError code); RequestErrorPtr fromSslErrors(const QList<QSslError> & errors); void setSession(const Session & session); void executeMethodFinished(QNetworkReply * reply, const QString & method, const QString & path); // !\} public slots: //! \{ void validateSessionFinished(); void validateSessionError(QNetworkReply::NetworkError code); void validateSessionSslErrors(const QList<QSslError> & errors); void handleLogInError(QNetworkReply::NetworkError code); void handleLogInSslErrors(const QList<QSslError> & errors); void executeMethodError(QNetworkReply::NetworkError code); void executeMethodSslErrors(const QList<QSslError> & errors); //! \} // Member state public: //! \{ Client *mClient; QNetworkAccessManager mManager; QUrl mUrl; SessionPtr mSession; QString mHWSPrefixPath; friend class LogInFinished; QStringHash mConfig; bool mIngoreSslErrors; //! \} }; //! \} //-------------------------------------------------------------------------- // Client::LogInFinished //-------------------------------------------------------------------------- // This is really a relatively simple object to bind the user and network // reply to a login callback on the main object. class Client::LogInFinished : public QObject { Q_OBJECT public: //! \{ LogInFinished(QObject *parent, Client::Impl & impl, QNetworkReply *reply, const QString & user) : QObject(parent), mImpl(impl), mReply(reply), mUser(user) { }; //! \} public slots: //! \{ void finished() { mImpl.logInFinished(mReply, mUser); }; //! \} private: Client::Impl & mImpl; QNetworkReply *mReply; QString mUser; }; //-------------------------------------------------------------------------- // Client::ExecuteMethodFinished //-------------------------------------------------------------------------- // This is really a relatively simple object to bind the method and path // to the execute method callback. class Client::ExecuteMethodFinished : public QObject { Q_OBJECT public: //! \{ ExecuteMethodFinished(QObject *parent, Client::Impl & impl, QNetworkReply *reply, const QString & method, const QString & path) : QObject(parent), mImpl(impl), mReply(reply), mMethod(method), mPath(path) { } //! \} public slots: //! \{ void finished() { mImpl.executeMethodFinished(mReply, mMethod, mPath); }; //! \} private: Client::Impl & mImpl; QNetworkReply * mReply; QString mMethod; QString mPath; }; //-------------------------------------------------------------------------- // Client::Impl //-------------------------------------------------------------------------- void Client::Impl::setBasicAuthFromSession( QNetworkRequestPtr request) { if (mClient->hasSession()) { QString userpass = QString("%1:%2").arg(mClient->session().user()) .arg(mClient->session().p4Ticket()); QString b64Userpass(userpass.toUtf8().toBase64()); QString value = QString("Basic %1").arg(b64Userpass); request->setRawHeader("Authorization", value.toUtf8()); } } void Client::Impl::logInFinished(QNetworkReply *reply, const QString & user) { if (reply->isFinished() && reply->error() == QNetworkReply::NoError) { QByteArray data = reply->readAll(); RequestErrorPtr error = parseRequestError(data); SessionPtr session; if (!error) { QJsonDocument doc; QJsonParseError err; doc = doc.fromJson(data, &err); if (err.error == QJsonParseError::NoError) { QJsonObject object = doc.object(); if (object.contains("ticket")) { QString p4Ticket = object["ticket"].toString(); session.reset(new Session); session->setUrl(mUrl); session->setUser(user); session->setP4Ticket(p4Ticket); mClient->setSession(*session); } } } if (!session) { QString msg = QString( "Unable to create session from JSON data: %1").arg( QString(data)); error.reset(new RequestError(msg, msg, RequestError::JSON_ERROR, RequestError::ERROR)); } emit mClient->logInDone(error, session); } } RequestErrorPtr Client::Impl::fromNetworkError( QNetworkReply::NetworkError code) { QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); RequestErrorPtr error; QVariant variantStatus = reply->attribute( QNetworkRequest::HttpStatusCodeAttribute); int status = -1; if (variantStatus.isValid()) { status = variantStatus.toInt(); } if (status == 403 || code == QNetworkReply::AuthenticationRequiredError) { error.reset(new RequestError); QString msg("Authentication Required"); error->setBaseMessageText(msg); error->setMessageText(msg); error->setCode(RequestError::AUTHENTICATION_ERROR); error->setSeverity(RequestError::ERROR); } if (error.isNull() && reply->isReadable()) { // See if we have any kind of request error response QByteArray data = reply->readAll(); RequestErrorPtr e = parseRequestError(data); if (!e.isNull()) error = e; } if (error.isNull()) { error.reset(new RequestError); // There's not a lot of information here, beyond typical request headers QString msg = QString("Network Error %1").arg(code); error->setMessageText(msg); error->setBaseMessageText(msg); error->setCode(RequestError::NETWORK_ERROR); error->setSeverity(RequestError::ERROR); } return error; } RequestErrorPtr Client::Impl::fromSslErrors( const QList<QSslError> & errors) { // We may want to convert these SSL errors to messages that are more // informative, but I have next to no information what clients would // need. I've found that with this API, information I neewd from Qt // is only logged, and not returned via these objects. RequestErrorPtr error(new RequestError()); error->setMessageText("SSL Error"); error->setBaseMessageText("SSL Error"); error->setCode(RequestError::SSL_ERROR); error->setSeverity(RequestError::ERROR); return error; } void Client::Impl::setSession(const Session & session) { mSession.reset(new Session(session)); } void Client::Impl::validateSessionFinished() { QNetworkReply * reply = static_cast<QNetworkReply*>(sender()); if (reply->isFinished() && reply->error() == QNetworkReply::NoError) { RequestErrorPtr error; emit mClient->validateSessionDone(error); } } void Client::Impl::validateSessionError(QNetworkReply::NetworkError code) { RequestErrorPtr error = fromNetworkError(code); emit mClient->validateSessionDone(error); } void Client::Impl::validateSessionSslErrors(const QList<QSslError> & errors) { if (mIngoreSslErrors) { QNetworkReply * reply = static_cast<QNetworkReply*>(sender()); reply->ignoreSslErrors(); } else { RequestErrorPtr error = fromSslErrors(errors); emit mClient->validateSessionDone(error); } } void Client::Impl::handleLogInError( QNetworkReply::NetworkError code) { RequestErrorPtr error = fromNetworkError(code); emit mClient->logInDone(error, SessionPtr()); }; void Client::Impl::handleLogInSslErrors( const QList<QSslError> & errors) { if (mIngoreSslErrors) { QNetworkReply * reply = static_cast<QNetworkReply*>(sender()); reply->ignoreSslErrors(); } else { RequestErrorPtr error = fromSslErrors(errors); emit mClient->logInDone(error, SessionPtr()); } } void Client::Impl::executeMethodFinished(QNetworkReply * reply, const QString & method, const QString & path) { QSharedPointer<QVariantMapList> data; RequestErrorPtr error; if (reply->isFinished() && reply->error() == QNetworkReply::NoError) { // The web API is intended to return small amounts of data. If you // try to, say, download a large file, you're more than likely // going to get a failure from the web service. QByteArray replyData = reply->readAll(); QJsonParseError jsonParseError; QJsonDocument jsonDocument; jsonDocument = jsonDocument.fromJson(replyData, &jsonParseError); if (jsonParseError.error == QJsonParseError::NoError) { data.reset(new QVariantMapList()); if (jsonDocument.isObject()) { QJsonObject jsonObject = jsonDocument.object(); data->append(jsonObject.toVariantMap()); } else if (jsonDocument.isArray()) { QJsonArray array = jsonDocument.array(); foreach (QJsonValue val, array) { if (val.isObject()) { data->append(val.toObject().toVariantMap()); } else { QVariantMap map; map["value"] = val.toVariant(); data->append(map); } } } else { data.reset(); error.reset(new RequestError("Invalid JSON data", "Invalid JSON data", RequestError::JSON_ERROR, RequestError::ERROR)); } } else { error.reset(new RequestError("Invalid JSON", "Invalid JSON", RequestError::JSON_ERROR, RequestError::ERROR)); } } // We *may* want to check for our error JSON format, though that // should be handled by the executeMethodError mechanism. emit mClient->executeMethodDone(error, method, path, data); } void Client::Impl::executeMethodError(QNetworkReply::NetworkError code) { RequestErrorPtr error = fromNetworkError(code); emit mClient->executeMethodDone(error, "", "", QSharedPointer<QVariantMapList>()); } void Client::Impl::executeMethodSslErrors(const QList<QSslError> & errors) { if (mIngoreSslErrors) { QNetworkReply * reply = static_cast<QNetworkReply*>(sender()); reply->ignoreSslErrors(); } else { RequestErrorPtr error = fromSslErrors(errors); emit mClient->executeMethodDone(error, "", "", QSharedPointer<QVariantMapList>()); } } //-------------------------------------------------------------------------- // Client //-------------------------------------------------------------------------- Client::Client(QObject *parent) : QObject(parent), mImpl(new Impl(this)) { } Client::Client(QObject *parent, QUrl url) : QObject(parent), mImpl(new Impl(this)) { setUrl(url); } Client::~Client() { delete mImpl; } void Client::setUrl(const QUrl & url) { mImpl->mUrl = url; } const QUrl & Client::url() const { return mImpl->mUrl; } bool Client::hasSession() const { return mImpl->mSession.isNull() == false; } const Session & Client::session() const { return *(mImpl->mSession); } void Client::setSession(const Session & session) { mImpl->setSession(session); } const QString & Client::hwsPrefixPath() const { return mImpl->mHWSPrefixPath; } void Client::setHWSPrefixPath(const QString & path) { mImpl->mHWSPrefixPath = path; } void Client::addRequestConfig(QString key, QString value) { mImpl->mConfig[key] = value; } void Client::ignoreSslErrors(bool ignore) { mImpl->mIngoreSslErrors = true; } // The status method might be used to double check our user has void Client::validateSession() { QNetworkRequestPtr request = mImpl->createRequest(); mImpl->setRequestUrlWithPath(request, "/status"); mImpl->setBasicAuthFromSession(request); QNetworkReply *reply = mImpl->mManager.get(*request); connect(reply, SIGNAL(finished()), mImpl, SLOT(validateSessionFinished())); connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), mImpl, SLOT(validateSessionError(QNetworkReply::NetworkError))); connect(reply, SIGNAL(sslErrors( const QList<QSslError> &)), mImpl, SLOT(validateSessionSslErrors( const QList<QSslError> &))); } void Client::logIn(const QString & user, const QString & password) { QByteArray data = mImpl->createSessionPostBody(user, password); QNetworkRequestPtr request = mImpl->createRequest(); mImpl->setRequestUrlWithPath(request, "/auth/v1/login"); // request->setRawHeader("Content-Length", // QByteArray::number(data.size())); QNetworkReply *reply = mImpl->mManager.post(*request, data); LogInFinished *finisher = new LogInFinished(this, *mImpl, reply, user); connect(reply, SIGNAL(finished()), finisher, SLOT(finished())); connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), mImpl, SLOT(handleLogInError(QNetworkReply::NetworkError))); connect(reply, SIGNAL(sslErrors( const QList<QSslError> &)), mImpl, SLOT(handleLogInSslErrors( const QList<QSslError> &))); } void Client::executeMethod(const QString & method, const QString & path, const QSharedPointer<QStringHash> params, const QSharedPointer<QByteArray> body) { QNetworkRequestPtr request = mImpl->createRequest(); mImpl->setRequestUrlWithPath(request, path, params); mImpl->setBasicAuthFromSession(request); QIODevice *data = NULL; if (body) { data = new QBuffer(body.data(), this); } QNetworkReply *reply = mImpl->mManager.sendCustomRequest(*request, method.toUtf8(), data); ExecuteMethodFinished * finisher = new ExecuteMethodFinished(this, *mImpl, reply, method, path); connect(reply, SIGNAL(finished()), finisher, SLOT(finished())); connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), mImpl, SLOT(executeMethodError(QNetworkReply::NetworkError))); connect(reply, SIGNAL(sslErrors( const QList<QSslError> &)), mImpl, SLOT(executeMethodSslErrors( const QList<QSslError> &))); } QJsonObject hashToJsonObject(const QVariantMap & map) { QJsonObject obj; foreach(QString key, map.keys()) { QVariant value = map[key]; QJsonValue jsonValue = QJsonValue::fromVariant(value); obj.insert(key, jsonValue); } return obj; } void Client::executeMethod(const QString & method, const QString & path, const QSharedPointer<QStringHash> params, const QVariantMap & body) { QJsonObject obj = hashToJsonObject(body); QJsonDocument doc(obj); QByteArray json = doc.toJson(QJsonDocument::Compact); executeMethod(method, path, params, QSharedPointer<QByteArray>(&json)); } void Client::executeMethod(const QString & method, const QString & path, const QSharedPointer<QStringHash> params, const QVariantMapList & body) { QJsonArray arr; foreach(QVariantMap map, body) { QJsonObject obj = hashToJsonObject(map); arr.append(obj); } QJsonDocument doc(arr); QByteArray json = doc.toJson(QJsonDocument::Compact); executeMethod(method, path, params, QSharedPointer<QByteArray>(&json)); } //! \} } #include "Client.moc"
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#1 | 15741 | ptomiak | Branch HWS for my use. | ||
//guest/perforce_software/helix-web-services/main/source/helix_web_services_client_qt/hws/Client.cpp | |||||
#1 | 15622 | tjuricek |
Move source code to 'source/' subdirectory of branch. build/ will remain where it is. |
||
//guest/perforce_software/helix-web-services/main/helix_web_services_client_qt/hws/Client.cpp | |||||
#11 | 15601 | tjuricek | validateSessionFinished should not emit a signal if an error occurred on the request | ||
#10 | 15578 | tjuricek |
Removing QSettings* usage from hws::Client. The way QSettings was being used only is relevant for one connection at a time, and, it didn't seem to work on windows nicely anyway. |
||
#9 | 15528 | tjuricek | Initialize the QSettings data from Variant instead of assuming it's a JSON string. | ||
#8 | 15523 | tjuricek | Sync the QSettings object after updating the HWS session information. | ||
#7 | 15521 | tjuricek | Call client.ignoreSslErrors(true) to bypass self-signed cert problems. | ||
#6 | 15445 | tjuricek | Allow per-request overrides to be set via addRequestConfig | ||
#5 | 15423 | tjuricek |
Revised HWS Qt API. This is a major revision of the API, which removes most of the "typed" data, replacing it with a more generic "executeMethodDone" callback. The main benefit here is to allow the API to interop with different versions of p4d, and not restrict the methods it can call. We may add more helpers in the future. |
||
#4 | 14102 | tjuricek |
Set the default prefix to "/hws" for now. This is very likely to become a real convention. The "primary" UI will be hosted on the root, with the Web Services instance hosted "under" /hws. |
||
#3 | 14054 | tjuricek |
Allowing the Qt client to select 'all' vs 'my' projects. Right now, it should default to using "my" projects as a rule. |
||
#2 | 14049 | tjuricek |
Add methods to generate client workspaces for a user. The Qt SDK was updated based on immediate need. Also, add Ruby client SDK documentation to the docs site. Everything is early, but there's *some* reference available at least. |
||
#1 | 14025 | tjuricek |
Revise Qt SDK to a single 'helix_web_services_client' project. Most references to "Phoenix" have now been removed. Additionally, this is more similar to the other platform client SDKs in Ruby and JavaScript. Documentation via Doxygen is now available, and will be how much of the SDK reference should occur. |
||
//guest/perforce_software/helix-web-services/main/qt/p4_phoenix_services_client/phoenix/PhoenixServicesClient.cpp | |||||
#17 | 13941 | tjuricek |
Re-implemented the sync project methods at the HTTP level. The Qt API is missing the "members" concept, but it's likely not quite usable just yet. It's existing logic does work, however. |
||
#16 | 13532 | tjuricek |
Qt SDK: Add constructor that uses an application's QSettings for caching Sessions for each URL Added a simple validateSession remote call to ping the services instance to see if the session is indeed usable. Also, removing puma configuraiton that is automatically generated in the (shared) development instance. |
||
#15 | 13530 | tjuricek |
Add p4_phoenix_services package and Salt configuration for deployment. This uncovered a couple of issues from the C++ API during it's conversion to C++03. So, in a nutshell, most operations, except for notifications, appear to be working (well, using Vagrant machines). |
||
#14 | 13510 | tjuricek |
Convert version information to macros to get around unused variable warnings that some people want to use to fail builds. Also, fixed an actual reorder issue, and enabled '-Wall' on the cmake config. |
||
#13 | 13508 | tjuricek | Replace use of C++11 'nullptr' with NULL | ||
#12 | 13506 | tjuricek |
Change to a C++03 build. Major changes: 1. QSharedPointer::create(...) is much more restricted than in C++11, so just use the .reset() method unless you're passing in a single const reference argument to the constructor. 2. Using Qt's foreach. 3. Replace lambdas with simpler helper methods. This seems to work, but we may have subtle memory issues I'm not 100% on top of yet. |
||
#11 | 13497 | tjuricek | Follow QObject memory management conventions for the PhoenixServicesClient. | ||
#10 | 13471 | tjuricek |
Define some JSON messages for Qt API's websocket interaction. Not tested... yet. |
||
#9 | 13470 | tjuricek |
Phoenix notification services, client API, including new phoenix_updater This is an interim commit containing a first pass implementation of the phoenix_updater. Notably missing parts: - The Qt API doesn't yet actually interact with the phoenix_updater - The phoenix_services web service doesn't filter out notifications I *may* end up creating another web application *just* to filter out notifications, since this may end up taking up a lot of background workers. |
||
#8 | 13460 | tjuricek |
Call out authentication problems with a specific application error code. This allows Qt clients to easily know when they should recreate a new session. |
||
#7 | 13459 | tjuricek |
Return the created project when creating a new project, since default values will often be filled out. Allow new projects to be created only with names set. We'll generate an ID at the moment, when I have better indexing, I'll double check for uniqueness. |
||
#6 | 13455 | tjuricek | Basic Phoenix project metadata (the project name) can now be created and read back. | ||
#5 | 13454 | tjuricek |
First cut at SDK implementation of the Create-Read lifecycle of Phoenix projects. (Bugs on the server side may be the issue, we'll see. TBD.) |
||
#4 | 13452 | tjuricek | Normalize header includes and preliminary implementation of the Project-related methods of the PhoenixServicesClient. | ||
#3 | 13451 | tjuricek | Setup common error handling callbacks for most of the first operations. | ||
#2 | 13449 | tjuricek | Adjusting the User-Agent string which gets rejected by Unicorn. | ||
#1 | 13443 | tjuricek |
Reorganized the Qt libraries into a separate part of the tree, to make it easier for CMake configuration. Added 'qt_build' and 'qt_test' tasks to at least execute tests and fail the build if they don't pass. Started the implementation of the project library interaction. Change: 1018651 Date: 3/4/15 11:43 AM Client: tjuricek_dhcp-141-n100_5959 User: tjuricek Status: pending Type: public Description: Reorganized the Qt libraries into a separate part of the tree, to make it easier for CMake configuration. Added 'qt_build' and 'qt_test' tasks to at least execute tests and fail the build if they don't pass. Started the implementation of the project library interaction. JobStatus: Jobs: Files: //web-services/p4ws-main/Rakefile //web-services/p4ws-main/p4_phoenix_services/clients/qt/p4_phoenix_services_client/CMakeLists.txt //web-services/p4ws-main/p4_phoenix_services/clients/qt/p4_phoenix_services_client/PhoenixIntegrationTests.cpp //web-services/p4ws-main/p4_phoenix_services/clients/qt/p4_phoenix_services_client/README //web-services/p4ws-main/p4_phoenix_services/clients/qt/p4_phoenix_services_client/phoenix.h //web-services/p4ws-main/p4_phoenix_services/clients/qt/p4_phoenix_services_client/phoenix/PhoenixProject.cpp //web-services/p4ws-main/p4_phoenix_services/clients/qt/p4_phoenix_services_client/phoenix/PhoenixProject.h //web-services/p4ws-main/p4_phoenix_services/clients/qt/p4_phoenix_services_client/phoenix/PhoenixServicesClient.cpp //web-services/p4ws-main/p4_phoenix_services/clients/qt/p4_phoenix_services_client/phoenix/PhoenixServicesClient.h //web-services/p4ws-main/p4_phoenix_services/clients/qt/p4_phoenix_services_client/phoenix/RequestError.cpp //web-services/p4ws-main/p4_phoenix_services/clients/qt/p4_phoenix_services_client/phoenix/RequestError.h //web-services/p4ws-main/p4_phoenix_services/clients/qt/p4_phoenix_services_client/phoenix/Session.cpp //web-services/p4ws-main/p4_phoenix_services/clients/qt/p4_phoenix_services_client/phoenix/Session.h //web-services/p4ws-main/p4_phoenix_services/clients/qt/p4_phoenix_services_client/test/PhoenixServicesClientTests.cpp //web-services/p4ws-main/p4_phoenix_services/clients/qt/p4_phoenix_services_client/test/PhoenixServicesClientTests.h //web-services/p4ws-main/p4_phoenix_services/clients/qt/p4_phoenix_services_client/test/SessionTests.cpp //web-services/p4ws-main/p4_phoenix_services/clients/qt/p4_phoenix_services_client/test/SessionTests.h //web-services/p4ws-main/p4_project_services/clients/qt/p4_project_services_client/CMakeLists.txt //web-services/p4ws-main/p4_project_services/clients/qt/p4_project_services_client/p4_project_services.h //web-services/p4ws-main/p4_project_services/clients/qt/p4_project_services_client/p4_project_services/Branch.cpp //web-services/p4ws-main/p4_project_services/clients/qt/p4_project_services_client/p4_project_services/Branch.h //web-services/p4ws-main/p4_project_services/clients/qt/p4_project_services_client/p4_project_services/Project.cpp //web-services/p4ws-main/p4_project_services/clients/qt/p4_project_services_client/p4_project_services/Project.h //web-services/p4ws-main/p4_project_services/clients/qt/p4_project_services_client/p4_project_services/View.cpp //web-services/p4ws-main/p4_project_services/clients/qt/p4_project_services_client/p4_project_services/View.h //web-services/p4ws-main/qt/CMakeLists.txt //web-services/p4ws-main/qt/p4_phoenix_services_client/CMakeLists.txt //web-services/p4ws-main/qt/p4_phoenix_services_client/PhoenixIntegrationTests.cpp //web-services/p4ws-main/qt/p4_phoenix_services_client/README //web-services/p4ws-main/qt/p4_phoenix_services_client/phoenix.h //web-services/p4ws-main/qt/p4_phoenix_services_client/phoenix/PhoenixProject.cpp //web-services/p4ws-main/qt/p4_phoenix_services_client/phoenix/PhoenixProject.h //web-services/p4ws-main/qt/p4_phoenix_services_client/phoenix/PhoenixServicesClient.cpp //web-services/p4ws-main/qt/p4_phoenix_services_client/phoenix/PhoenixServicesClient.h //web-services/p4ws-main/qt/p4_phoenix_services_client/phoenix/RequestError.cpp //web-services/p4ws-main/qt/p4_phoenix_services_client/phoenix/RequestError.h //web-services/p4ws-main/qt/p4_phoenix_services_client/phoenix/Session.cpp //web-services/p4ws-main/qt/p4_phoenix_services_client/phoenix/Session.h //web-services/p4ws-main/qt/p4_phoenix_services_client/test/PhoenixServicesClientTests.cpp //web-services/p4ws-main/qt/p4_phoenix_services_client/test/PhoenixServicesClientTests.h //web-services/p4ws-main/qt/p4_phoenix_services_client/test/SessionTests.cpp //web-services/p4ws-main/qt/p4_phoenix_services_client/test/SessionTests.h //web-services/p4ws-main/qt/p4_project_services_client/CMakeLists.txt //web-services/p4ws-main/qt/p4_project_services_client/build/CMakeCache.txt //web-services/p4ws-main/qt/p4_project_services_client/build/CMakeFiles/2.8.12/CMakeCCompiler.cmake //web-services/p4ws-main/qt/p4_project_services_client/build/CMakeFiles/2.8.12/CMakeCXXCompiler.cmake //web-services/p4ws-main/qt/p4_project_services_client/build/CMakeFiles/2.8.12/CMakeDetermineCompilerABI_C.bin //web-services/p4ws-main/qt/p4_project_services_client/build/CMakeFiles/2.8.12/CMakeDetermineCompilerABI_CXX.bin //web-services/p4ws-main/qt/p4_project_services_client/build/CMakeFiles/2.8.12/CMakeSystem.cmake //web-services/p4ws-main/qt/p4_project_services_client/build/CMakeFiles/2.8.12/CompilerIdC/CMakeCCompilerId.c //web-services/p4ws-main/qt/p4_project_services_client/build/CMakeFiles/2.8.12/CompilerIdC/a.out //web-services/p4ws-main/qt/p4_project_services_client/build/CMakeFiles/2.8.12/CompilerIdCXX/CMakeCXXCompilerId.cpp //web-services/p4ws-main/qt/p4_project_services_client/build/CMakeFiles/2.8.12/CompilerIdCXX/a.out //web-services/p4ws-main/qt/p4_project_services_client/build/CMakeFiles/CMakeDirectoryInformation.cmake //web-services/p4ws-main/qt/p4_project_services_client/build/CMakeFiles/CMakeOutput.log //web-services/p4ws-main/qt/p4_project_services_client/build/CMakeFiles/Makefile.cmake //web-services/p4ws-main/qt/p4_project_services_client/build/CMakeFiles/Makefile2 //web-services/p4ws-main/qt/p4_project_services_client/build/CMakeFiles/TargetDirectories.txt //web-services/p4ws-main/qt/p4_project_services_client/build/CMakeFiles/cmake.check_cache //web-services/p4ws-main/qt/p4_project_services_client/build/CMakeFiles/p4_project_services_client.dir/DependInfo.cmake //web-services/p4ws-main/qt/p4_project_services_client/build/CMakeFiles/p4_project_services_client.dir/build.make //web-services/p4ws-main/qt/p4_project_services_client/build/CMakeFiles/p4_project_services_client.dir/cmake_clean.cmake //web-services/p4ws-main/qt/p4_project_services_client/build/CMakeFiles/p4_project_services_client.dir/cmake_clean_target.cmake //web-services/p4ws-main/qt/p4_project_services_client/build/CMakeFiles/p4_project_services_client.dir/depend.make //web-services/p4ws-main/qt/p4_project_services_client/build/CMakeFiles/p4_project_services_client.dir/flags.make //web-services/p4ws-main/qt/p4_project_services_client/build/CMakeFiles/p4_project_services_client.dir/link.txt //web-services/p4ws-main/qt/p4_project_services_client/build/CMakeFiles/p4_project_services_client.dir/progress.make //web-services/p4ws-main/qt/p4_project_services_client/build/CMakeFiles/progress.marks //web-services/p4ws-main/qt/p4_project_services_client/build/Makefile //web-services/p4ws-main/qt/p4_project_services_client/build/cmake_install.cmake //web-services/p4ws-main/qt/p4_project_services_client/p4_project_services.h //web-services/p4ws-main/qt/p4_project_services_client/p4_project_services/Branch.cpp //web-services/p4ws-main/qt/p4_project_services_client/p4_project_services/Branch.h //web-services/p4ws-main/qt/p4_project_services_client/p4_project_services/Project.cpp //web-services/p4ws-main/qt/p4_project_services_client/p4_project_services/Project.h //web-services/p4ws-main/qt/p4_project_services_client/p4_project_services/View.cpp //web-services/p4ws-main/qt/p4_project_services_client/p4_project_services/View.h |