forked from qt-creator/qt-creator
Change-Id: Idfb4faf1cbfbfd6c2914b057e6c76461de0ceeff Reviewed-by: David Schulz <david.schulz@qt.io> Reviewed-by: Christian Stenger <christian.stenger@qt.io>
289 lines
9.8 KiB
C++
289 lines
9.8 KiB
C++
/****************************************************************************
|
|
**
|
|
** Copyright (C) 2022 The Qt Company Ltd.
|
|
** Contact: https://www.qt.io/licensing/
|
|
**
|
|
** This file is part of Qt Creator.
|
|
**
|
|
** Commercial License Usage
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
** accordance with the commercial license agreement provided with the
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
** information use the contact form at https://www.qt.io/contact-us.
|
|
**
|
|
** GNU General Public License Usage
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
** General Public License version 3 as published by the Free Software
|
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
|
** included in the packaging of this file. Please review the following
|
|
** information to ensure the GNU General Public License requirements will
|
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
|
**
|
|
****************************************************************************/
|
|
|
|
#include "resultparser.h"
|
|
|
|
#include <QJsonArray>
|
|
#include <QJsonDocument>
|
|
#include <QJsonObject>
|
|
|
|
#include <utility>
|
|
|
|
namespace GitLab {
|
|
|
|
|
|
QString Event::toMessage() const
|
|
{
|
|
QString message;
|
|
if (author.realname.isEmpty())
|
|
message.append(author.name);
|
|
else
|
|
message.append(author.realname + " (" + author.name + ')');
|
|
message.append(' ');
|
|
if (!pushData.isEmpty())
|
|
message.append(pushData);
|
|
else if (!targetTitle.isEmpty())
|
|
message.append(action + ' ' + targetType + " '" + targetTitle +'\'');
|
|
else
|
|
message.append(action + ' ' + targetType);
|
|
return message;
|
|
}
|
|
|
|
namespace ResultParser {
|
|
|
|
static PageInformation paginationInformation(const QByteArray &header)
|
|
{
|
|
PageInformation result;
|
|
const QByteArrayList lines = header.split('\n');
|
|
for (const QByteArray &line : lines) {
|
|
const QByteArray lower = line.toLower(); // depending on OS this may be capitalized
|
|
if (lower.startsWith("x-page: "))
|
|
result.currentPage = line.mid(8).toInt();
|
|
else if (lower.startsWith("x-per-page: "))
|
|
result.perPage = line.mid(12).toInt();
|
|
else if (lower.startsWith("x-total: "))
|
|
result.total = line.mid(9).toInt();
|
|
else if (lower.startsWith("x-total-pages: "))
|
|
result.totalPages = line.mid(15).toInt();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static std::pair<QByteArray, QByteArray> splitHeaderAndBody(const QByteArray &input)
|
|
{
|
|
QByteArray header;
|
|
QByteArray json;
|
|
int emptyLine = input.indexOf("\r\n\r\n"); // we always get \r\n as line separator?
|
|
if (emptyLine != -1) {
|
|
header = input.left(emptyLine);
|
|
json = input.mid(emptyLine + 4);
|
|
} else {
|
|
json = input;
|
|
}
|
|
return std::make_pair(header, json);
|
|
}
|
|
|
|
static std::pair<Error, QJsonObject> preHandleSingle(const QByteArray &json)
|
|
{
|
|
Error result;
|
|
QJsonObject object;
|
|
QJsonParseError error;
|
|
const QJsonDocument doc = QJsonDocument::fromJson(json, &error);
|
|
|
|
if (error.error != QJsonParseError::NoError) {
|
|
if (!json.isEmpty() && json.at(0) == '<') // we likely got an HTML response
|
|
result.code = 399;
|
|
result.message = error.errorString();
|
|
} else if (!doc.isObject()) {
|
|
result.message = "Not an Object";
|
|
} else {
|
|
object = doc.object();
|
|
if (object.contains("message")) {
|
|
result = parseErrorMessage(object.value("message").toString());
|
|
} else if (object.contains("error")) {
|
|
if (object.value("error").toString() == "insufficient_scope")
|
|
result.code = 1;
|
|
result.message = object.value("error_description").toString();
|
|
}
|
|
}
|
|
|
|
return std::make_pair(result, object);
|
|
}
|
|
|
|
static std::pair<Error, QJsonDocument> preHandleHeaderAndBody(const QByteArray &header,
|
|
const QByteArray &json)
|
|
{
|
|
Error result;
|
|
if (header.isEmpty()) {
|
|
result.message = "Missing Expected Header";
|
|
return std::make_pair(result, QJsonDocument());
|
|
}
|
|
|
|
QJsonParseError error;
|
|
const QJsonDocument doc = QJsonDocument::fromJson(json, &error);
|
|
if (error.error != QJsonParseError::NoError) {
|
|
result.message = error.errorString();
|
|
return std::make_pair(result, doc);
|
|
}
|
|
|
|
if (doc.isObject()) {
|
|
const QJsonObject obj = doc.object();
|
|
if (obj.contains("message")) {
|
|
result = parseErrorMessage(obj.value("message").toString());
|
|
return std::make_pair(result, doc);
|
|
} else if (obj.contains("error")) {
|
|
if (obj.value("error").toString() == "insufficient_scope")
|
|
result.code = 1;
|
|
result.message = obj.value("error_description").toString();
|
|
return std::make_pair(result, doc);
|
|
}
|
|
}
|
|
|
|
if (!doc.isArray())
|
|
result.message = "Not an Array";
|
|
|
|
return std::make_pair(result, doc);
|
|
}
|
|
|
|
static User userFromJson(const QJsonObject &jsonObj)
|
|
{
|
|
User user;
|
|
user.name = jsonObj.value("username").toString();
|
|
user.realname = jsonObj.value("name").toString();
|
|
user.id = jsonObj.value("id").toInt(-1);
|
|
user.email = jsonObj.value("email").toString();
|
|
user.lastLogin = jsonObj.value("last_sign_in_at").toString();
|
|
user.bot = jsonObj.value("bot").toBool();
|
|
return user;
|
|
}
|
|
|
|
static Project projectFromJson(const QJsonObject &jsonObj)
|
|
{
|
|
Project project;
|
|
project.name = jsonObj.value("name").toString();
|
|
project.displayName = jsonObj.value("name_with_namespace").toString();
|
|
project.pathName = jsonObj.value("path_with_namespace").toString();
|
|
project.id = jsonObj.value("id").toInt(-1);
|
|
project.visibility = jsonObj.value("visibility").toString("public");
|
|
project.httpUrl = jsonObj.value("http_url_to_repo").toString();
|
|
project.sshUrl = jsonObj.value("ssh_url_to_repo").toString();
|
|
if (jsonObj.contains("forks_count"))
|
|
project.forkCount = jsonObj.value("forks_count").toInt();
|
|
if (jsonObj.contains("star_count"))
|
|
project.starCount = jsonObj.value("star_count").toInt();
|
|
if (jsonObj.contains("open_issues_count"))
|
|
project.issuesCount = jsonObj.value("open_issues_count").toInt();
|
|
const QJsonObject permissions = jsonObj.value("permissions").toObject();
|
|
if (!permissions.isEmpty()) { // separate permissions obj?
|
|
const QJsonObject projAccObj = permissions.value("project_access").toObject();
|
|
if (!projAccObj.isEmpty())
|
|
project.accessLevel = projAccObj.value("access_level").toInt(-1);
|
|
}
|
|
return project;
|
|
}
|
|
|
|
static Event eventFromJson(const QJsonObject &jsonObj)
|
|
{
|
|
Event event;
|
|
event.action = jsonObj.value("action_name").toString();
|
|
const QJsonValue value = jsonObj.value("target_type");
|
|
event.targetType = value.isNull() ? "project" : jsonObj.value("target_type").toString();
|
|
if (event.targetType == "DiffNote") {
|
|
const QJsonObject noteObject = jsonObj.value("note").toObject();
|
|
event.targetType = noteObject.value("noteable_type").toString();
|
|
}
|
|
event.targetTitle = jsonObj.value("target_title").toString();
|
|
event.author = userFromJson(jsonObj.value("author").toObject());
|
|
event.timeStamp = jsonObj.value("created_at").toString();
|
|
if (jsonObj.contains("push_data")) {
|
|
const QJsonObject pushDataObj = jsonObj.value("push_data").toObject();
|
|
if (!pushDataObj.isEmpty()) {
|
|
const QString action = pushDataObj.value("action").toString();
|
|
const QString ref = pushDataObj.value("ref").toString();
|
|
const QString refType = pushDataObj.value("ref_type").toString();
|
|
event.pushData = action + ' ' + refType + " '" + ref + '\'';
|
|
}
|
|
}
|
|
return event;
|
|
}
|
|
|
|
User parseUser(const QByteArray &input)
|
|
{
|
|
auto [error, userObj] = preHandleSingle(input);
|
|
if (!error.message.isEmpty()) {
|
|
User result;
|
|
result.error = error;
|
|
return result;
|
|
}
|
|
return userFromJson(userObj);
|
|
}
|
|
|
|
Project parseProject(const QByteArray &input)
|
|
{
|
|
auto [error, projectObj] = preHandleSingle(input);
|
|
if (!error.message.isEmpty()) {
|
|
Project result;
|
|
result.error = error;
|
|
return result;
|
|
}
|
|
return projectFromJson(projectObj);
|
|
}
|
|
|
|
Projects parseProjects(const QByteArray &input)
|
|
{
|
|
auto [header, json] = splitHeaderAndBody(input);
|
|
auto [error, jsonDoc] = preHandleHeaderAndBody(header, json);
|
|
Projects result;
|
|
if (!error.message.isEmpty()) {
|
|
result.error = error;
|
|
return result;
|
|
}
|
|
result.pageInfo = paginationInformation(header);
|
|
const QJsonArray projectsArray = jsonDoc.array();
|
|
for (const QJsonValue &value : projectsArray) {
|
|
if (!value.isObject())
|
|
continue;
|
|
const QJsonObject projectObj = value.toObject();
|
|
result.projects.append(projectFromJson(projectObj));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
Events parseEvents(const QByteArray &input)
|
|
{
|
|
auto [header, json] = splitHeaderAndBody(input);
|
|
auto [error, jsonDoc] = preHandleHeaderAndBody(header, json);
|
|
Events result;
|
|
if (!error.message.isEmpty()) {
|
|
result.error = error;
|
|
return result;
|
|
}
|
|
result.pageInfo = paginationInformation(header);
|
|
const QJsonArray eventsArray = jsonDoc.array();
|
|
for (const QJsonValue &value : eventsArray) {
|
|
if (!value.isObject())
|
|
continue;
|
|
const QJsonObject eventObj = value.toObject();
|
|
result.events.append(eventFromJson(eventObj));
|
|
}
|
|
return result;
|
|
|
|
}
|
|
|
|
Error parseErrorMessage(const QString &message)
|
|
{
|
|
Error error;
|
|
bool ok = false;
|
|
error.code = message.left(3).toInt(&ok);
|
|
if (ok)
|
|
error.message = message.mid(4);
|
|
else
|
|
error.message = "Internal Parse Error";
|
|
return error;
|
|
}
|
|
|
|
} // namespace ResultParser
|
|
} // namespace GitLab
|