mirror of
https://gitlab.com/spaceti-app/integrations/common/server-sdk.git
synced 2026-07-12 19:20:42 +02:00
Merge branch 'feature/soap-test' into 'master'
Feature/soap test See merge request spaceti-app/integrations/common/server-sdk!3
This commit is contained in:
commit
ff01168967
@ -25,7 +25,7 @@ win32:CONFIG(release, debug|release): LIBS += -L$$SDKDIR/release/ -lserversdk
|
||||
else:win32:CONFIG(debug, debug|release): LIBS += -L$$SDKDIR/debug/ -lserversdk
|
||||
else:unix: LIBS += -L$$SDKDIR/ -lserversdk -lz
|
||||
|
||||
|
||||
QT += network
|
||||
INCLUDEPATH += $$PWD/src/
|
||||
DEPENDPATH += $$PWD/src/serversdk
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
|
||||
Q_LOGGING_CATEGORY(sdkcore, "sdk.core");
|
||||
Q_LOGGING_CATEGORY(sdknetwork, "sdk.network");
|
||||
Q_LOGGING_CATEGORY(sdkcompress, "sdk.compress");
|
||||
Q_LOGGING_CATEGORY(sdkutils, "sdk.utils");
|
||||
|
||||
namespace serversdk {
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(sdkcore)
|
||||
Q_DECLARE_LOGGING_CATEGORY(sdknetwork)
|
||||
Q_DECLARE_LOGGING_CATEGORY(sdkcompress)
|
||||
Q_DECLARE_LOGGING_CATEGORY(sdkutils)
|
||||
|
||||
namespace serversdk {
|
||||
|
||||
|
||||
@ -28,7 +28,7 @@ QNetworkRequest Request::networkRequest() const
|
||||
request.setRawHeader("Accept-Encoding", "gzip, deflate");
|
||||
#endif
|
||||
|
||||
// Enabling HTTP/2 ( not supported on Ubuntu 16.04 )
|
||||
// Enabling HTTP/2 ( does not work on Qt 5.9.8 )
|
||||
// request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, true);
|
||||
return request;
|
||||
}
|
||||
|
||||
@ -24,8 +24,6 @@ public:
|
||||
QString url() const;
|
||||
void setUrl(const QString &url);
|
||||
|
||||
|
||||
|
||||
// Authorization
|
||||
QString userName() const;
|
||||
void setUserName(const QString &userName);
|
||||
@ -61,4 +59,6 @@ private:
|
||||
QByteArray mResponseData;
|
||||
};
|
||||
|
||||
|
||||
} // core
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@ RequestManager::RequestManager(QObject *parent)
|
||||
|
||||
|
||||
/**
|
||||
* @brief Soap::downloadRequest
|
||||
* @brief Soap::processRequest
|
||||
* @param soapRequest
|
||||
*/
|
||||
void RequestManager::processRequest(const std::shared_ptr<Request>& request)
|
||||
|
||||
@ -12,7 +12,7 @@ SoapRequest::SoapRequest(QString action, QString endpoint, QByteArray data)
|
||||
QNetworkRequest SoapRequest::networkRequest() const
|
||||
{
|
||||
Q_ASSERT(!this->url().isEmpty());
|
||||
QNetworkRequest request(QUrl(this->url()));
|
||||
QNetworkRequest request = Request::networkRequest();
|
||||
|
||||
// Have to set header
|
||||
QString header;
|
||||
@ -32,9 +32,6 @@ QNetworkRequest SoapRequest::networkRequest() const
|
||||
// Enabled compression
|
||||
request.setRawHeader("Accept-Encoding", "gzip, deflate");
|
||||
|
||||
// Enabling HTTP/2 ( not supported on Ubuntu 16.04 )
|
||||
// request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, true);
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@ namespace serversdk {
|
||||
|
||||
class SoapRequest : public Request
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SoapRequest(QString action, QString endpoint, QByteArray data);
|
||||
QNetworkRequest networkRequest() const override;
|
||||
|
||||
@ -68,7 +68,7 @@ QByteArray Utils::decompress(const QByteArray &compressed)
|
||||
float ratio = (float)compressed.count() / (float)result.count();
|
||||
float compression = ratio * 100.0;
|
||||
|
||||
qCInfo(sdkcompress) << "compress data:" << compressed.count()
|
||||
qCInfo(sdkutils) << "compress data:" << compressed.count()
|
||||
<< "uncompressed data:" << result.count()
|
||||
<< "compression:" << compression;
|
||||
|
||||
@ -97,7 +97,7 @@ QByteArray Utils::sourceData(const QString &subpath)
|
||||
// Get data from file
|
||||
QString filePath = sourcePath(subpath);
|
||||
if (! QFileInfo::exists(filePath)) {
|
||||
qCWarning(sdkcompress) << "path does not exists:" << filePath;
|
||||
qCWarning(sdkutils) << "path does not exists:" << filePath;
|
||||
return QByteArray();
|
||||
}
|
||||
QFile file(filePath);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
TEMPLATE = subdirs
|
||||
|
||||
SUBDIRS += \
|
||||
basic \
|
||||
unittests \
|
||||
soap \
|
||||
request \
|
||||
utils
|
||||
|
||||
@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) Filip Bucek - All Rights Reserved
|
||||
* Unauthorized copying of this file, via any medium is strictly prohibited
|
||||
* Proprietary and confidential
|
||||
* Written by Filip Bucek <fbucek@invloop.cz>, 2019
|
||||
*/
|
||||
#include <QtTest>
|
||||
|
||||
// add necessary includes here
|
||||
|
||||
class basicTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
basicTest();
|
||||
~basicTest();
|
||||
|
||||
private slots:
|
||||
void test_case1();
|
||||
|
||||
};
|
||||
|
||||
basicTest::basicTest()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
basicTest::~basicTest()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void basicTest::test_case1()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QTEST_APPLESS_MAIN(basicTest)
|
||||
|
||||
#include "tst_basictest.moc"
|
||||
@ -3,7 +3,6 @@ CONFIG += testcase # neccesary to have posibility to run: make check UnitTest
|
||||
|
||||
SOURCES += \
|
||||
tst_request.cpp
|
||||
HEADERS += \
|
||||
tst_request.h
|
||||
|
||||
|
||||
include(../../../serversdkint.pri)
|
||||
@ -1,9 +1,16 @@
|
||||
#include "tst_request.h"
|
||||
#include <QtTest>
|
||||
|
||||
#include "serversdk/request.h"
|
||||
|
||||
namespace serversdk {
|
||||
|
||||
class RequestTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
Q_SLOT void basicAuthorization_test();
|
||||
};
|
||||
|
||||
void RequestTest::basicAuthorization_test()
|
||||
{
|
||||
QString expected = "Basic VGVzdGVyOlRlc3Q=";
|
||||
@ -17,3 +24,5 @@ void RequestTest::basicAuthorization_test()
|
||||
}
|
||||
|
||||
QTEST_APPLESS_MAIN(serversdk::RequestTest)
|
||||
|
||||
#include "tst_request.moc"
|
||||
@ -6,6 +6,6 @@ CONFIG -= app_bundle
|
||||
|
||||
TEMPLATE = app
|
||||
|
||||
SOURCES += tst_basictest.cpp
|
||||
SOURCES += tst_soaptest.cpp
|
||||
|
||||
include(../../../serversdkint.pri)
|
||||
166
test/auto/soap/tst_soaptest.cpp
Normal file
166
test/auto/soap/tst_soaptest.cpp
Normal file
@ -0,0 +1,166 @@
|
||||
#include <QtTest>
|
||||
#include <QProcess>
|
||||
|
||||
#include "serversdk/requestmanager.h"
|
||||
#include "serversdk/utils.h"
|
||||
#include "serversdk/soaprequest.h"
|
||||
|
||||
namespace serversdk {
|
||||
|
||||
class SoapTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SoapTest();
|
||||
|
||||
private:
|
||||
Q_SLOT void construct();
|
||||
|
||||
Q_SLOT void public_soap_xml();
|
||||
|
||||
Q_SLOT void public_soap_test();
|
||||
Q_SLOT void public_soap_test_data();
|
||||
};
|
||||
|
||||
SoapTest::SoapTest()
|
||||
{
|
||||
qRegisterMetaType<std::shared_ptr<Request>>("std::shared_ptr<Request>");
|
||||
}
|
||||
|
||||
void SoapTest::construct()
|
||||
{
|
||||
RequestManager soap;
|
||||
|
||||
QVERIFY(soap.mRequestMap.isEmpty());
|
||||
|
||||
QFileInfo info(Utils::sourcePath(""));
|
||||
QCOMPARE(info.fileName(), QStringLiteral("data"));
|
||||
|
||||
QCOMPARE(Utils::sourceData("soap/LatLonListSubgrid-response.xml").count(), 1298);
|
||||
//QCOMPARE(Utils::sourceData("test/cmd/LatLonListSubgrid-response.xml").count(), 1298);
|
||||
}
|
||||
|
||||
void SoapTest::public_soap_xml()
|
||||
{
|
||||
QString soapAction = "blz:getBank";
|
||||
QString endpoint = "http://www.thomas-bayer.com/axis2/services/BLZService";
|
||||
QString xml = QString("<soapenv:Envelope " \
|
||||
"xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" " \
|
||||
"xmlns:blz=\"http://thomas-bayer.com/blz/\">" \
|
||||
"<soapenv:Header/>" \
|
||||
" <soapenv:Body>" \
|
||||
" <blz:getBank>" \
|
||||
" <blz:blz>%1</blz:blz>" \
|
||||
" </blz:getBank>" \
|
||||
"</soapenv:Body>" \
|
||||
"</soapenv:Envelope>").arg("10020200");
|
||||
|
||||
QByteArray xmlRequest = xml.toUtf8();
|
||||
|
||||
// SOAP download
|
||||
RequestManager soap;
|
||||
auto response = std::make_shared<SoapRequest>(soapAction, endpoint, xmlRequest);
|
||||
QCOMPARE(response.use_count(), 1L );
|
||||
soap.processRequest(response);
|
||||
QVERIFY(soap.mRequestMap.count() == 1);
|
||||
QSignalSpy spyFinished(&soap, &RequestManager::downloadFinished);
|
||||
|
||||
QVERIFY(spyFinished.wait());
|
||||
//QCOMPARE(response.use_count(), 2L ); // this context + Soap
|
||||
spyFinished.clear();
|
||||
QCOMPARE(response.use_count(), 1L ); // this context + Soap
|
||||
|
||||
// Response
|
||||
QByteArray xmlResponse = Utils::sourceData("soap/bankapi-response.xml");
|
||||
QVERIFY( ! xmlResponse.isEmpty());
|
||||
|
||||
// QFile fileWrite(cmdDir() + "/tst_soap-response.xml");
|
||||
// QVERIFY(fileWrite.open(QIODevice::WriteOnly));
|
||||
// fileWrite.write(soap.data());
|
||||
// fileWrite.close();
|
||||
|
||||
QCOMPARE(response.use_count(), 1L ); // this context + Soap
|
||||
QCOMPARE(response->responseData(), xmlResponse);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void SoapTest::public_soap_test()
|
||||
{
|
||||
QFETCH(QString, soapAction);
|
||||
QFETCH(QString, endpoint);
|
||||
QFETCH(QString, xmlRequestFile);
|
||||
QFETCH(QString, xmlResponseFile);
|
||||
QFETCH(bool, soap12);
|
||||
|
||||
// Request
|
||||
QByteArray xmlRequest = Utils::sourceData(xmlRequestFile);
|
||||
QVERIFY( ! xmlRequest.isEmpty());
|
||||
// Response
|
||||
QByteArray xmlResponse = Utils::sourceData(xmlResponseFile);
|
||||
QVERIFY( ! xmlResponse.isEmpty());
|
||||
|
||||
// RequestManagerrequest
|
||||
auto req = std::make_shared<SoapRequest>(soapAction, endpoint, xmlRequest);
|
||||
req->setSoapVersion12(soap12);
|
||||
|
||||
// RequestManagerdownload
|
||||
RequestManager soap;
|
||||
soap.processRequest(req);
|
||||
|
||||
QSignalSpy progressSpy(&soap, &RequestManager::progresss);
|
||||
QSignalSpy finishedSpy(&soap, &RequestManager::downloadFinished);
|
||||
QVERIFY(finishedSpy.wait(20000));
|
||||
QVERIFY(progressSpy.count());
|
||||
|
||||
//Uncoment to write .xml response to cmd folder
|
||||
// QFile fileWrite(Utils::sourcePath("test/cmd/tst_soap-response.xml"));
|
||||
// QVERIFY(fileWrite.open(QIODevice::WriteOnly));
|
||||
// fileWrite.write(req->responseData());
|
||||
// fileWrite.close();
|
||||
QProcess process;
|
||||
process.start("hostname");
|
||||
process.waitForFinished(-1); // will wait forever until finished
|
||||
|
||||
QString stdout = process.readAllStandardOutput();
|
||||
|
||||
QCOMPARE(req->responseData(), xmlResponse);
|
||||
}
|
||||
|
||||
|
||||
void SoapTest::public_soap_test_data()
|
||||
{
|
||||
QTest::addColumn<QString>("soapAction");
|
||||
QTest::addColumn<QString>("endpoint");
|
||||
QTest::addColumn<QString>("xmlRequestFile");
|
||||
QTest::addColumn<QString>("xmlResponseFile");
|
||||
QTest::addColumn<bool>("soap12");
|
||||
QTest::addColumn<bool>("soap12");
|
||||
|
||||
// QTest::newRow("weather service")
|
||||
// << "LatLonListSubgrid"
|
||||
// << "https://graphical.weather.gov/xml/SOAP_server/ndfdXMLserver.php"
|
||||
// << "soap/LatLonListSubgrid.xml"
|
||||
// << "soap/LatLonListSubgrid-response.xml"
|
||||
// << false;
|
||||
|
||||
QTest::newRow("bank service")
|
||||
<< "blz:getBank"
|
||||
<< "http://www.thomas-bayer.com/axis2/services/BLZService"
|
||||
<< "soap/bankapi.xml"
|
||||
<< "soap/bankapi-response.xml"
|
||||
<< false;
|
||||
|
||||
QTest::newRow("bank service SOAP 1.2")
|
||||
<< "blz:getBank"
|
||||
<< "http://www.thomas-bayer.com/axis2/services/BLZService"
|
||||
<< "soap/bankapi-1.2.xml"
|
||||
<< "soap/bankapi-1.2-response.xml"
|
||||
<< true; // SOAP 1.2
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
QTEST_MAIN(serversdk::SoapTest)
|
||||
|
||||
#include "tst_soaptest.moc"
|
||||
@ -1,21 +0,0 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
|
||||
namespace serversdk {
|
||||
|
||||
class RequestTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
// Default testcases ( not needed to declare it )
|
||||
// Q_SLOT void initTestCase();
|
||||
// Q_SLOT void cleanupTestCase();
|
||||
// void wont_run(); // no Q_SLOT ( only slots will be runned within tests )
|
||||
|
||||
|
||||
Q_SLOT void basicAuthorization_test();
|
||||
};
|
||||
|
||||
} //
|
||||
1
test/data/soap/LatLonListSubgrid-response.xml
Normal file
1
test/data/soap/LatLonListSubgrid-response.xml
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?><SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"><SOAP-ENV:Body><ns1:LatLonListSubgridResponse xmlns:ns1="uri:DWMLgen"><listLatLonOut xsi:type="xsd:string"><?xml version='1.0'?><dwml version='1.0' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:noNamespaceSchemaLocation='https://graphical.weather.gov/xml/DWMLgen/schema/DWML.xsd'><latLonList>34.986638,-82.027411 35.120891,-82.011661 35.255084,-81.995872 35.389215,-81.980045 35.523285,-81.964180 34.973669,-81.863528 35.107911,-81.847581 35.242094,-81.831596 35.376216,-81.815571 35.510276,-81.799508 34.960536,-81.699684 35.094768,-81.683540 35.228941,-81.667358 35.363052,-81.651136 35.497102,-81.634876 34.947240,-81.535878 35.081463,-81.519539 35.215625,-81.503160 35.349726,-81.486741 35.483766,-81.470282</latLonList></dwml></listLatLonOut></ns1:LatLonListSubgridResponse></SOAP-ENV:Body></SOAP-ENV:Envelope>
|
||||
12
test/data/soap/LatLonListSubgrid.xml
Normal file
12
test/data/soap/LatLonListSubgrid.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
|
||||
<SOAP-ENV:Body>
|
||||
<ns5689:LatLonListSubgrid xmlns:ns5689="uri:DWMLgen">
|
||||
<lowerLeftLatitude xsi:type="xsd:string">35.00</lowerLeftLatitude>
|
||||
<lowerLeftLongitude xsi:type="xsd:string">-82.00</lowerLeftLongitude>
|
||||
<upperRightLatitude xsi:type="xsd:string">35.5</upperRightLatitude>
|
||||
<upperRightLongitude xsi:type="xsd:string">-81.50</upperRightLongitude>
|
||||
<resolution xsi:type="xsd:string">20.0</resolution>
|
||||
</ns5689:LatLonListSubgrid>
|
||||
</SOAP-ENV:Body>
|
||||
</SOAP-ENV:Envelope>
|
||||
1
test/data/soap/bankapi-1.2-response.xml
Normal file
1
test/data/soap/bankapi-1.2-response.xml
Normal file
@ -0,0 +1 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?><soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope"><soapenv:Body><ns1:getBankResponse xmlns:ns1="http://thomas-bayer.com/blz/"><ns1:details><ns1:bezeichnung>BHF-BANK</ns1:bezeichnung><ns1:bic>BHFBDEFF100</ns1:bic><ns1:ort>Berlin</ns1:ort><ns1:plz>10117</ns1:plz></ns1:details></ns1:getBankResponse></soapenv:Body></soapenv:Envelope>
|
||||
14
test/data/soap/bankapi-1.2.sh
Executable file
14
test/data/soap/bankapi-1.2.sh
Executable file
@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
|
||||
# @see https://stackoverflow.com/a/55352633/1917249
|
||||
|
||||
ACTION_YOU_WANT_TO_CALL="blz:getBank"
|
||||
|
||||
XMLFILENAME=test03-1.2.xml
|
||||
|
||||
URL_OF_THE_SOAP_WEB_SERVICE_ENDPOINT="http://www.thomas-bayer.com/axis2/services/BLZService"
|
||||
|
||||
curl -X POST $URL_OF_THE_SOAP_WEB_SERVICE_ENDPOINT \
|
||||
-H 'Content-Type: application/soap+xml;action=$ACTION_YOU_WANT_TO_CALL' \
|
||||
-H 'SOAPAction: $ACTION_YOU_WANT_TO_CALL' \
|
||||
--data @$XMLFILENAME > test03-1.2-response.xml
|
||||
10
test/data/soap/bankapi-1.2.xml
Normal file
10
test/data/soap/bankapi-1.2.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<soapenv:Envelope
|
||||
xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope"
|
||||
xmlns:blz="http://thomas-bayer.com/blz/">
|
||||
<soapenv:Header/>
|
||||
<soapenv:Body>
|
||||
<blz:getBank>
|
||||
<blz:blz>10020200</blz:blz>
|
||||
</blz:getBank>
|
||||
</soapenv:Body>
|
||||
</soapenv:Envelope>
|
||||
1
test/data/soap/bankapi-response.xml
Normal file
1
test/data/soap/bankapi-response.xml
Normal file
@ -0,0 +1 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Body><ns1:getBankResponse xmlns:ns1="http://thomas-bayer.com/blz/"><ns1:details><ns1:bezeichnung>BHF-BANK</ns1:bezeichnung><ns1:bic>BHFBDEFF100</ns1:bic><ns1:ort>Berlin</ns1:ort><ns1:plz>10117</ns1:plz></ns1:details></ns1:getBankResponse></soapenv:Body></soapenv:Envelope>
|
||||
14
test/data/soap/bankapi.sh
Executable file
14
test/data/soap/bankapi.sh
Executable file
@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
|
||||
# @see https://stackoverflow.com/a/55352633/1917249
|
||||
|
||||
ACTION_YOU_WANT_TO_CALL="blz:getBank"
|
||||
|
||||
XMLFILENAME=test03.xml
|
||||
|
||||
URL_OF_THE_SOAP_WEB_SERVICE_ENDPOINT="http://www.thomas-bayer.com/axis2/services/BLZService"
|
||||
|
||||
curl -X POST $URL_OF_THE_SOAP_WEB_SERVICE_ENDPOINT \
|
||||
-H 'Content-Type: text/xml' \
|
||||
-H 'SOAPAction: $ACTION_YOU_WANT_TO_CALL' \
|
||||
--data @$XMLFILENAME > test03-response.xml
|
||||
10
test/data/soap/bankapi.xml
Normal file
10
test/data/soap/bankapi.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<soapenv:Envelope
|
||||
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
|
||||
xmlns:blz="http://thomas-bayer.com/blz/">
|
||||
<soapenv:Header/>
|
||||
<soapenv:Body>
|
||||
<blz:getBank>
|
||||
<blz:blz>10020200</blz:blz>
|
||||
</blz:getBank>
|
||||
</soapenv:Body>
|
||||
</soapenv:Envelope>
|
||||
Loading…
x
Reference in New Issue
Block a user