Qt6 Migration: Merge latest Qt5 devel content and apply migration fixes
- Merged 292 changed files from dlbo snapshot - Fixed IDL enum collisions (PortType, PdmEventType) - Fixed Grantlee template for C++11 scoped enums - Fixed CMake VERSION_GREATER syntax - Added missing directories (THP, LtsRestClient, etc.) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
310 files changed
+2823
-740
No files matched your search
@@ -0,0 +1,38 @@
|
||||
#
|
||||
# Tool for sending http-requests to a REST API
|
||||
# $Id$
|
||||
# $HeadURL$
|
||||
# $Author$
|
||||
# Created by Hannes Fritsche, ESZS, 2026-08-25
|
||||
#
|
||||
|
||||
SET(SRCS
|
||||
main_window.cpp
|
||||
main_window.h
|
||||
main.cpp
|
||||
)
|
||||
|
||||
NGL_WRAP_UI(SRCS main_window.ui)
|
||||
|
||||
NGL_ADD_EXECUTABLE(TOOLSWS RestClientUi ${SRCS})
|
||||
|
||||
NGL_INSTALL(WS DIRECTORY rest_templates/ DESTINATION bin/updates/)
|
||||
|
||||
NGL_TARGET_LINK_LIBRARIES(RestClientUi
|
||||
Qt5::Widgets
|
||||
Qt5::Gui
|
||||
LtsRestClient
|
||||
)
|
||||
|
||||
NGL_INSTALL_TARGET(RestClientUi)
|
||||
|
||||
IF (BUILD_TOOLSWS)
|
||||
SET(TEMPLATES_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/rest_templates")
|
||||
SET(TEMPLATES_DESTINATION "${CMAKE_BINARY_DIR}/bin/rest_templates")
|
||||
|
||||
ADD_CUSTOM_COMMAND(
|
||||
TARGET RestClientUi POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory "${TEMPLATES_SOURCE}" "${TEMPLATES_DESTINATION}"
|
||||
COMMENT "Copy ${TEMPLATES_SOURCE} to ${TEMPLATES_DESTINATION}"
|
||||
)
|
||||
ENDIF()
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* main for RestClientUi
|
||||
* $Id$
|
||||
* $HeadURL$
|
||||
* $Author$
|
||||
* Created by Hannes Fritsche, ESZS, 2026-08-25
|
||||
*/
|
||||
|
||||
#include <QApplication>
|
||||
#include "main_window.h"
|
||||
|
||||
// Documentation on how to use it can be found at https://confluence-classified.rh-int.de/spaces/LEG/pages/207919552/HOWTO+RestClientUi+Tool
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication app(argc, argv);
|
||||
|
||||
MainWindow window;
|
||||
window.show();
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
#include "main_window.h"
|
||||
#include "LtsRestClient/lts_rest_client.h"
|
||||
#include "ui_main_window.h"
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileDialog>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonParseError>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
|
||||
// Documentation on how to use it can be found at https://confluence-classified.rh-int.de/spaces/LEG/pages/207919552/HOWTO+RestClientUi+Tool
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent)
|
||||
: QMainWindow(parent)
|
||||
, ui(new Ui::MainWindow)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
// Connect core HTTP actions
|
||||
connect(ui->m_getBtn, &QPushButton::clicked, this, &MainWindow::onGetClicked);
|
||||
connect(ui->m_postBtn, &QPushButton::clicked, this, &MainWindow::onPostClicked);
|
||||
connect(ui->m_putBtn, &QPushButton::clicked, this, &MainWindow::onPutClicked);
|
||||
connect(ui->m_deleteBtn, &QPushButton::clicked, this, &MainWindow::onDeleteClicked);
|
||||
|
||||
// Connect logging output actions
|
||||
connect(ui->m_clearResponseBtn, &QPushButton::clicked, this, &MainWindow::onClearResponseClicked);
|
||||
connect(ui->m_exportResponseBtn, &QPushButton::clicked, this, &MainWindow::onExportResponseClicked);
|
||||
|
||||
loadRequestTemplates();
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void MainWindow::onClearResponseClicked()
|
||||
{
|
||||
ui->m_responseOutput->clear();
|
||||
}
|
||||
|
||||
void MainWindow::onExportResponseClicked()
|
||||
{
|
||||
const QString content = ui->m_responseOutput->toPlainText().trimmed();
|
||||
|
||||
if (content.isEmpty())
|
||||
{
|
||||
QMessageBox::warning(this, QStringLiteral("Export Failed"), QStringLiteral("There is no response data available to export."));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const QString fileName = QFileDialog::getSaveFileName(this,
|
||||
QStringLiteral("Save Response as JSON"),
|
||||
QDir::homePath() + QStringLiteral("/response.json"),
|
||||
QStringLiteral("JSON Files (*.json);;All Files (*)"));
|
||||
|
||||
if (fileName.isEmpty())
|
||||
return;
|
||||
|
||||
QJsonParseError parseError;
|
||||
const QJsonDocument jsonDoc = QJsonDocument::fromJson(content.toUtf8(), &parseError);
|
||||
QByteArray dataToWrite;
|
||||
|
||||
if (parseError.error == QJsonParseError::NoError)
|
||||
dataToWrite = jsonDoc.toJson(QJsonDocument::Indented);
|
||||
else
|
||||
dataToWrite = content.toUtf8();
|
||||
|
||||
QFile file(fileName);
|
||||
|
||||
if (file.open(QIODevice::WriteOnly | QIODevice::Text))
|
||||
{
|
||||
file.write(dataToWrite);
|
||||
file.close();
|
||||
|
||||
QMessageBox::information(this, QStringLiteral("Success"), QStringLiteral("File saved successfully."));
|
||||
}
|
||||
else
|
||||
QMessageBox::critical(this, QStringLiteral("Error"), QStringLiteral("Could not write to file:\n") + file.errorString());
|
||||
}
|
||||
|
||||
void MainWindow::highlightMethodButton(const QString &method)
|
||||
{
|
||||
// Reset stylesheets for all HTTP method buttons back to default
|
||||
ui->m_getBtn->setStyleSheet(QString());
|
||||
ui->m_postBtn->setStyleSheet(QString());
|
||||
ui->m_putBtn->setStyleSheet(QString());
|
||||
ui->m_deleteBtn->setStyleSheet(QString());
|
||||
|
||||
// Modern green border highlight style string
|
||||
const QString highlightStyle = QStringLiteral("border: 2px solid #2ecc71; font-weight: bold; background-color: #e8f8f5;");
|
||||
|
||||
// Apply style based on the uppercase method string
|
||||
const QString upperMethod = method.toUpper().trimmed();
|
||||
if (upperMethod == QStringLiteral("GET"))
|
||||
ui->m_getBtn->setStyleSheet(highlightStyle);
|
||||
else if (upperMethod == QStringLiteral("POST"))
|
||||
ui->m_postBtn->setStyleSheet(highlightStyle);
|
||||
else if (upperMethod == QStringLiteral("PUT"))
|
||||
ui->m_putBtn->setStyleSheet(highlightStyle);
|
||||
else if (upperMethod == QStringLiteral("DELETE")
|
||||
|| upperMethod == QStringLiteral("DEL"))
|
||||
ui->m_deleteBtn->setStyleSheet(highlightStyle);
|
||||
}
|
||||
|
||||
void MainWindow::loadRequestTemplates()
|
||||
{
|
||||
const QString templatePath = QCoreApplication::applicationDirPath() + QStringLiteral("/templates");
|
||||
QDir directory(templatePath);
|
||||
|
||||
if (!directory.exists())
|
||||
directory.mkpath(QStringLiteral("."));
|
||||
|
||||
QStringList filters;
|
||||
filters << QStringLiteral("*.json");
|
||||
directory.setNameFilters(filters);
|
||||
|
||||
const QFileInfoList fileList = directory.entryInfoList(QDir::Files);
|
||||
|
||||
int insertIndex = 0;
|
||||
for (const QFileInfo &fileInfo : fileList)
|
||||
{
|
||||
const QString absolutePath = fileInfo.absoluteFilePath();
|
||||
|
||||
QFile file(absolutePath);
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const QByteArray fileContent = file.readAll();
|
||||
file.close();
|
||||
|
||||
QJsonParseError parseError;
|
||||
const QJsonDocument metaDoc = QJsonDocument::fromJson(fileContent, &parseError);
|
||||
|
||||
if (parseError.error != QJsonParseError::NoError
|
||||
|| !metaDoc.isObject())
|
||||
continue;
|
||||
|
||||
const QJsonObject metaObj = metaDoc.object();
|
||||
|
||||
QString displayName = metaObj.value(QStringLiteral("name")).toString();
|
||||
if (displayName.isEmpty())
|
||||
displayName = fileInfo.fileName();
|
||||
|
||||
const QString targetEndpoint = metaObj.value(QStringLiteral("endpoint")).toString();
|
||||
const QString targetMethod = metaObj.value(QStringLiteral("method")).toString().toUpper().trimmed();
|
||||
|
||||
QPushButton *templateBtn = new QPushButton(displayName, this);
|
||||
|
||||
connect(templateBtn, &QPushButton::clicked, this, [this, metaObj, targetEndpoint, targetMethod]()
|
||||
{
|
||||
// 1. Fill values into fields
|
||||
ui->m_endpointInput->setText(targetEndpoint);
|
||||
ui->m_jsonInput->clear();
|
||||
|
||||
if (metaObj.contains(QStringLiteral("payload")))
|
||||
{
|
||||
const QJsonValue payloadValue = metaObj.value(QStringLiteral("payload"));
|
||||
|
||||
if (payloadValue.isObject())
|
||||
{
|
||||
const QJsonDocument doc(payloadValue.toObject());
|
||||
ui->m_jsonInput->setPlainText(QString::fromUtf8(doc.toJson(QJsonDocument::Indented)));
|
||||
}
|
||||
else if (payloadValue.isArray())
|
||||
{
|
||||
const QJsonDocument doc(payloadValue.toArray());
|
||||
ui->m_jsonInput->setPlainText(QString::fromUtf8(doc.toJson(QJsonDocument::Indented)));
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->m_jsonInput->setPlainText(payloadValue.toVariant().toString());
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Visually focus the matching HTTP button
|
||||
highlightMethodButton(targetMethod);
|
||||
|
||||
// 3. NEW: Optional Auto-Submit trigger chain execution
|
||||
if (ui->m_autoSubmitCheck->isChecked())
|
||||
{
|
||||
if (targetMethod == QStringLiteral("GET"))
|
||||
ui->m_getBtn->animateClick(); // animateClick provides nice visual feedback
|
||||
else if (targetMethod == QStringLiteral("POST"))
|
||||
ui->m_postBtn->animateClick();
|
||||
else if (targetMethod == QStringLiteral("PUT"))
|
||||
ui->m_putBtn->animateClick();
|
||||
else if (targetMethod == QStringLiteral("DELETE")
|
||||
|| targetMethod == QStringLiteral("DEL"))
|
||||
ui->m_deleteBtn->animateClick();
|
||||
}
|
||||
});
|
||||
|
||||
ui->templateButtonContainer->insertWidget(insertIndex++, templateBtn);
|
||||
}
|
||||
}
|
||||
|
||||
LtsCommon::LtsRestClient *MainWindow::createClient()
|
||||
{
|
||||
return new LtsCommon::LtsRestClient(ui->m_urlInput->text(), this);
|
||||
}
|
||||
|
||||
void MainWindow::showSuccess(const QJsonDocument &json)
|
||||
{
|
||||
ui->m_responseOutput->setTextColor(QColor(Qt::darkGreen));
|
||||
ui->m_responseOutput->setText(QString::fromLatin1(json.toJson(QJsonDocument::Indented)));
|
||||
}
|
||||
|
||||
void MainWindow::showError(const QString &error)
|
||||
{
|
||||
ui->m_responseOutput->setTextColor(QColor(Qt::red));
|
||||
ui->m_responseOutput->setText(QStringLiteral("Error: ") + error);
|
||||
}
|
||||
|
||||
void MainWindow::onGetClicked()
|
||||
{
|
||||
ui->m_responseOutput->clear();
|
||||
LtsCommon::LtsRestClient *client = createClient();
|
||||
client->get(
|
||||
ui->m_endpointInput->text(),
|
||||
[this](const QJsonDocument &json)
|
||||
{
|
||||
showSuccess(json);
|
||||
},
|
||||
[this](const QString &err)
|
||||
{
|
||||
showError(err);
|
||||
});
|
||||
}
|
||||
|
||||
void MainWindow::onPostClicked()
|
||||
{
|
||||
ui->m_responseOutput->clear();
|
||||
QJsonParseError parseError;
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(ui->m_jsonInput->toPlainText().toUtf8(), &parseError);
|
||||
|
||||
if (parseError.error != QJsonParseError::NoError)
|
||||
{
|
||||
showError(QStringLiteral("Invalid JSON input syntax: ") + parseError.errorString());
|
||||
return;
|
||||
}
|
||||
|
||||
LtsCommon::LtsRestClient *client = createClient();
|
||||
client->post(
|
||||
ui->m_endpointInput->text(), jsonDoc,
|
||||
[this](const QJsonDocument &json)
|
||||
{
|
||||
showSuccess(json);
|
||||
},
|
||||
[this](const QString &err)
|
||||
{
|
||||
showError(err);
|
||||
});
|
||||
}
|
||||
|
||||
void MainWindow::onPutClicked()
|
||||
{
|
||||
ui->m_responseOutput->clear();
|
||||
QJsonParseError parseError;
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(ui->m_jsonInput->toPlainText().toUtf8(), &parseError);
|
||||
|
||||
if (parseError.error != QJsonParseError::NoError)
|
||||
{
|
||||
showError(QStringLiteral("Invalid JSON input syntax: ") + parseError.errorString());
|
||||
return;
|
||||
}
|
||||
|
||||
LtsCommon::LtsRestClient *client = createClient();
|
||||
client->put(
|
||||
ui->m_endpointInput->text(), jsonDoc,
|
||||
[this](const QJsonDocument &json)
|
||||
{
|
||||
showSuccess(json);
|
||||
},
|
||||
[this](const QString &err)
|
||||
{
|
||||
showError(err);
|
||||
});
|
||||
}
|
||||
|
||||
void MainWindow::onDeleteClicked()
|
||||
{
|
||||
ui->m_responseOutput->clear();
|
||||
LtsCommon::LtsRestClient *client = createClient();
|
||||
client->del(
|
||||
ui->m_endpointInput->text(),
|
||||
[this](const QJsonDocument &json)
|
||||
{
|
||||
showSuccess(json);
|
||||
},
|
||||
[this](const QString &err)
|
||||
{
|
||||
showError(err);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include "LtsRestClient/lts_rest_client.h"
|
||||
#include <QJsonDocument>
|
||||
#include <QMainWindow>
|
||||
|
||||
// Documentation on how to use it can be found at https://confluence-classified.rh-int.de/spaces/LEG/pages/207919552/HOWTO+RestClientUi+Tool
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class MainWindow;
|
||||
}
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MainWindow(QWidget *parent = nullptr);
|
||||
~MainWindow();
|
||||
|
||||
private slots:
|
||||
void onGetClicked();
|
||||
void onPostClicked();
|
||||
void onPutClicked();
|
||||
void onDeleteClicked();
|
||||
void onClearResponseClicked();
|
||||
void onExportResponseClicked();
|
||||
|
||||
private:
|
||||
void loadRequestTemplates();
|
||||
LtsCommon::LtsRestClient *createClient();
|
||||
void showSuccess(const QJsonDocument &json);
|
||||
void showError(const QString &error);
|
||||
void highlightMethodButton(const QString &method);
|
||||
|
||||
Ui::MainWindow *ui;
|
||||
}; // class MainWindow
|
||||
@@ -0,0 +1,215 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>750</width>
|
||||
<height>580</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Qt REST Client GUI</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QFrame" name="leftFrame">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>180</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>220</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="leftLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="labelTemplates">
|
||||
<property name="font">
|
||||
<font>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>JSON Templates:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="scrollArea">
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>200</width>
|
||||
<height>520</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="templateButtonContainer">
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="mainVerticalLayout">
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="labelUrl">
|
||||
<property name="text">
|
||||
<string>Basis URL:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="m_urlInput">
|
||||
<property name="text">
|
||||
<string>http://localhost:8080</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="labelEndpoint">
|
||||
<property name="text">
|
||||
<string>Endpunkt:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="m_endpointInput">
|
||||
<property name="text">
|
||||
<string>/posts</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="labelJson">
|
||||
<property name="text">
|
||||
<string>JSON Input (for POST / PUT):</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextEdit" name="m_jsonInput"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="m_autoSubmitCheck">
|
||||
<property name="text">
|
||||
<string>Submit automatically on template click</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="buttonLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="m_getBtn">
|
||||
<property name="text">
|
||||
<string>GET</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="m_postBtn">
|
||||
<property name="text">
|
||||
<string>POST</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="m_putBtn">
|
||||
<property name="text">
|
||||
<string>PUT</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="m_deleteBtn">
|
||||
<property name="text">
|
||||
<string>DELETE</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="labelResponse">
|
||||
<property name="text">
|
||||
<string>Server Response / Error Log:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextEdit" name="m_responseOutput">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="responseActionLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="m_clearResponseBtn">
|
||||
<property name="text">
|
||||
<string>Clear Log</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="m_exportResponseBtn">
|
||||
<property name="text">
|
||||
<string>Export as JSON...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "itemsKeys",
|
||||
"method": "GET",
|
||||
"endpoint": "/uncat/itemsKeys"
|
||||
}
|
||||
Reference in new issue
Block a user