Commit 101bc004 authored by Volker Krause's avatar Volker Krause
Browse files

Deal with colliding system ids

The specification requires those to be globally unique, but you guessed it,
they aren't... There's at least three different operators who seem to be
using a constant there, a UUID of all possible constants even in two cases.

We now identify those and assign our own system id based on a hashed feed
URL. This isn't ideal as the feed URL can change as new versions become
available, but it's the best we can do.
parent e6cb8e5e
......@@ -32,6 +32,15 @@ private Q_SLOTS:
QCOMPARE(GBFS::typeForKeyName(u"geofencing_zones_information"), GBFS::Unknown);
}
void testGenerateSystemId()
{
GBFSService service;
service.discoveryUrl = QUrl(QStringLiteral("https://mds-global-dud.neuron-mobility.com/gbfs/2/"));
QVERIFY(service.systemId.isEmpty());
service.generateSystemId();
QCOMPARE(service.systemId, QLatin1String("GAFF_F5kMPFE5ZO2iFmAU0AgBcc="));
}
void testServiceRepo()
{
const auto &services = GBFSServiceRepository::services();
......
......@@ -271,8 +271,10 @@ void GBFSJob::parseSystemInformation(const QJsonDocument &doc)
Q_EMIT finished();
return;
}
m_service.systemId = systemId;
m_store = GBFSStore(systemId);
if (m_service.systemId.isEmpty()) {
m_service.systemId = systemId;
}
m_store = GBFSStore(m_service.systemId);
m_store.storeData(GBFS::Discovery, m_discoverDoc);
m_store.storeData(GBFS::SystemInformation, doc);
if (!m_versionDoc.isEmpty()) {
......
......@@ -7,6 +7,7 @@
#include "gbfsservice.h"
#include "datatypes/json_p.h"
#include <QCryptographicHash>
#include <QDebug>
#include <QDirIterator>
#include <QFile>
......@@ -16,6 +17,14 @@
using namespace KPublicTransport;
void GBFSService::generateSystemId()
{
if (discoveryUrl.isEmpty()) {
return;
}
systemId = QString::fromUtf8(QCryptographicHash::hash(discoveryUrl.toString().toUtf8(), QCryptographicHash::Sha1).toBase64(QByteArray::Base64UrlEncoding));
}
QJsonObject GBFSService::toJson(const GBFSService &service)
{
return Json::toJson(service);
......
......@@ -20,9 +20,9 @@ class QJsonObject;
namespace KPublicTransport {
/** A single service offering a GBFS feed.
* @internal only exported for testing
* @internal only exported for testing and the feed discovery tool
*/
class GBFSService
class KPUBLICTRANSPORT_EXPORT GBFSService
{
Q_GADGET
Q_PROPERTY(QUrl discoveryUrl MEMBER discoveryUrl)
......@@ -34,6 +34,11 @@ public:
QString systemId;
QRectF boundingBox;
/** Generate a systemId based on the URL.
* This is used for cases of colliding systemIds.
*/
void generateSystemId();
static KPUBLICTRANSPORT_EXPORT QJsonObject toJson(const GBFSService &service);
static GBFSService fromJson(const QJsonObject &obj);
};
......
......@@ -35,6 +35,7 @@ public:
void start();
void getFeedList();
void discoverNextFeed();
void checkDuplicateSystemIds();
void writeFeeds();
QNetworkAccessManager m_nam;
......@@ -42,6 +43,7 @@ public:
int m_currentFeedIdx = -1;
int m_throttleTime = 0;
QStringList m_throttledFeeds;
bool m_syntheticSystemId = false;
std::vector<GBFSService> m_services;
QString m_outputFileName;
......@@ -126,7 +128,7 @@ void GBFSProbe::discoverNextFeed()
m_currentFeedIdx = 0;
m_throttleTime = m_throttleTime == 0 ? 500 : (2 * m_throttleTime);
} else {
writeFeeds();
checkDuplicateSystemIds();
return;
}
}
......@@ -150,12 +152,50 @@ void GBFSProbe::discoverNextFeed()
GBFSService service;
service.discoveryUrl = QUrl(m_gbfsFeeds[m_currentFeedIdx]);
if (m_syntheticSystemId) {
service.generateSystemId();
}
job->discoverAndUpdate(service);
}
static bool sortBySystemId(const GBFSService &lhs, const GBFSService &rhs)
{
return lhs.systemId < rhs.systemId;
}
void GBFSProbe::checkDuplicateSystemIds()
{
m_gbfsFeeds.clear();
std::sort(m_services.begin(), m_services.end(), sortBySystemId);
for (auto it = m_services.begin(); it != m_services.end();) {
const auto range = std::equal_range(it, m_services.end(), (*it), sortBySystemId);
assert(range.first == it);
if (std::next(it) == range.second) {
it = range.second;
} else {
for (auto it2 = range.first; it2 != range.second; ++it2) {
m_gbfsFeeds.push_back((*it2).discoveryUrl.toString());
}
it = m_services.erase(range.first, range.second);
}
}
if (!m_gbfsFeeds.isEmpty()) {
assert(!m_syntheticSystemId);
qWarning() << "Feeds with colliding system ids:" << m_gbfsFeeds;
m_syntheticSystemId = true;
m_currentFeedIdx = -1;
m_throttleTime = 0;
std::shuffle(m_gbfsFeeds.begin(), m_gbfsFeeds.end(), std::default_random_engine());
discoverNextFeed();
} else {
writeFeeds();
}
}
void GBFSProbe::writeFeeds()
{
std::sort(m_services.begin(), m_services.end(), [](const auto &lhs, const auto &rhs) { return lhs.systemId < rhs.systemId; });
QJsonArray array;
for (const auto &service : m_services) {
array.push_back(GBFSService::toJson(service));
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment