diff options
-rw-r--r-- | include/nostr.hpp | 36 | ||||
-rw-r--r-- | src/nostr_service.cpp | 81 | ||||
-rw-r--r-- | test/nostr_service_test.cpp | 106 |
3 files changed, 40 insertions, 183 deletions
diff --git a/include/nostr.hpp b/include/nostr.hpp index 5f5ce25..e450505 100644 --- a/include/nostr.hpp +++ b/include/nostr.hpp @@ -163,15 +163,6 @@ public: /** * @brief Queries all open relay connections for events matching the given set of filters. * @param filters The filters to use for the query. - * @returns The ID of the subscription created for the query. - * @remarks The service will store a limited number of events returned from the relay for the - * given filters. These events may be retrieved via `getNewEvents`. - */ - std::string queryRelays(std::shared_ptr<Filters> filters); - - /** - * @brief Queries all open relay connections for events matching the given set of filters. - * @param filters The filters to use for the query. * @param responseHandler A callable object that will be invoked each time the client receives * an event matching the filters. * @returns The ID of the subscription created for the query. @@ -182,20 +173,6 @@ public: std::string queryRelays( std::shared_ptr<Filters> filters, std::function<void(const std::string&, std::shared_ptr<Event>)> responseHandler); - - /** - * @brief Get any new events received since the last call to this method, across all - * subscriptions. - * @returns A pointer to a vector of new events. - */ - std::vector<std::shared_ptr<Event>> getNewEvents(); - - /** - * @brief Get any new events received since the last call to this method, for the given - * subscription. - * @returns A pointer to a vector of new events. - */ - std::vector<std::shared_ptr<Event>> getNewEvents(std::string subscriptionId); /** * @brief Closes the subscription with the given ID on all open relay connections. @@ -238,10 +215,6 @@ private: RelayList _activeRelays; ///< A map from relay URIs to the subscription IDs open on each relay. std::unordered_map<std::string, std::vector<std::string>> _subscriptions; - ///< A map from subscription IDs to the events returned by the relays for each subscription. - std::unordered_map<std::string, std::vector<std::shared_ptr<Event>>> _events; - ///< A map from the subscription IDs to the ID of the latest read event for each subscription. - std::unordered_map<std::string, std::string> _lastRead; /** * @brief Determines which of the given relays are currently connected. @@ -301,15 +274,6 @@ private: void onMessage( std::string message, std::function<void(const std::string&, std::shared_ptr<Event>)> eventHandler); - - /** - * @brief A default message handler for events returned from relay queries. - * @param subscriptionId The ID of the subscription for which the event was received. - * @param event The event received from the relay. - * @remark By default, new events are stored in a map of subscription IDs to vectors of events. - * Events are retrieved by calling `getNewEvents` or `getNewEvents(subscriptionId)`. - */ - void onEvent(std::string subscriptionId, std::shared_ptr<Event> event); }; class ISigner diff --git a/src/nostr_service.cpp b/src/nostr_service.cpp index d1744e3..614e64f 100644 --- a/src/nostr_service.cpp +++ b/src/nostr_service.cpp @@ -179,14 +179,6 @@ tuple<RelayList, RelayList> NostrService::publishEvent(shared_ptr<Event> event) return make_tuple(successfulRelays, failedRelays); }; -string NostrService::queryRelays(shared_ptr<Filters> filters) -{ - return this->queryRelays(filters, [this](string subscriptionId, shared_ptr<Event> event) { - this->_lastRead[subscriptionId] = event->id; - this->onEvent(subscriptionId, event); - }); -}; - string NostrService::queryRelays( shared_ptr<Filters> filters, function<void(const string&, shared_ptr<Event>)> responseHandler) @@ -242,52 +234,6 @@ string NostrService::queryRelays( return subscriptionId; }; -vector<shared_ptr<Event>> NostrService::getNewEvents() -{ - vector<shared_ptr<Event>> newEvents; - - for (auto& [subscriptionId, events] : this->_events) - { - vector<shared_ptr<Event>> subscriptionEvents = this->getNewEvents(subscriptionId); - newEvents.insert(newEvents.end(), subscriptionEvents.begin(), subscriptionEvents.end()); - } - - return newEvents; -}; - -vector<shared_ptr<Event>> NostrService::getNewEvents(string subscriptionId) -{ - if (this->_events.find(subscriptionId) == this->_events.end()) - { - PLOG_ERROR << "No events found for subscription: " << subscriptionId; - throw out_of_range("No events found for subscription: " + subscriptionId); - } - - if (this->_lastRead.find(subscriptionId) == this->_lastRead.end()) - { - PLOG_ERROR << "No last read event ID found for subscription: " << subscriptionId; - throw out_of_range("No last read event ID found for subscription: " + subscriptionId); - } - - lock_guard<mutex> lock(this->_propertyMutex); - vector<shared_ptr<Event>> newEvents; - vector<shared_ptr<Event>> receivedEvents = this->_events[subscriptionId]; - vector<shared_ptr<Event>>::iterator eventIt = find_if( - receivedEvents.begin(), - receivedEvents.end(), - [this,subscriptionId](shared_ptr<Event> event) { - return event->id == this->_lastRead[subscriptionId]; - }) + 1; - - while (eventIt != receivedEvents.end()) - { - newEvents.push_back(move(*eventIt)); - eventIt++; - } - - return newEvents; -}; - tuple<RelayList, RelayList> NostrService::closeSubscription(string subscriptionId) { RelayList successfulRelays; @@ -523,31 +469,4 @@ void NostrService::onMessage( throw je; } }; - -void NostrService::onEvent(string subscriptionId, shared_ptr<Event> event) -{ - lock_guard<mutex> lock(this->_propertyMutex); - this->_events[subscriptionId].push_back(move(event)); - PLOG_INFO << "Received event for subscription: " << subscriptionId; - - // To protect memory, only keep a limited number of events per subscription. - while (this->_events[subscriptionId].size() > NostrService::MAX_EVENTS_PER_SUBSCRIPTION) - { - auto events = this->_events[subscriptionId]; - auto eventIt = find_if( - events.begin(), - events.end(), - [this, subscriptionId](shared_ptr<Event> event) { - return event->id == this->_lastRead[subscriptionId]; - }); - - if (eventIt == events.begin()) - { - eventIt++; - } - - this->_lastRead[subscriptionId] = (*eventIt)->id; - _events[subscriptionId].erase(_events[subscriptionId].begin()); - } -}; } // namespace nostr diff --git a/test/nostr_service_test.cpp b/test/nostr_service_test.cpp index 71af093..7adda7e 100644 --- a/test/nostr_service_test.cpp +++ b/test/nostr_service_test.cpp @@ -54,7 +54,7 @@ public: "wss://nostr.thesamecat.io" }; - static const nostr::Event getTestEvent() + static const nostr::Event getTextNoteTestEvent() { nostr::Event event; event.pubkey = "13tn5ccv2guflxgffq4aj0hw5x39pz70zcdrfd6vym887gry38zys28dask"; @@ -70,10 +70,24 @@ public: return event; }; - static const string getTestEventMessage(string subscriptionId) + static const nostr::Event getLongFormTestEvent() { - auto event = make_shared<nostr::Event>(getTestEvent()); - + nostr::Event event; + event.pubkey = "13tn5ccv2guflxgffq4aj0hw5x39pz70zcdrfd6vym887gry38zys28dask"; + event.kind = 30023; + event.tags = + { + { "event", "5c83da77af1dec6d7289834998ad7aafbd9e2191396d75ec3cc27f5a77226f36", "wss://nostr.example.com" }, + { "pubkey", "f7234bd4c1394dda46d09f35bd384dd30cc552ad5541990f98844fb06676e9ca" }, + { "author", "30023:f7234bd4c1394dda46d09f35bd384dd30cc552ad5541990f98844fb06676e9ca:abcd", "wss://nostr.example.com" } + }; + event.content = "Hello, World!"; + + return event; + } + + static const string getTestEventMessage(shared_ptr<nostr::Event> event, string subscriptionId) + { auto signer = make_unique<FakeSigner>(); signer->sign(event); @@ -85,7 +99,7 @@ public: return jarr.dump(); } - static const nostr::Filters getTestFilters() + static const nostr::Filters getKind0And1TestFilters() { nostr::Filters filters; filters.authors = { @@ -99,6 +113,20 @@ public: return filters; } + static const nostr::Filters getKind30023TestFilters() + { + nostr::Filters filters; + filters.authors = { + "13tn5ccv2guflxgffq4aj0hw5x39pz70zcdrfd6vym887gry38zys28dask", + "1l9d9jh67rkwayalrxcy686aujyz5pper5kzjv8jvg8pu9v9ns4ls0xvq42", + "187ujhtmnv82ftg03h4heetwk3dd9mlfkf8th3fvmrk20nxk9mansuzuyla" + }; + filters.kinds = { 30023 }; + filters.limit = 5; + + return filters; + } + protected: shared_ptr<plog::ConsoleAppender<plog::TxtFormatter>> testAppender; shared_ptr<MockWebSocketClient> mockClient; @@ -398,7 +426,7 @@ TEST_F(NostrServiceTest, PublishEvent_CorrectlyIndicates_AllSuccesses) return make_tuple(uri, true); })); - auto testEvent = make_shared<nostr::Event>(getTestEvent()); + auto testEvent = make_shared<nostr::Event>(getTextNoteTestEvent()); auto [successes, failures] = nostrService->publishEvent(testEvent); ASSERT_EQ(successes.size(), defaultTestRelays.size()); @@ -443,7 +471,7 @@ TEST_F(NostrServiceTest, PublishEvent_CorrectlyIndicates_AllFailures) return make_tuple(uri, false); })); - auto testEvent = make_shared<nostr::Event>(getTestEvent()); + auto testEvent = make_shared<nostr::Event>(getTextNoteTestEvent()); auto [successes, failures] = nostrService->publishEvent(testEvent); ASSERT_EQ(successes.size(), 0); @@ -494,7 +522,7 @@ TEST_F(NostrServiceTest, PublishEvent_CorrectlyIndicates_MixedSuccessesAndFailur return make_tuple(uri, false); })); - auto testEvent = make_shared<nostr::Event>(getTestEvent()); + auto testEvent = make_shared<nostr::Event>(getTextNoteTestEvent()); auto [successes, failures] = nostrService->publishEvent(testEvent); ASSERT_EQ(successes.size(), 1); @@ -504,61 +532,6 @@ TEST_F(NostrServiceTest, PublishEvent_CorrectlyIndicates_MixedSuccessesAndFailur ASSERT_EQ(failures[0], defaultTestRelays[1]); }; -TEST_F(NostrServiceTest, QueryRelays_UsesDefaultHandler_AndReturnsSubscriptionId) -{ - mutex connectionStatusMutex; - auto connectionStatus = make_shared<unordered_map<string, bool>>(); - connectionStatus->insert({ defaultTestRelays[0], false }); - connectionStatus->insert({ defaultTestRelays[1], false }); - - EXPECT_CALL(*mockClient, isConnected(_)) - .WillRepeatedly(Invoke([connectionStatus, &connectionStatusMutex](string uri) - { - lock_guard<mutex> lock(connectionStatusMutex); - bool status = connectionStatus->at(uri); - if (status == false) - { - connectionStatus->at(uri) = true; - } - return status; - })); - - auto nostrService = make_unique<nostr::NostrService>( - testAppender, - mockClient, - fakeSigner, - defaultTestRelays); - nostrService->openRelayConnections(); - - auto sentSubscriptionId = make_shared<string>(); - EXPECT_CALL(*mockClient, send(_, _)) - .Times(2) - .WillRepeatedly(Invoke([sentSubscriptionId](string message, string uri) - { - json jarr = json::array(); - jarr = json::parse(message); - - string temp = jarr[1].dump(); - if (!temp.empty() && temp[0] == '\"' && temp[temp.size() - 1] == '\"') - { - *sentSubscriptionId = temp.substr(1, temp.size() - 2); - } - - return make_tuple(uri, true); - })); - EXPECT_CALL(*mockClient, receive(_, _)) - .Times(2) - .WillRepeatedly(Invoke([sentSubscriptionId](string _, function<void(const string&)> messageHandler) - { - messageHandler(getTestEventMessage(*sentSubscriptionId)); - })); - - auto filters = make_shared<nostr::Filters>(getTestFilters()); - auto receivedSubscriptionId = nostrService->queryRelays(filters); - - ASSERT_STREQ(receivedSubscriptionId.c_str(), sentSubscriptionId->c_str()); -}; - TEST_F(NostrServiceTest, QueryRelays_UsesProvidedHandler_AndReturnsSubscriptionId) { mutex connectionStatusMutex; @@ -605,11 +578,12 @@ TEST_F(NostrServiceTest, QueryRelays_UsesProvidedHandler_AndReturnsSubscriptionI .Times(2) .WillRepeatedly(Invoke([sentSubscriptionId](string _, function<void(const string&)> messageHandler) { - messageHandler(getTestEventMessage(*sentSubscriptionId)); + auto event = make_shared<nostr::Event>(getTextNoteTestEvent()); + messageHandler(getTestEventMessage(event, *sentSubscriptionId)); })); - auto filters = make_shared<nostr::Filters>(getTestFilters()); - nostr::Event expectedEvent = getTestEvent(); + auto filters = make_shared<nostr::Filters>(getKind0And1TestFilters()); + nostr::Event expectedEvent = getTextNoteTestEvent(); auto receivedSubscriptionId = nostrService->queryRelays( filters, [expectedEvent](const string& subscriptionId, shared_ptr<nostr::Event> event) |