From 5d32b925b807409b78e99dd7491f791458fdb436 Mon Sep 17 00:00:00 2001 From: Andy Ford Date: Sun, 2 Jan 2022 13:06:03 +0000 Subject: [PATCH] feat(metar): Add QFE to pressure monitor messages, QFE query command (#376) * Introduce parsed metars and components * Collection of metars * Components factories * Push event handler * Push event sync * Style * Pull it all together * More style * Query message * Tidy and docs * Style * Format * Remove duplicate method --- docs/UserGuide/Features/Features.md | 2 +- docs/UserGuide/Features/PressureMonitor.md | 25 + src/plugin/CMakeLists.txt | 19 +- src/plugin/airfield/AirfieldCollection.cpp | 5 + src/plugin/airfield/AirfieldCollection.h | 1 + src/plugin/bootstrap/CollectionBootstrap.cpp | 2 - .../EventHandlerCollectionBootstrap.cpp | 68 +- src/plugin/bootstrap/InitialisePlugin.cpp | 2 + .../message/MessageSerializableInterface.h | 16 +- .../metar/MetarComponentFactoryInterface.h | 15 + src/plugin/metar/MetarComponents.h | 13 + src/plugin/metar/MetarComponentsFactory.cpp | 26 + src/plugin/metar/MetarComponentsFactory.h | 20 + .../metar/MetarComponentsFactoryFactory.cpp | 12 + .../metar/MetarComponentsFactoryFactory.h | 7 + .../metar/MetarEventHandlerCollection.cpp | 60 +- .../metar/MetarEventHandlerCollection.h | 42 +- src/plugin/metar/MetarEventHandlerInterface.h | 3 +- src/plugin/metar/MetarModule.cpp | 37 + src/plugin/metar/MetarModule.h | 9 + src/plugin/metar/MetarParsingFunctions.cpp | 24 - src/plugin/metar/MetarParsingFunctions.h | 10 - .../metar/MetarsUpdatedPushEventProcessor.cpp | 55 + .../metar/MetarsUpdatedPushEventProcessor.h | 37 + src/plugin/metar/ParsedMetar.cpp | 28 + src/plugin/metar/ParsedMetar.h | 34 + src/plugin/metar/ParsedMetarCollection.cpp | 24 + src/plugin/metar/ParsedMetarCollection.h | 22 + src/plugin/metar/ParsedMetarFactory.cpp | 36 + src/plugin/metar/ParsedMetarFactory.h | 30 + src/plugin/metar/PressureChangeMessage.cpp | 42 +- src/plugin/metar/PressureChangeMessage.h | 61 +- src/plugin/metar/PressureComponent.cpp | 45 + src/plugin/metar/PressureComponent.h | 29 + src/plugin/metar/PressureComponentFactory.cpp | 49 + src/plugin/metar/PressureComponentFactory.h | 18 + src/plugin/metar/PressureMonitor.cpp | 159 +- src/plugin/metar/PressureMonitor.h | 93 +- src/plugin/metar/PressureMonitorBootstrap.cpp | 2 +- src/plugin/metar/PressureNotFoundMessage.cpp | 48 + src/plugin/metar/PressureNotFoundMessage.h | 28 + .../metar/PressureQueryCommandHandler.cpp | 35 + .../metar/PressureQueryCommandHandler.h | 26 + src/plugin/metar/PressureQueryMessage.cpp | 69 + src/plugin/metar/PressureQueryMessage.h | 35 + src/plugin/metar/PressureUnit.h | 12 + src/plugin/plugin/UKPlugin.cpp | 20 +- src/plugin/plugin/UKPlugin.h | 5 - src/plugin/plugin/UkPluginBootstrap.cpp | 51 +- src/plugin/plugin/UkPluginBootstrap.h | 30 +- src/utils/api/ApiHelper.cpp | 776 ++++---- src/utils/api/ApiHelper.h | 225 +-- src/utils/api/ApiInterface.h | 181 +- src/utils/api/ApiRequestBuilder.cpp | 929 ++++----- src/utils/api/ApiRequestBuilder.h | 229 +-- test/plugin/CMakeLists.txt | 14 +- .../airfield/AirfieldCollectionTest.cpp | 14 + .../EventHandlerCollectionBootstrapTest.cpp | 6 - .../metar/MetarComponentsFactoryTest.cpp | 48 + .../metar/MetarEventHandlerCollectionTest.cpp | 104 +- test/plugin/metar/MetarModuleTest.cpp | 42 + .../metar/MetarParsingFunctionsTest.cpp | 40 - .../MetarsUpdatedPushEventProcessorTest.cpp | 129 ++ .../metar/ParsedMetarCollectionTest.cpp | 68 + test/plugin/metar/ParsedMetarFactoryTest.cpp | 119 ++ test/plugin/metar/ParsedMetarTest.cpp | 36 + .../metar/PressureChangeMessageTest.cpp | 110 +- .../metar/PressureComponentFactoryTest.cpp | 136 ++ test/plugin/metar/PressureComponentTest.cpp | 53 + test/plugin/metar/PressureMonitorTest.cpp | 269 +-- .../metar/PressureNotFoundMessageTest.cpp | 57 + .../metar/PressureQueryCommandHandlerTest.cpp | 107 ++ .../plugin/metar/PressureQueryMessageTest.cpp | 71 + test/plugin/mock/MockMinStack.h | 14 - test/plugin/pch/pch.h | 1 - test/testingutils/mock/MockApiInterface.h | 139 +- test/utils/api/ApiHelperTest.cpp | 1687 +++++++++-------- test/utils/api/ApiRequestBuilderTest.cpp | 1159 +++++------ 78 files changed, 4997 insertions(+), 3307 deletions(-) create mode 100644 docs/UserGuide/Features/PressureMonitor.md create mode 100644 src/plugin/metar/MetarComponentFactoryInterface.h create mode 100644 src/plugin/metar/MetarComponents.h create mode 100644 src/plugin/metar/MetarComponentsFactory.cpp create mode 100644 src/plugin/metar/MetarComponentsFactory.h create mode 100644 src/plugin/metar/MetarComponentsFactoryFactory.cpp create mode 100644 src/plugin/metar/MetarComponentsFactoryFactory.h create mode 100644 src/plugin/metar/MetarModule.cpp create mode 100644 src/plugin/metar/MetarModule.h delete mode 100644 src/plugin/metar/MetarParsingFunctions.cpp delete mode 100644 src/plugin/metar/MetarParsingFunctions.h create mode 100644 src/plugin/metar/MetarsUpdatedPushEventProcessor.cpp create mode 100644 src/plugin/metar/MetarsUpdatedPushEventProcessor.h create mode 100644 src/plugin/metar/ParsedMetar.cpp create mode 100644 src/plugin/metar/ParsedMetar.h create mode 100644 src/plugin/metar/ParsedMetarCollection.cpp create mode 100644 src/plugin/metar/ParsedMetarCollection.h create mode 100644 src/plugin/metar/ParsedMetarFactory.cpp create mode 100644 src/plugin/metar/ParsedMetarFactory.h create mode 100644 src/plugin/metar/PressureComponent.cpp create mode 100644 src/plugin/metar/PressureComponent.h create mode 100644 src/plugin/metar/PressureComponentFactory.cpp create mode 100644 src/plugin/metar/PressureComponentFactory.h create mode 100644 src/plugin/metar/PressureNotFoundMessage.cpp create mode 100644 src/plugin/metar/PressureNotFoundMessage.h create mode 100644 src/plugin/metar/PressureQueryCommandHandler.cpp create mode 100644 src/plugin/metar/PressureQueryCommandHandler.h create mode 100644 src/plugin/metar/PressureQueryMessage.cpp create mode 100644 src/plugin/metar/PressureQueryMessage.h create mode 100644 src/plugin/metar/PressureUnit.h create mode 100644 test/plugin/metar/MetarComponentsFactoryTest.cpp create mode 100644 test/plugin/metar/MetarModuleTest.cpp delete mode 100644 test/plugin/metar/MetarParsingFunctionsTest.cpp create mode 100644 test/plugin/metar/MetarsUpdatedPushEventProcessorTest.cpp create mode 100644 test/plugin/metar/ParsedMetarCollectionTest.cpp create mode 100644 test/plugin/metar/ParsedMetarFactoryTest.cpp create mode 100644 test/plugin/metar/ParsedMetarTest.cpp create mode 100644 test/plugin/metar/PressureComponentFactoryTest.cpp create mode 100644 test/plugin/metar/PressureComponentTest.cpp create mode 100644 test/plugin/metar/PressureNotFoundMessageTest.cpp create mode 100644 test/plugin/metar/PressureQueryCommandHandlerTest.cpp create mode 100644 test/plugin/metar/PressureQueryMessageTest.cpp delete mode 100644 test/plugin/mock/MockMinStack.h diff --git a/docs/UserGuide/Features/Features.md b/docs/UserGuide/Features/Features.md index 2ec992e5e..283efc7c8 100644 --- a/docs/UserGuide/Features/Features.md +++ b/docs/UserGuide/Features/Features.md @@ -16,7 +16,7 @@ dynamically to the plugin. - Actual off-block times - Estimated departure times - UK Wake Categories -- QNH change notifications +- [Pressure Monitor and Notifications](PressureMonitor.md) - Hold Manager, synced between controllers - [Departure Handoff Indicator](Handoffs.md) - Altimeter setting regions and regional pressures diff --git a/docs/UserGuide/Features/PressureMonitor.md b/docs/UserGuide/Features/PressureMonitor.md new file mode 100644 index 000000000..1f7feafcb --- /dev/null +++ b/docs/UserGuide/Features/PressureMonitor.md @@ -0,0 +1,25 @@ +# Pressure Monitor + +The plugin provides functionality for aerodrome controllers to monitor changes in pressure. This allows controllers to +be notified of important pressure changes so that they can then update pilots. + +## Configuration + +Configuration for the Pressure Monitor is under the "General Settings" option of the `OP` menu. + +## Pressure Update Notifications + +Pressure update notifications are displayed to controllers in the `UKCP_PRESSURE_MONITOR` chat handler. + +These messages will display the QNH/QFE before and after. Messages will not be sent for the first time a pressure is +updated, or if the pressure does not change between METARs. + +## Querying Pressure + +Users may query the plugin to provide the current QNH and QFE at any airfield they wish. + +This is achieved by typing a command into the EuroScope chat box, as follows: + +`.ukcp pressure EGKK` + +The plugin will then respond in the `UKCP_PRESSURE_MONITOR` with the current QNH and QFE at the given airfield. diff --git a/src/plugin/CMakeLists.txt b/src/plugin/CMakeLists.txt index fc1efeeb5..5bdde58a9 100644 --- a/src/plugin/CMakeLists.txt +++ b/src/plugin/CMakeLists.txt @@ -450,16 +450,29 @@ source_group("src\\message" FILES ${src__message}) set(src__metar "metar/MetarEventHandlerCollection.cpp" "metar/MetarEventHandlerCollection.h" + metar/MetarEventHandlerInterface.cpp "metar/MetarEventHandlerInterface.h" - "metar/MetarParsingFunctions.cpp" - "metar/MetarParsingFunctions.h" "metar/PressureChangeMessage.cpp" "metar/PressureChangeMessage.h" "metar/PressureMonitor.cpp" "metar/PressureMonitor.h" "metar/PressureMonitorBootstrap.cpp" "metar/PressureMonitorBootstrap.h" - metar/MetarEventHandlerInterface.cpp) + metar/ParsedMetar.cpp metar/ParsedMetar.h + metar/PressureComponent.h metar/PressureComponent.cpp + metar/PressureUnit.h + metar/MetarComponents.h + metar/ParsedMetarCollection.cpp metar/ParsedMetarCollection.h + metar/MetarComponentsFactory.cpp metar/MetarComponentsFactory.h + metar/MetarComponentFactoryInterface.h + metar/PressureComponentFactory.cpp metar/PressureComponentFactory.h + metar/MetarComponentsFactoryFactory.cpp metar/MetarComponentsFactoryFactory.h + metar/MetarsUpdatedPushEventProcessor.cpp metar/MetarsUpdatedPushEventProcessor.h + metar/ParsedMetarFactory.cpp metar/ParsedMetarFactory.h + metar/MetarModule.cpp metar/MetarModule.h + metar/PressureQueryCommandHandler.cpp metar/PressureQueryCommandHandler.h + metar/PressureQueryMessage.cpp metar/PressureQueryMessage.h + metar/PressureNotFoundMessage.cpp metar/PressureNotFoundMessage.h) source_group("src\\metar" FILES ${src__metar}) set(src__minstack diff --git a/src/plugin/airfield/AirfieldCollection.cpp b/src/plugin/airfield/AirfieldCollection.cpp index cadf8b5f3..201befa18 100644 --- a/src/plugin/airfield/AirfieldCollection.cpp +++ b/src/plugin/airfield/AirfieldCollection.cpp @@ -47,4 +47,9 @@ namespace UKControllerPlugin::Airfield { callback(*airfieldMapping.second); } } + + auto AirfieldCollection::FetchById(int id) const -> std::shared_ptr + { + return this->airfieldMap.count(id) != 0 ? this->airfieldMap.at(id) : nullptr; + } } // namespace UKControllerPlugin::Airfield diff --git a/src/plugin/airfield/AirfieldCollection.h b/src/plugin/airfield/AirfieldCollection.h index 1dca269c2..a79ea5943 100644 --- a/src/plugin/airfield/AirfieldCollection.h +++ b/src/plugin/airfield/AirfieldCollection.h @@ -18,6 +18,7 @@ namespace UKControllerPlugin::Airfield { auto operator=(const AirfieldCollection&) -> AirfieldCollection& = delete; auto operator=(AirfieldCollection&&) noexcept -> AirfieldCollection&; void AddAirfield(std::shared_ptr airfield); + [[nodiscard]] auto FetchById(int id) const -> std::shared_ptr; [[nodiscard]] auto FetchAirfieldByIcao(const std::string& icao) const -> std::shared_ptr; void ForEach(const std::function& callback) const; [[nodiscard]] auto GetSize() const -> size_t; diff --git a/src/plugin/bootstrap/CollectionBootstrap.cpp b/src/plugin/bootstrap/CollectionBootstrap.cpp index c66b3247c..69bd7b76a 100644 --- a/src/plugin/bootstrap/CollectionBootstrap.cpp +++ b/src/plugin/bootstrap/CollectionBootstrap.cpp @@ -5,7 +5,6 @@ #include "dependency/DependencyLoaderInterface.h" #include "flightplan/FlightPlanEventHandlerCollection.h" #include "flightplan/StoredFlightplanCollection.h" -#include "metar/MetarEventHandlerCollection.h" #include "ownership/AirfieldOwnershipManager.h" #include "radarscreen/RadarRenderableCollection.h" @@ -14,7 +13,6 @@ using UKControllerPlugin::Command::CommandHandlerCollection; using UKControllerPlugin::Controller::ActiveCallsignCollection; using UKControllerPlugin::Dependency::DependencyLoaderInterface; using UKControllerPlugin::Flightplan::StoredFlightplanCollection; -using UKControllerPlugin::Metar::MetarEventHandlerCollection; using UKControllerPlugin::RadarScreen::RadarRenderableCollection; namespace UKControllerPlugin::Bootstrap { diff --git a/src/plugin/bootstrap/EventHandlerCollectionBootstrap.cpp b/src/plugin/bootstrap/EventHandlerCollectionBootstrap.cpp index 43d55c208..a57e10fec 100644 --- a/src/plugin/bootstrap/EventHandlerCollectionBootstrap.cpp +++ b/src/plugin/bootstrap/EventHandlerCollectionBootstrap.cpp @@ -1,51 +1,45 @@ -#include "pch/pch.h" #include "bootstrap/EventHandlerCollectionBootstrap.h" #include "bootstrap/PersistenceContainer.h" -#include "tag/TagItemCollection.h" +#include "command/CommandHandlerCollection.h" +#include "controller/HandoffEventHandlerCollection.h" +#include "controller/ControllerStatusEventHandlerCollection.h" #include "euroscope/RadarTargetEventHandlerCollection.h" #include "euroscope/RunwayDialogAwareCollection.h" +#include "euroscope/UserSettingAwareCollection.h" #include "flightplan/FlightPlanEventHandlerCollection.h" -#include "controller/ControllerStatusEventHandlerCollection.h" -#include "timedevent/TimedEventCollection.h" #include "plugin/FunctionCallEventHandler.h" -#include "metar/MetarEventHandlerCollection.h" -#include "euroscope/UserSettingAwareCollection.h" #include "plugin/UKPlugin.h" -#include "command/CommandHandlerCollection.h" -#include "controller/HandoffEventHandlerCollection.h" +#include "tag/TagItemCollection.h" +#include "timedevent/TimedEventCollection.h" using UKControllerPlugin::Bootstrap::PersistenceContainer; -using UKControllerPlugin::Tag::TagItemCollection; +using UKControllerPlugin::Command::CommandHandlerCollection; +using UKControllerPlugin::Controller::ControllerStatusEventHandlerCollection; +using UKControllerPlugin::Controller::HandoffEventHandlerCollection; using UKControllerPlugin::Euroscope::RadarTargetEventHandlerCollection; +using UKControllerPlugin::Euroscope::RunwayDialogAwareCollection; +using UKControllerPlugin::Euroscope::UserSettingAwareCollection; using UKControllerPlugin::Flightplan::FlightPlanEventHandlerCollection; -using UKControllerPlugin::Controller::ControllerStatusEventHandlerCollection; -using UKControllerPlugin::TimedEvent::TimedEventCollection; using UKControllerPlugin::Plugin::FunctionCallEventHandler; -using UKControllerPlugin::Metar::MetarEventHandlerCollection; -using UKControllerPlugin::Euroscope::UserSettingAwareCollection; -using UKControllerPlugin::Command::CommandHandlerCollection; -using UKControllerPlugin::Euroscope::RunwayDialogAwareCollection; -using UKControllerPlugin::Controller::HandoffEventHandlerCollection; +using UKControllerPlugin::Tag::TagItemCollection; +using UKControllerPlugin::TimedEvent::TimedEventCollection; -namespace UKControllerPlugin { - namespace Bootstrap { +namespace UKControllerPlugin::Bootstrap { - /* - Set up all the event handler collections. - */ - void EventHandlerCollectionBootstrap::BoostrapPlugin(PersistenceContainer & persistence) - { - persistence.tagHandler.reset(new TagItemCollection); - persistence.radarTargetHandler.reset(new RadarTargetEventHandlerCollection); - persistence.flightplanHandler.reset(new FlightPlanEventHandlerCollection); - persistence.controllerHandler.reset(new ControllerStatusEventHandlerCollection); - persistence.timedHandler.reset(new TimedEventCollection); - persistence.pluginFunctionHandlers.reset(new FunctionCallEventHandler); - persistence.metarEventHandler.reset(new MetarEventHandlerCollection); - persistence.userSettingHandlers.reset(new UserSettingAwareCollection); - persistence.commandHandlers.reset(new CommandHandlerCollection); - persistence.runwayDialogEventHandlers.reset(new RunwayDialogAwareCollection); - persistence.controllerHandoffHandlers.reset(new HandoffEventHandlerCollection); - } - } // namespace Bootstrap -} // namespace UKControllerPlugin + /* + Set up all the event handler collections. + */ + void EventHandlerCollectionBootstrap::BoostrapPlugin(PersistenceContainer& persistence) + { + persistence.tagHandler = std::make_unique(); + persistence.radarTargetHandler = std::make_unique(); + persistence.flightplanHandler = std::make_unique(); + persistence.controllerHandler = std::make_unique(); + persistence.timedHandler = std::make_unique(); + persistence.pluginFunctionHandlers = std::make_unique(); + persistence.userSettingHandlers = std::make_unique(); + persistence.commandHandlers = std::make_unique(); + persistence.runwayDialogEventHandlers = std::make_unique(); + persistence.controllerHandoffHandlers = std::make_unique(); + } +} // namespace UKControllerPlugin::Bootstrap diff --git a/src/plugin/bootstrap/InitialisePlugin.cpp b/src/plugin/bootstrap/InitialisePlugin.cpp index 4d027d870..9e75e9555 100644 --- a/src/plugin/bootstrap/InitialisePlugin.cpp +++ b/src/plugin/bootstrap/InitialisePlugin.cpp @@ -26,6 +26,7 @@ #include "log/LoggerBootstrap.h" #include "login/LoginModule.h" #include "message/UserMessagerBootstrap.h" +#include "metar/MetarModule.h" #include "metar/PressureMonitorBootstrap.h" #include "minstack/MinStackModule.h" #include "missedapproach/MissedApproachModule.h" @@ -209,6 +210,7 @@ namespace UKControllerPlugin { *this->container->userSettingHandlers); // Bootstrap the modules + Metar::BootstrapPlugin(*this->container); InitialAltitudeModule::BootstrapPlugin(*this->container); InitialHeading::BootstrapPlugin(*this->container); Srd::BootstrapPlugin(*this->container); diff --git a/src/plugin/message/MessageSerializableInterface.h b/src/plugin/message/MessageSerializableInterface.h index 55382b4e1..b324a151d 100644 --- a/src/plugin/message/MessageSerializableInterface.h +++ b/src/plugin/message/MessageSerializableInterface.h @@ -20,46 +20,46 @@ namespace UKControllerPlugin::Message { Returns the handler (or, tab) in the chat area that the message will be displayed in. */ - virtual std::string MessageHandler(void) const = 0; + [[nodiscard]] virtual auto MessageHandler() const -> std::string = 0; /* Returns who the message comes from. */ - virtual std::string MessageSender(void) const = 0; + [[nodiscard]] virtual auto MessageSender() const -> std::string = 0; /* Returns the string of the message. */ - virtual std::string MessageString(void) const = 0; + [[nodiscard]] virtual auto MessageString() const -> std::string = 0; /* Returns whether or not the message handler should be shown when this message is sent. */ - virtual bool MessageShowHandler(void) const = 0; + [[nodiscard]] virtual auto MessageShowHandler() const -> bool = 0; /* Returns whether or not the message handler should be marked as unread when this message is sent. */ - virtual bool MessageMarkUnread(void) const = 0; + [[nodiscard]] virtual auto MessageMarkUnread() const -> bool = 0; /* Returns whether or not the message should override the busy indicator of the controller. */ - virtual bool MessageOverrideBusy(void) const = 0; + [[nodiscard]] virtual auto MessageOverrideBusy() const -> bool = 0; /* Returns whether or not the sending of this message should flash the chat handler. */ - virtual bool MessageFlashHandler(void) const = 0; + [[nodiscard]] virtual auto MessageFlashHandler() const -> bool = 0; /* Returns whether or not this message requires confirmation from the user that they have read it. */ - virtual bool MessageRequiresConfirm(void) const = 0; + [[nodiscard]] virtual auto MessageRequiresConfirm() const -> bool = 0; }; } // namespace UKControllerPlugin::Message diff --git a/src/plugin/metar/MetarComponentFactoryInterface.h b/src/plugin/metar/MetarComponentFactoryInterface.h new file mode 100644 index 000000000..56a4d8414 --- /dev/null +++ b/src/plugin/metar/MetarComponentFactoryInterface.h @@ -0,0 +1,15 @@ +#pragma once + +namespace UKControllerPlugin::Metar { + struct MetarComponents; + + /** + * Adds a component of a METAR to the MetarComponents + */ + class MetarComponentFactoryInterface + { + public: + virtual ~MetarComponentFactoryInterface() = default; + virtual void FromJson(const nlohmann::json& json, MetarComponents& components) const = 0; + }; +} // namespace UKControllerPlugin::Metar diff --git a/src/plugin/metar/MetarComponents.h b/src/plugin/metar/MetarComponents.h new file mode 100644 index 000000000..ed6f160a2 --- /dev/null +++ b/src/plugin/metar/MetarComponents.h @@ -0,0 +1,13 @@ +#pragma once + +namespace UKControllerPlugin::Metar { + class PressureComponent; + + /** + * Struct containing the individual components of the METAR + */ + using MetarComponents = struct MetarComponents + { + std::shared_ptr pressure; + }; +} // namespace UKControllerPlugin::Metar diff --git a/src/plugin/metar/MetarComponentsFactory.cpp b/src/plugin/metar/MetarComponentsFactory.cpp new file mode 100644 index 000000000..a4c880099 --- /dev/null +++ b/src/plugin/metar/MetarComponentsFactory.cpp @@ -0,0 +1,26 @@ +#include "MetarComponentFactoryInterface.h" +#include "MetarComponents.h" +#include "MetarComponentsFactory.h" + +namespace UKControllerPlugin::Metar { + + MetarComponentsFactory::MetarComponentsFactory(std::set> factories) + : factories(std::move(factories)) + { + } + + auto MetarComponentsFactory::FromJson(const nlohmann::json& json) const -> std::unique_ptr + { + auto components = std::make_unique(); + if (!json.is_object()) { + LogError("Parsed metar json is not object"); + return components; + } + + for (const auto& factory : factories) { + factory->FromJson(json, *components); + } + + return components; + } +} // namespace UKControllerPlugin::Metar diff --git a/src/plugin/metar/MetarComponentsFactory.h b/src/plugin/metar/MetarComponentsFactory.h new file mode 100644 index 000000000..910dacb15 --- /dev/null +++ b/src/plugin/metar/MetarComponentsFactory.h @@ -0,0 +1,20 @@ +#pragma once + +namespace UKControllerPlugin::Metar { + struct MetarComponents; + class MetarComponentFactoryInterface; + + /** + * Builds the components of a METAR from JSON + */ + class MetarComponentsFactory + { + public: + MetarComponentsFactory(std::set> factories); + [[nodiscard]] auto FromJson(const nlohmann::json& json) const -> std::unique_ptr; + + private: + // The factories + std::set> factories; + }; +} // namespace UKControllerPlugin::Metar diff --git a/src/plugin/metar/MetarComponentsFactoryFactory.cpp b/src/plugin/metar/MetarComponentsFactoryFactory.cpp new file mode 100644 index 000000000..99dfc55b5 --- /dev/null +++ b/src/plugin/metar/MetarComponentsFactoryFactory.cpp @@ -0,0 +1,12 @@ +#include "MetarComponentsFactory.h" +#include "MetarComponentsFactoryFactory.h" +#include "MetarComponentFactoryInterface.h" +#include "PressureComponentFactory.h" + +namespace UKControllerPlugin::Metar { + auto BuildComponentsFactory() -> std::shared_ptr + { + return std::make_shared( + std::set>{std::make_shared()}); + } +} // namespace UKControllerPlugin::Metar diff --git a/src/plugin/metar/MetarComponentsFactoryFactory.h b/src/plugin/metar/MetarComponentsFactoryFactory.h new file mode 100644 index 000000000..06052386c --- /dev/null +++ b/src/plugin/metar/MetarComponentsFactoryFactory.h @@ -0,0 +1,7 @@ +#pragma once + +namespace UKControllerPlugin::Metar { + class MetarComponentsFactory; + + [[nodiscard]] auto BuildComponentsFactory() -> std::shared_ptr; +} // namespace UKControllerPlugin::Metar diff --git a/src/plugin/metar/MetarEventHandlerCollection.cpp b/src/plugin/metar/MetarEventHandlerCollection.cpp index 3087c590a..9ee1f4535 100644 --- a/src/plugin/metar/MetarEventHandlerCollection.cpp +++ b/src/plugin/metar/MetarEventHandlerCollection.cpp @@ -1,41 +1,35 @@ -#include "pch/pch.h" -#include "metar/MetarEventHandlerCollection.h" -#include "metar/MetarEventHandlerInterface.h" +#include "MetarEventHandlerCollection.h" +#include "MetarEventHandlerInterface.h" using UKControllerPlugin::Metar::MetarEventHandlerInterface; -namespace UKControllerPlugin { - namespace Metar { +namespace UKControllerPlugin::Metar { + /* + Returns the number of registered handlers. + */ + auto MetarEventHandlerCollection::CountHandlers() const -> size_t + { + return this->handlers.size(); + } - /* - Returns the number of registered handlers. - */ - int MetarEventHandlerCollection::CountHandlers(void) const - { - return this->handlers.size(); + /* + Loop through all the handlers and call their metar event. + */ + void MetarEventHandlerCollection::UpdatedMetarEvent(const ParsedMetar& metar) const + { + for (const auto& handler : this->handlers) { + handler->MetarUpdated(metar); } + } - /* - Loop through all the handlers and call their metar event. - */ - void MetarEventHandlerCollection::NewMetarEvent(std::string station, std::string metar) const - { - for ( - std::set>::const_iterator it = this->handlers.cbegin(); - it != this->handlers.cend(); - ++it - ) { - (*it)->NewMetar(station, metar); - } + /* + Add a handler to the collection. + */ + void MetarEventHandlerCollection::RegisterHandler(std::shared_ptr handler) + { + if (!this->handlers.insert(handler).second) { + LogWarning("Duplicate metar handler added"); } - - /* - Add a handler to the collection. - */ - void MetarEventHandlerCollection::RegisterHandler(std::shared_ptr handler) - { - this->handlers.insert(handler); - } - } // namespace Metar -} // namespace UKControllerPlugin + } +} // namespace UKControllerPlugin::Metar diff --git a/src/plugin/metar/MetarEventHandlerCollection.h b/src/plugin/metar/MetarEventHandlerCollection.h index d536c7f0d..617ca669c 100644 --- a/src/plugin/metar/MetarEventHandlerCollection.h +++ b/src/plugin/metar/MetarEventHandlerCollection.h @@ -1,29 +1,21 @@ #pragma once -// Forward declarations -namespace UKControllerPlugin { - namespace Metar { - class MetarEventHandlerInterface; - } // namespace Metar -} // namespace UKControllerPlugin -// END +namespace UKControllerPlugin::Metar { + class MetarEventHandlerInterface; + class ParsedMetar; -namespace UKControllerPlugin { - namespace Metar { + /* + A class that stores pointers to classes that want to know + when a new METAR comes in. + */ + class MetarEventHandlerCollection + { + public: + [[nodiscard]] auto CountHandlers() const -> size_t; + void UpdatedMetarEvent(const ParsedMetar& metar) const; + void RegisterHandler(std::shared_ptr handler); - /* - A class that stores pointers to classes that want to know - when a new METAR comes in. - */ - class MetarEventHandlerCollection - { - public: - int CountHandlers(void) const; - void NewMetarEvent(std::string station, std::string metar) const; - void RegisterHandler(std::shared_ptr handler); - - private: - std::set> handlers; - }; - } // namespace Metar -} // namespace UKControllerPlugin + private: + std::set> handlers; + }; +} // namespace UKControllerPlugin::Metar diff --git a/src/plugin/metar/MetarEventHandlerInterface.h b/src/plugin/metar/MetarEventHandlerInterface.h index b59406f60..10b5ae97a 100644 --- a/src/plugin/metar/MetarEventHandlerInterface.h +++ b/src/plugin/metar/MetarEventHandlerInterface.h @@ -1,6 +1,7 @@ #pragma once namespace UKControllerPlugin::Metar { + class ParsedMetar; /* An interface to be implemented by classes that need to react to a new METAR coming in. @@ -14,6 +15,6 @@ namespace UKControllerPlugin::Metar { MetarEventHandlerInterface(MetarEventHandlerInterface&&) noexcept; [[nodiscard]] auto operator=(const MetarEventHandlerInterface&) -> MetarEventHandlerInterface&; [[nodiscard]] auto operator=(MetarEventHandlerInterface&&) noexcept -> MetarEventHandlerInterface&; - virtual void NewMetar(std::string station, std::string metar) = 0; + virtual void MetarUpdated(const ParsedMetar& metar) = 0; }; } // namespace UKControllerPlugin::Metar diff --git a/src/plugin/metar/MetarModule.cpp b/src/plugin/metar/MetarModule.cpp new file mode 100644 index 000000000..75a0d871f --- /dev/null +++ b/src/plugin/metar/MetarModule.cpp @@ -0,0 +1,37 @@ +#include "MetarComponentsFactory.h" +#include "MetarComponentsFactoryFactory.h" +#include "MetarEventHandlerCollection.h" +#include "MetarModule.h" +#include "MetarsUpdatedPushEventProcessor.h" +#include "ParsedMetarCollection.h" +#include "ParsedMetarFactory.h" +#include "PressureQueryCommandHandler.h" +#include "bootstrap/PersistenceContainer.h" +#include "command/CommandHandlerCollection.h" +#include "push/PushEventProcessorCollection.h" + +namespace UKControllerPlugin::Metar { + + std::unique_ptr parsedMetars; // NOLINT + std::shared_ptr componentsFactory; // NOLINT + std::unique_ptr parsedMetarFactory; // NOLINT + + void BootstrapPlugin(Bootstrap::PersistenceContainer& container) + { + // Make the bits + parsedMetars = std::make_unique(); + componentsFactory = BuildComponentsFactory(); + parsedMetarFactory = std::make_unique(*componentsFactory, *container.airfields); + + // Collection for handling METAR events + container.metarEventHandler = std::make_unique(); + + // Handle all the push events for METARs + container.pushEventProcessors->AddProcessor( + std::make_shared(*parsedMetars, *parsedMetarFactory, *container.api)); + + // Handler for the pressure query command + container.commandHandlers->RegisterHandler( + std::make_shared(*parsedMetars, *container.userMessager)); + } +} // namespace UKControllerPlugin::Metar diff --git a/src/plugin/metar/MetarModule.h b/src/plugin/metar/MetarModule.h new file mode 100644 index 000000000..cc5e2e336 --- /dev/null +++ b/src/plugin/metar/MetarModule.h @@ -0,0 +1,9 @@ +#pragma once + +namespace UKControllerPlugin::Bootstrap { + struct PersistenceContainer; +} // namespace UKControllerPlugin::Bootstrap + +namespace UKControllerPlugin::Metar { + void BootstrapPlugin(Bootstrap::PersistenceContainer& container); +} // namespace UKControllerPlugin::Metar diff --git a/src/plugin/metar/MetarParsingFunctions.cpp b/src/plugin/metar/MetarParsingFunctions.cpp deleted file mode 100644 index 82409e7cd..000000000 --- a/src/plugin/metar/MetarParsingFunctions.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "pch/pch.h" -#include "metar/MetarParsingFunctions.h" - -namespace UKControllerPlugin { - namespace Metar { - - const std::string noQnh = "0000"; - - /* - Return the QNH string from a METAR if available. - */ - std::string GetQnhString(std::string metar) - { - std::regex qnhPattern(" Q([0-9]{4})( |$)"); - std::smatch qnhMatch; - - if (!std::regex_search(metar, qnhMatch, qnhPattern)) { - return noQnh; - } - - return qnhMatch[1]; - } - } // namespace Metar -} // namespace UKControllerPlugin diff --git a/src/plugin/metar/MetarParsingFunctions.h b/src/plugin/metar/MetarParsingFunctions.h deleted file mode 100644 index 08ed09e4b..000000000 --- a/src/plugin/metar/MetarParsingFunctions.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -namespace UKControllerPlugin { - namespace Metar { - - extern const std::string noQnh; - - std::string GetQnhString(std::string metar); - } // namespace Metar -} // namespace UKControllerPlugin diff --git a/src/plugin/metar/MetarsUpdatedPushEventProcessor.cpp b/src/plugin/metar/MetarsUpdatedPushEventProcessor.cpp new file mode 100644 index 000000000..fb9e73da6 --- /dev/null +++ b/src/plugin/metar/MetarsUpdatedPushEventProcessor.cpp @@ -0,0 +1,55 @@ +#include "MetarsUpdatedPushEventProcessor.h" +#include "ParsedMetarCollection.h" +#include "ParsedMetar.h" +#include "ParsedMetarFactory.h" +#include "api/ApiException.h" +#include "api/ApiInterface.h" +#include "push/PushEventSubscription.h" + +namespace UKControllerPlugin::Metar { + + MetarsUpdatedPushEventProcessor::MetarsUpdatedPushEventProcessor( + ParsedMetarCollection& metars, const ParsedMetarFactory& factory, const Api::ApiInterface& api) + : metars(metars), factory(factory), api(api) + { + } + + void MetarsUpdatedPushEventProcessor::ProcessPushEvent(const Push::PushEvent& message) + { + this->ProcessMetarsUpdatedJson(message.data); + } + + auto MetarsUpdatedPushEventProcessor::GetPushEventSubscriptions() const -> std::set + { + return {{Push::PushEventSubscription::SUB_TYPE_EVENT, "metars.updated"}}; + } + + void MetarsUpdatedPushEventProcessor::PluginEventsSynced() + { + Async([this]() { + try { + this->ProcessMetarsUpdatedJson(this->api.GetAllMetars()); + LogInfo("Loaded " + std::to_string(this->metars.Count()) + " METARs"); + } catch (Api::ApiException) { + } + }); + } + + void MetarsUpdatedPushEventProcessor::ProcessMetarsUpdatedJson(const nlohmann::json& data) + { + if (!data.is_array()) { + LogError("Invalid metar data: " + data.dump()); + return; + } + + for (const auto& metarUpdate : data) { + const auto parsed = factory.FromJson(metarUpdate); + if (parsed == nullptr) { + continue; + } + + metars.UpdateMetar(parsed); + LogInfo("Received updated METAR: " + parsed->Raw()); + } + } +} // namespace UKControllerPlugin::Metar diff --git a/src/plugin/metar/MetarsUpdatedPushEventProcessor.h b/src/plugin/metar/MetarsUpdatedPushEventProcessor.h new file mode 100644 index 000000000..2fcb37441 --- /dev/null +++ b/src/plugin/metar/MetarsUpdatedPushEventProcessor.h @@ -0,0 +1,37 @@ +#pragma once +#include "push/PushEventProcessorInterface.h" + +namespace UKControllerPlugin::Api { + class ApiInterface; +} // namespace UKControllerPlugin::Api + +namespace UKControllerPlugin::Metar { + class ParsedMetarCollection; + class ParsedMetarFactory; + + /** + * Handles push events related to METARs updating + */ + class MetarsUpdatedPushEventProcessor : public Push::PushEventProcessorInterface + { + public: + MetarsUpdatedPushEventProcessor( + ParsedMetarCollection& metars, const ParsedMetarFactory& factory, const Api::ApiInterface& api); + void ProcessPushEvent(const Push::PushEvent& message) override; + [[nodiscard]] auto GetPushEventSubscriptions() const -> std::set override; + void PluginEventsSynced() override; + + private: + void ProcessMetarsUpdatedJson(const nlohmann::json& data); + + // All the parsed metars + ParsedMetarCollection& metars; + + // Builds the parsed metars from json + const ParsedMetarFactory& factory; + + // The API for pulling all METAR updates + const Api::ApiInterface& api; + }; + +} // namespace UKControllerPlugin::Metar diff --git a/src/plugin/metar/ParsedMetar.cpp b/src/plugin/metar/ParsedMetar.cpp new file mode 100644 index 000000000..b319c62ab --- /dev/null +++ b/src/plugin/metar/ParsedMetar.cpp @@ -0,0 +1,28 @@ +#include "MetarComponents.h" +#include "ParsedMetar.h" + +namespace UKControllerPlugin::Metar { + + ParsedMetar::ParsedMetar(std::string airfield, std::string raw, std::unique_ptr components) + : airfield(std::move(airfield)), raw(std::move(raw)), components(std::move(components)) + { + } + + ParsedMetar::~ParsedMetar() = default; + ParsedMetar::ParsedMetar(ParsedMetar&&) noexcept = default; + + auto ParsedMetar::Airfield() const -> const std::string& + { + return airfield; + } + + auto ParsedMetar::Raw() const -> const std::string& + { + return raw; + } + + auto ParsedMetar::Components() const -> const MetarComponents& + { + return *components; + } +} // namespace UKControllerPlugin::Metar diff --git a/src/plugin/metar/ParsedMetar.h b/src/plugin/metar/ParsedMetar.h new file mode 100644 index 000000000..d54fa0ba5 --- /dev/null +++ b/src/plugin/metar/ParsedMetar.h @@ -0,0 +1,34 @@ +#pragma once + +namespace UKControllerPlugin::Metar { + struct MetarComponents; + class PressureComponent; + + /** + * A METAR, that has been parsed into its individual components. + */ + class ParsedMetar + { + public: + ParsedMetar(std::string airfield, std::string raw, std::unique_ptr components); + ~ParsedMetar(); + ParsedMetar(const ParsedMetar&) = delete; + ParsedMetar(ParsedMetar&&) noexcept; + auto operator=(const ParsedMetar&) -> ParsedMetar& = delete; + auto operator=(ParsedMetar&&) noexcept -> ParsedMetar& = delete; + + [[nodiscard]] auto Airfield() const -> const std::string&; + [[nodiscard]] auto Raw() const -> const std::string&; + [[nodiscard]] auto Components() const -> const MetarComponents&; + + private: + // The airfield for the METAR + const std::string airfield; + + // The raw METAR + const std::string raw; + + // The different components of the METAR + std::unique_ptr components; + }; +} // namespace UKControllerPlugin::Metar diff --git a/src/plugin/metar/ParsedMetarCollection.cpp b/src/plugin/metar/ParsedMetarCollection.cpp new file mode 100644 index 000000000..0bc9baaf3 --- /dev/null +++ b/src/plugin/metar/ParsedMetarCollection.cpp @@ -0,0 +1,24 @@ +#include "ParsedMetar.h" +#include "ParsedMetarCollection.h" + +namespace UKControllerPlugin::Metar { + + auto ParsedMetarCollection::Count() const -> size_t + { + auto lock = std::lock_guard(containerLock); + return this->metars.size(); + } + + auto ParsedMetarCollection::GetForAirfield(const std::string& airfield) const -> std::shared_ptr + { + auto lock = std::lock_guard(containerLock); + auto metar = this->metars.find(airfield); + return metar != metars.cend() ? metar->second : nullptr; + } + + void ParsedMetarCollection::UpdateMetar(const std::shared_ptr& metar) + { + auto lock = std::lock_guard(containerLock); + this->metars[metar->Airfield()] = metar; + } +} // namespace UKControllerPlugin::Metar diff --git a/src/plugin/metar/ParsedMetarCollection.h b/src/plugin/metar/ParsedMetarCollection.h new file mode 100644 index 000000000..081bbb470 --- /dev/null +++ b/src/plugin/metar/ParsedMetarCollection.h @@ -0,0 +1,22 @@ +#pragma once + +namespace UKControllerPlugin::Metar { + class ParsedMetar; + + /** + * Stores parsed METAR information + */ + class ParsedMetarCollection + { + public: + [[nodiscard]] auto Count() const -> size_t; + [[nodiscard]] auto GetForAirfield(const std::string& airfield) const -> std::shared_ptr; + void UpdateMetar(const std::shared_ptr& metar); + + private: + std::map> metars; + + // Locks the container + mutable std::mutex containerLock; + }; +} // namespace UKControllerPlugin::Metar diff --git a/src/plugin/metar/ParsedMetarFactory.cpp b/src/plugin/metar/ParsedMetarFactory.cpp new file mode 100644 index 000000000..817bc3a9a --- /dev/null +++ b/src/plugin/metar/ParsedMetarFactory.cpp @@ -0,0 +1,36 @@ +#include "MetarComponents.h" +#include "MetarComponentsFactory.h" +#include "ParsedMetar.h" +#include "ParsedMetarFactory.h" +#include "airfield/AirfieldCollection.h" +#include "airfield/AirfieldModel.h" + +namespace UKControllerPlugin::Metar { + + ParsedMetarFactory::ParsedMetarFactory( + const MetarComponentsFactory& componentsFactory, const Airfield::AirfieldCollection& airfields) + : componentsFactory(componentsFactory), airfields(airfields) + { + } + + auto ParsedMetarFactory::FromJson(const nlohmann::json& metarData) const -> std::shared_ptr + { + if (!MessageValid(metarData)) { + LogError("Metar json is not valid"); + return nullptr; + } + + return std::make_shared( + airfields.FetchById(metarData.at("airfield_id").get())->Icao(), + metarData.at("raw").get(), + componentsFactory.FromJson(metarData.at("parsed"))); + } + + auto ParsedMetarFactory::MessageValid(const nlohmann::json& message) const -> bool + { + return message.is_object() && message.contains("airfield_id") && + message.at("airfield_id").is_number_integer() && + airfields.FetchById(message.at("airfield_id").get()) != nullptr && message.contains("raw") && + message.at("raw").is_string() && message.contains("parsed") && message.at("parsed").is_object(); + } +} // namespace UKControllerPlugin::Metar diff --git a/src/plugin/metar/ParsedMetarFactory.h b/src/plugin/metar/ParsedMetarFactory.h new file mode 100644 index 000000000..bc9ed4a51 --- /dev/null +++ b/src/plugin/metar/ParsedMetarFactory.h @@ -0,0 +1,30 @@ +#pragma once + +namespace UKControllerPlugin::Airfield { + class AirfieldCollection; +} // namespace UKControllerPlugin::Airfield + +namespace UKControllerPlugin::Metar { + class ParsedMetar; + class MetarComponentsFactory; + + /** + * Builds a ParsedMetar from JSON + */ + class ParsedMetarFactory + { + public: + ParsedMetarFactory( + const MetarComponentsFactory& componentsFactory, const Airfield::AirfieldCollection& airfields); + [[nodiscard]] auto FromJson(const nlohmann::json& metarData) const -> std::shared_ptr; + + private: + [[nodiscard]] auto MessageValid(const nlohmann::json& message) const -> bool; + + // Builds individual METAR components + const MetarComponentsFactory& componentsFactory; + + // All the airfields + const Airfield::AirfieldCollection& airfields; + }; +} // namespace UKControllerPlugin::Metar diff --git a/src/plugin/metar/PressureChangeMessage.cpp b/src/plugin/metar/PressureChangeMessage.cpp index 291aa992e..7250c2d95 100644 --- a/src/plugin/metar/PressureChangeMessage.cpp +++ b/src/plugin/metar/PressureChangeMessage.cpp @@ -1,15 +1,17 @@ #include "PressureChangeMessage.h" +#include "PressureComponent.h" namespace UKControllerPlugin::Metar { - PressureChangeMessage::PressureChangeMessage(std::string station, std::string qnhBefore, std::string qnhAfter) - : qnhBefore(std::move(qnhBefore)), qnhAfter(std::move(qnhAfter)), station(std::move(station)) + PressureChangeMessage::PressureChangeMessage( + std::string airfield, const PressureComponent& pressureBefore, const PressureComponent& pressureNow) + : airfield(airfield), pressureBefore(pressureBefore), pressureNow(pressureNow) { } auto PressureChangeMessage::MessageHandler() const -> std::string { - return "UKCP_QNH"; + return "UKCP_PRESSURE_MONITOR"; } auto PressureChangeMessage::MessageSender() const -> std::string @@ -19,7 +21,8 @@ namespace UKControllerPlugin::Metar { auto PressureChangeMessage::MessageString() const -> std::string { - return "New QNH at " + station + ", Was: " + qnhBefore + ", Now: " + qnhAfter; + return "Pressure change at " + airfield + ". QNH was " + this->QnhBefore() + ", now " + this->QnhAfter() + + ". QFE was " + this->QfeBefore() + ", now " + this->QfeAfter() + "."; } auto PressureChangeMessage::MessageShowHandler() const -> bool @@ -46,4 +49,35 @@ namespace UKControllerPlugin::Metar { { return true; } + + auto PressureChangeMessage::QfeBefore() const -> std::string + { + return this->pressureBefore.ReportedAsHectopascals() ? std::to_string(this->pressureBefore.QfeHectopascals()) + : FormatInHg(this->pressureBefore.QfeInHg()); + } + + auto PressureChangeMessage::QfeAfter() const -> std::string + { + return this->pressureNow.ReportedAsHectopascals() ? std::to_string(this->pressureNow.QfeHectopascals()) + : FormatInHg(this->pressureNow.QfeInHg()); + } + + auto PressureChangeMessage::QnhBefore() const -> std::string + { + return this->pressureBefore.ReportedAsHectopascals() ? std::to_string(this->pressureBefore.QnhHectopascals()) + : FormatInHg(this->pressureBefore.QnhInHg()); + } + + auto PressureChangeMessage::QnhAfter() const -> std::string + { + return this->pressureNow.ReportedAsHectopascals() ? std::to_string(this->pressureNow.QnhHectopascals()) + : FormatInHg(this->pressureNow.QnhInHg()); + } + + auto PressureChangeMessage::FormatInHg(float pressure) -> std::string + { + char formatted[INHG_BUFFER_SIZE]; // NOLINT + sprintf_s(formatted, "%.2f", pressure); // NOLINT + return formatted; + } } // namespace UKControllerPlugin::Metar diff --git a/src/plugin/metar/PressureChangeMessage.h b/src/plugin/metar/PressureChangeMessage.h index 142bd120c..2745eea9d 100644 --- a/src/plugin/metar/PressureChangeMessage.h +++ b/src/plugin/metar/PressureChangeMessage.h @@ -1,38 +1,39 @@ #pragma once #include "message/MessageSerializableInterface.h" -namespace UKControllerPlugin { - namespace Metar { +namespace UKControllerPlugin::Metar { + class PressureComponent; - /* - A message to be used when the QNH - changes at an airfield. - */ - class PressureChangeMessage : public UKControllerPlugin::Message::MessageSerializableInterface - { - public: - PressureChangeMessage(std::string station, std::string qnhBefore, std::string qnhAfter); + /* + A message to be used when the pressure at an airfield changes. + */ + class PressureChangeMessage : public UKControllerPlugin::Message::MessageSerializableInterface + { + public: + PressureChangeMessage( + std::string airfield, const PressureComponent& pressureBefore, const PressureComponent& pressureNow); - // Inherited via MessageSerializableInterface - std::string MessageHandler(void) const override; - std::string MessageSender(void) const override; - std::string MessageString(void) const override; - bool MessageShowHandler(void) const override; - bool MessageMarkUnread(void) const override; - bool MessageOverrideBusy(void) const override; - bool MessageFlashHandler(void) const override; - bool MessageRequiresConfirm(void) const override; + // Inherited via MessageSerializableInterface + [[nodiscard]] auto MessageHandler() const -> std::string override; + [[nodiscard]] auto MessageSender() const -> std::string override; + [[nodiscard]] auto MessageString() const -> std::string override; + [[nodiscard]] auto MessageShowHandler() const -> bool override; + [[nodiscard]] auto MessageMarkUnread() const -> bool override; + [[nodiscard]] auto MessageOverrideBusy() const -> bool override; + [[nodiscard]] auto MessageFlashHandler() const -> bool override; + [[nodiscard]] auto MessageRequiresConfirm() const -> bool override; - private: + private: + [[nodiscard]] auto QnhBefore() const -> std::string; + [[nodiscard]] auto QnhAfter() const -> std::string; + [[nodiscard]] auto QfeBefore() const -> std::string; + [[nodiscard]] auto QfeAfter() const -> std::string; + [[nodiscard]] static auto FormatInHg(float pressure) -> std::string; - // The previous QNH stored - const std::string qnhBefore; + std::string airfield; + const PressureComponent& pressureBefore; + const PressureComponent& pressureNow; - // The new QNH that has come in. - const std::string qnhAfter; - - // The station with which the QNH is associated. - const std::string station; - }; - } // namespace Metar -} // namespace UKControllerPlugin + static inline const int INHG_BUFFER_SIZE = 6; + }; +} // namespace UKControllerPlugin::Metar diff --git a/src/plugin/metar/PressureComponent.cpp b/src/plugin/metar/PressureComponent.cpp new file mode 100644 index 000000000..cc130d5a4 --- /dev/null +++ b/src/plugin/metar/PressureComponent.cpp @@ -0,0 +1,45 @@ +#include "PressureComponent.h" + +namespace UKControllerPlugin::Metar { + PressureComponent::PressureComponent( + int qnhHectopascals, int qfeHectopascals, float qnhInHg, float qfeInHg, PressureUnit reportedIn) + : qnhHectopascals(qnhHectopascals), qfeHectopascals(qfeHectopascals), qnhInHg(qnhInHg), qfeInHg(qfeInHg), + reportedIn(reportedIn) + { + } + + auto PressureComponent::QnhHectopascals() const -> int + { + return qnhHectopascals; + } + + auto PressureComponent::QfeHectopascals() const -> int + { + return qfeHectopascals; + } + + auto PressureComponent::QnhInHg() const -> float + { + return qnhInHg; + } + + auto PressureComponent::QfeInHg() const -> float + { + return qfeInHg; + } + + auto PressureComponent::ReportedIn() const -> PressureUnit + { + return reportedIn; + } + + auto PressureComponent::ReportedAsHectopascals() const -> bool + { + return ReportedIn() == PressureUnit::Hectopascals; + } + + auto PressureComponent::ReportedAsInHg() const -> bool + { + return ReportedIn() == PressureUnit::InHg; + } +} // namespace UKControllerPlugin::Metar diff --git a/src/plugin/metar/PressureComponent.h b/src/plugin/metar/PressureComponent.h new file mode 100644 index 000000000..93346fcbd --- /dev/null +++ b/src/plugin/metar/PressureComponent.h @@ -0,0 +1,29 @@ +#pragma once +#include "metar/PressureUnit.h" + +namespace UKControllerPlugin::Metar { + /** + * The pressure component of a METAR, includes + * QNH, QFE in both hPa and inhg. + */ + class PressureComponent + { + public: + PressureComponent( + int qnhHectopascals, int qfeHectopascals, float qnhInHg, float qfeInHg, PressureUnit reportedIn); + [[nodiscard]] auto QnhHectopascals() const -> int; + [[nodiscard]] auto QfeHectopascals() const -> int; + [[nodiscard]] auto QnhInHg() const -> float; + [[nodiscard]] auto QfeInHg() const -> float; + [[nodiscard]] auto ReportedIn() const -> PressureUnit; + [[nodiscard]] auto ReportedAsHectopascals() const -> bool; + [[nodiscard]] auto ReportedAsInHg() const -> bool; + + private: + const int qnhHectopascals; + const int qfeHectopascals; + const float qnhInHg; + const float qfeInHg; + const PressureUnit reportedIn; + }; +} // namespace UKControllerPlugin::Metar diff --git a/src/plugin/metar/PressureComponentFactory.cpp b/src/plugin/metar/PressureComponentFactory.cpp new file mode 100644 index 000000000..691bb3461 --- /dev/null +++ b/src/plugin/metar/PressureComponentFactory.cpp @@ -0,0 +1,49 @@ +#include "MetarComponents.h" +#include "PressureComponent.h" +#include "PressureComponentFactory.h" + +namespace UKControllerPlugin::Metar { + + void PressureComponentFactory::FromJson(const nlohmann::json& json, MetarComponents& components) const + { + if (!JsonValid(json)) { + LogError("Pressure component of metar is invalid: " + json.dump()); + return; + } + + components.pressure = std::make_shared( + json.at("qnh").get(), + json.at("qfe").get(), + json.at("qnh_inhg").get(), + json.at("qfe_inhg").get(), + json.at("pressure_format").get() == "hpa" ? PressureUnit::Hectopascals : PressureUnit::InHg); + } + + auto PressureComponentFactory::JsonValid(const nlohmann::json& json) const -> bool + { + return PressureFormatValid(json) && HectopascalPressuresValid(json) && InHgPressuresValid(json); + } + + auto PressureComponentFactory::PressureFormatValid(const nlohmann::json& json) const -> bool + { + return json.contains("pressure_format") && json.at("pressure_format").is_string() && + PressureFormatValidUnit(json.at("pressure_format").get()); + } + + auto PressureComponentFactory::PressureFormatValidUnit(const std::string& pressureFormat) -> bool + { + return pressureFormat == "hpa" || pressureFormat == "inhg"; + } + + auto PressureComponentFactory::InHgPressuresValid(const nlohmann::json& json) -> bool + { + return json.contains("qnh_inhg") && json.at("qnh_inhg").is_number() && json.contains("qfe_inhg") && + json.at("qfe_inhg").is_number(); + } + + auto PressureComponentFactory::HectopascalPressuresValid(const nlohmann::json& json) -> bool + { + return json.contains("qnh") && json.at("qnh").is_number_integer() && json.contains("qfe") && + json.at("qfe").is_number_integer(); + } +} // namespace UKControllerPlugin::Metar diff --git a/src/plugin/metar/PressureComponentFactory.h b/src/plugin/metar/PressureComponentFactory.h new file mode 100644 index 000000000..103b47f06 --- /dev/null +++ b/src/plugin/metar/PressureComponentFactory.h @@ -0,0 +1,18 @@ +#pragma once +#include "MetarComponentFactoryInterface.h" +#include "json/json.hpp" + +namespace UKControllerPlugin::Metar { + class PressureComponentFactory : public MetarComponentFactoryInterface + { + public: + void FromJson(const nlohmann::json& json, MetarComponents& components) const override; + + private: + [[nodiscard]] auto JsonValid(const nlohmann::json& json) const -> bool; + [[nodiscard]] static auto HectopascalPressuresValid(const nlohmann::json& json) -> bool; + [[nodiscard]] static auto InHgPressuresValid(const nlohmann::json& json) -> bool; + [[nodiscard]] auto PressureFormatValid(const nlohmann::json& json) const -> bool; + [[nodiscard]] static auto PressureFormatValidUnit(const std::string& pressureFormat) -> bool; + }; +} // namespace UKControllerPlugin::Metar diff --git a/src/plugin/metar/PressureMonitor.cpp b/src/plugin/metar/PressureMonitor.cpp index 095cbac70..6dfc666e6 100644 --- a/src/plugin/metar/PressureMonitor.cpp +++ b/src/plugin/metar/PressureMonitor.cpp @@ -1,93 +1,96 @@ -#include "pch/pch.h" -#include "metar/PressureMonitor.h" -#include "metar/MetarParsingFunctions.h" -#include "metar/PressureChangeMessage.h" -#include "euroscope/GeneralSettingsEntries.h" +#include "MetarComponents.h" +#include "ParsedMetar.h" +#include "PressureChangeMessage.h" +#include "PressureComponent.h" +#include "PressureMonitor.h" #include "controller/ControllerPosition.h" +#include "euroscope/GeneralSettingsEntries.h" -using UKControllerPlugin::Euroscope::UserSetting; +using UKControllerPlugin::Controller::ActiveCallsignCollection; using UKControllerPlugin::Euroscope::GeneralSettingsEntries; +using UKControllerPlugin::Euroscope::UserSetting; using UKControllerPlugin::Message::UserMessager; using UKControllerPlugin::Metar::PressureChangeMessage; -using UKControllerPlugin::Controller::ActiveCallsignCollection; - -namespace UKControllerPlugin { - namespace Metar { - - - PressureMonitor::PressureMonitor(UserMessager & userMessager, const ActiveCallsignCollection& activeCallsigns) - : userMessager(userMessager), activeCallsigns(activeCallsigns) - { +namespace UKControllerPlugin::Metar { + + PressureMonitor::PressureMonitor(UserMessager& userMessager, const ActiveCallsignCollection& activeCallsigns) + : userMessager(userMessager), activeCallsigns(activeCallsigns) + { + } + + /* + Return the QNH stored for a station. + */ + auto PressureMonitor::GetStoredPressure(const std::string& airfield) const -> std::shared_ptr + { + return this->pressures.count(airfield) != 0 ? this->pressures.at(airfield) : nullptr; + } + + /* + Are notifications enabled + */ + bool PressureMonitor::NotificationsEnabled(void) const + { + return this->notificationsEnabled; + } + + /* + Turn notifications on or off + */ + void PressureMonitor::SetNotficationsEnabled(bool enabled) + { + this->notificationsEnabled = enabled; + } + + /* + Update the stored QNH with that of a new METAR. Notify the user if the + QNH has changed. + */ + void PressureMonitor::MetarUpdated(const ParsedMetar& metar) + { + if (metar.Components().pressure == nullptr) { + return; } - /* - Return the QNH stored for a station. - */ - std::string PressureMonitor::GetStoredQnh(std::string station) const - { - return this->qnhs.count(station) ? this->qnhs.at(station) : this->qnhNotStored; + const auto pressureComponent = metar.Components().pressure; + const auto airfield = metar.Airfield(); + if (!this->PressureHasUpdated(airfield, *pressureComponent)) { + this->pressures[airfield] = pressureComponent; + return; } - /* - Are notifications enabled - */ - bool PressureMonitor::NotificationsEnabled(void) const - { - return this->notificationsEnabled; + this->SendNotificationIfRequired(airfield, *pressureComponent); + this->pressures[airfield] = pressureComponent; + } + + /* + User settings have been updated, update local variables from them. + */ + void PressureMonitor::UserSettingsUpdated(UserSetting& userSettings) + { + this->notificationsEnabled = + userSettings.GetBooleanEntry(GeneralSettingsEntries::pressureMonitorSendMessageKey, false); + } + + void + PressureMonitor::SendNotificationIfRequired(const std::string& airfield, const PressureComponent& pressure) const + { + if (!this->notificationsEnabled || !this->activeCallsigns.UserHasCallsign()) { + return; } - /* - Turn notifications on or off - */ - void PressureMonitor::SetNotficationsEnabled(bool enabled) - { - this->notificationsEnabled = enabled; + if (!this->activeCallsigns.GetUserCallsign().GetNormalisedPosition().HasTopdownAirfield(airfield)) { + return; } - /* - Update the stored QNH with that of a new METAR. Notify the user if the - QNH has changed. - */ - void PressureMonitor::NewMetar(std::string station, std::string metar) - { - std::string newQnh = GetQnhString(metar); + this->userMessager.SendMessageToUser(PressureChangeMessage(airfield, *this->pressures.at(airfield), pressure)); + } - // Couldn't find QNH in METAR - if (newQnh == noQnh) { - LogInfo("Unable to parse QNH from METAR for " + station + ": " + metar); - return; - } - - if (!this->qnhs.count(station) || this->qnhs.at(station) == newQnh) { - this->qnhs[station] = newQnh; - return; - } - - if ( - this->notificationsEnabled && - this->activeCallsigns.UserHasCallsign() && - this->activeCallsigns.GetUserCallsign().GetNormalisedPosition().HasTopdownAirfield(station) - ) { - - - // Send message - PressureChangeMessage message(station, this->qnhs.at(station), newQnh); - this->userMessager.SendMessageToUser(message); - } - - this->qnhs[station] = newQnh; - } - - /* - User settings have been updated, update local variables from them. - */ - void PressureMonitor::UserSettingsUpdated(UserSetting & userSettings) - { - this->notificationsEnabled = userSettings.GetBooleanEntry( - GeneralSettingsEntries::pressureMonitorSendMessageKey, - false - ); - } - } // namespace Metar -} // namespace UKControllerPlugin + auto PressureMonitor::PressureHasUpdated(const std::string& airfield, const PressureComponent& pressure) const + -> bool + { + return this->pressures.count(airfield) != 0 && + this->pressures.at(airfield)->QnhHectopascals() != pressure.QnhHectopascals(); + } +} // namespace UKControllerPlugin::Metar diff --git a/src/plugin/metar/PressureMonitor.h b/src/plugin/metar/PressureMonitor.h index 233ebc851..afc672432 100644 --- a/src/plugin/metar/PressureMonitor.h +++ b/src/plugin/metar/PressureMonitor.h @@ -1,52 +1,49 @@ #pragma once -#include "metar/MetarEventHandlerInterface.h" +#include "MetarEventHandlerInterface.h" +#include "controller/ActiveCallsignCollection.h" #include "euroscope/UserSetting.h" -#include "message/UserMessager.h" #include "euroscope/UserSettingAwareInterface.h" -#include "controller/ActiveCallsignCollection.h" - -namespace UKControllerPlugin { - namespace Metar { - - /* - Monitors changing QNHs as reported by EuroScope - and notifies the user if the QNH has changed. - */ - class PressureMonitor : public UKControllerPlugin::Metar::MetarEventHandlerInterface, - public UKControllerPlugin::Euroscope::UserSettingAwareInterface - { - public: - - PressureMonitor( - UKControllerPlugin::Message::UserMessager & userMessager, - const UKControllerPlugin::Controller::ActiveCallsignCollection& activeCallsigns - ); - std::string GetStoredQnh(std::string station) const; - bool NotificationsEnabled(void) const; - void SetNotficationsEnabled(bool enabled); - - // Inherited via MetarEventHandlerInterface - void NewMetar(std::string station, std::string metar) override; - - // Inherited via UserSettingAwareInterface - void UserSettingsUpdated(UKControllerPlugin::Euroscope::UserSetting & userSettings) override; - - // String to return if no QNHn is stored - const std::string qnhNotStored = "NONE"; - - private: - - // Whether or not to send notifications. - bool notificationsEnabled = false; - - // A map of airfield -> last recorded QNH. - std::map qnhs; - - // Interface with ES for sending messages to the user about pressure changes. - UKControllerPlugin::Message::UserMessager & userMessager; +#include "message/UserMessager.h" - // All the active controller callsigns - const UKControllerPlugin::Controller::ActiveCallsignCollection& activeCallsigns; - }; - } // namespace Metar -} // namespace UKControllerPlugin +namespace UKControllerPlugin::Metar { + class PressureComponent; + + /* + Monitors changing QNHs as reported by EuroScope + and notifies the user if the QNH has changed. + */ + class PressureMonitor : public UKControllerPlugin::Metar::MetarEventHandlerInterface, + public UKControllerPlugin::Euroscope::UserSettingAwareInterface + { + public: + PressureMonitor( + UKControllerPlugin::Message::UserMessager& userMessager, + const UKControllerPlugin::Controller::ActiveCallsignCollection& activeCallsigns); + [[nodiscard]] auto GetStoredPressure(const std::string& airfield) const -> std::shared_ptr; + bool NotificationsEnabled(void) const; + void SetNotficationsEnabled(bool enabled); + + // Inherited via MetarEventHandlerInterface + void MetarUpdated(const ParsedMetar& metar) override; + + // Inherited via UserSettingAwareInterface + void UserSettingsUpdated(UKControllerPlugin::Euroscope::UserSetting& userSettings) override; + + private: + [[nodiscard]] auto PressureHasUpdated(const std::string& airfield, const PressureComponent& pressure) const + -> bool; + void SendNotificationIfRequired(const std::string& airfield, const PressureComponent& pressure) const; + + // Whether or not to send notifications. + bool notificationsEnabled = false; + + // A map of airfield -> last recorded QNH. + std::map> pressures; + + // Interface with ES for sending messages to the user about pressure changes. + UKControllerPlugin::Message::UserMessager& userMessager; + + // All the active controller callsigns + const UKControllerPlugin::Controller::ActiveCallsignCollection& activeCallsigns; + }; +} // namespace UKControllerPlugin::Metar diff --git a/src/plugin/metar/PressureMonitorBootstrap.cpp b/src/plugin/metar/PressureMonitorBootstrap.cpp index d5fb0551d..5baa5412c 100644 --- a/src/plugin/metar/PressureMonitorBootstrap.cpp +++ b/src/plugin/metar/PressureMonitorBootstrap.cpp @@ -1,7 +1,7 @@ +#include "MetarEventHandlerCollection.h" #include "PressureMonitor.h" #include "PressureMonitorBootstrap.h" #include "euroscope/UserSettingAwareCollection.h" -#include "metar/MetarEventHandlerCollection.h" using UKControllerPlugin::Bootstrap::PersistenceContainer; using UKControllerPlugin::Metar::PressureMonitor; diff --git a/src/plugin/metar/PressureNotFoundMessage.cpp b/src/plugin/metar/PressureNotFoundMessage.cpp new file mode 100644 index 000000000..ab97793df --- /dev/null +++ b/src/plugin/metar/PressureNotFoundMessage.cpp @@ -0,0 +1,48 @@ +#include "PressureNotFoundMessage.h" + +namespace UKControllerPlugin::Metar { + + PressureNotFoundMessage::PressureNotFoundMessage(std::string airfield) : airfield(airfield) + { + } + + auto PressureNotFoundMessage::MessageHandler() const -> std::string + { + return "UKCP_PRESSURE_MONITOR"; + } + + auto PressureNotFoundMessage::MessageSender() const -> std::string + { + return "UKCP"; + } + + auto PressureNotFoundMessage::MessageString() const -> std::string + { + return "Pressure information is not available for " + airfield + "."; + } + + auto PressureNotFoundMessage::MessageShowHandler() const -> bool + { + return true; + } + + auto PressureNotFoundMessage::MessageMarkUnread() const -> bool + { + return true; + } + + auto PressureNotFoundMessage::MessageOverrideBusy() const -> bool + { + return true; + } + + auto PressureNotFoundMessage::MessageFlashHandler() const -> bool + { + return true; + } + + auto PressureNotFoundMessage::MessageRequiresConfirm() const -> bool + { + return true; + } +} // namespace UKControllerPlugin::Metar diff --git a/src/plugin/metar/PressureNotFoundMessage.h b/src/plugin/metar/PressureNotFoundMessage.h new file mode 100644 index 000000000..3870e6cb9 --- /dev/null +++ b/src/plugin/metar/PressureNotFoundMessage.h @@ -0,0 +1,28 @@ +#pragma once +#include "message/MessageSerializableInterface.h" + +namespace UKControllerPlugin::Metar { + class PressureComponent; + + /* + A message to be used when the pressure at an airfield changes. + */ + class PressureNotFoundMessage : public UKControllerPlugin::Message::MessageSerializableInterface + { + public: + PressureNotFoundMessage(std::string airfield); + + // Inherited via MessageSerializableInterface + [[nodiscard]] auto MessageHandler() const -> std::string override; + [[nodiscard]] auto MessageSender() const -> std::string override; + [[nodiscard]] auto MessageString() const -> std::string override; + [[nodiscard]] auto MessageShowHandler() const -> bool override; + [[nodiscard]] auto MessageMarkUnread() const -> bool override; + [[nodiscard]] auto MessageOverrideBusy() const -> bool override; + [[nodiscard]] auto MessageFlashHandler() const -> bool override; + [[nodiscard]] auto MessageRequiresConfirm() const -> bool override; + + private: + std::string airfield; + }; +} // namespace UKControllerPlugin::Metar diff --git a/src/plugin/metar/PressureQueryCommandHandler.cpp b/src/plugin/metar/PressureQueryCommandHandler.cpp new file mode 100644 index 000000000..b4e76d1a3 --- /dev/null +++ b/src/plugin/metar/PressureQueryCommandHandler.cpp @@ -0,0 +1,35 @@ +#include "MetarComponents.h" +#include "ParsedMetar.h" +#include "ParsedMetarCollection.h" +#include "PressureComponent.h" +#include "PressureNotFoundMessage.h" +#include "PressureQueryCommandHandler.h" +#include "PressureQueryMessage.h" +#include "message/UserMessager.h" + +namespace UKControllerPlugin::Metar { + + PressureQueryCommandHandler::PressureQueryCommandHandler( + const ParsedMetarCollection& metars, Message::UserMessager& userMessager) + : metars(metars), userMessager(userMessager), commandPattern("^\\.ukcp pressure ([A-Z]{4})$") + { + } + + auto PressureQueryCommandHandler::ProcessCommand(std::string command) -> bool + { + std::smatch matches; + if (!std::regex_search(command, matches, commandPattern)) { + return false; + } + + const std::string airfield = matches[1]; + const auto metar = this->metars.GetForAirfield(airfield); + if (metar == nullptr || metar->Components().pressure == nullptr) { + this->userMessager.SendMessageToUser(PressureNotFoundMessage(airfield)); + } else { + this->userMessager.SendMessageToUser(PressureQueryMessage(airfield, *metar->Components().pressure)); + } + + return true; + } +} // namespace UKControllerPlugin::Metar diff --git a/src/plugin/metar/PressureQueryCommandHandler.h b/src/plugin/metar/PressureQueryCommandHandler.h new file mode 100644 index 000000000..b7e451601 --- /dev/null +++ b/src/plugin/metar/PressureQueryCommandHandler.h @@ -0,0 +1,26 @@ +#pragma once +#include "command/CommandHandlerInterface.h" + +namespace UKControllerPlugin::Message { + class UserMessager; +} // namespace UKControllerPlugin::Message + +namespace UKControllerPlugin::Metar { + class ParsedMetarCollection; + + class PressureQueryCommandHandler : public Command::CommandHandlerInterface + { + public: + PressureQueryCommandHandler(const ParsedMetarCollection& metars, Message::UserMessager& userMessager); + auto ProcessCommand(std::string command) -> bool override; + + private: + // All the metars + const ParsedMetarCollection& metars; + + // Sends messages to the user + Message::UserMessager& userMessager; + + const std::regex commandPattern; + }; +} // namespace UKControllerPlugin::Metar diff --git a/src/plugin/metar/PressureQueryMessage.cpp b/src/plugin/metar/PressureQueryMessage.cpp new file mode 100644 index 000000000..a0cdb2891 --- /dev/null +++ b/src/plugin/metar/PressureQueryMessage.cpp @@ -0,0 +1,69 @@ +#include "PressureQueryMessage.h" +#include "PressureComponent.h" + +namespace UKControllerPlugin::Metar { + + PressureQueryMessage::PressureQueryMessage(std::string airfield, const PressureComponent& pressure) + : airfield(airfield), pressure(pressure) + { + } + + auto PressureQueryMessage::MessageHandler() const -> std::string + { + return "UKCP_PRESSURE_MONITOR"; + } + + auto PressureQueryMessage::MessageSender() const -> std::string + { + return "UKCP"; + } + + auto PressureQueryMessage::MessageString() const -> std::string + { + return "QNH at " + airfield + " is " + this->Qnh() + ", QFE is " + this->Qfe() + "."; + } + + auto PressureQueryMessage::MessageShowHandler() const -> bool + { + return true; + } + + auto PressureQueryMessage::MessageMarkUnread() const -> bool + { + return true; + } + + auto PressureQueryMessage::MessageOverrideBusy() const -> bool + { + return true; + } + + auto PressureQueryMessage::MessageFlashHandler() const -> bool + { + return true; + } + + auto PressureQueryMessage::MessageRequiresConfirm() const -> bool + { + return true; + } + + auto PressureQueryMessage::Qfe() const -> std::string + { + return this->pressure.ReportedAsHectopascals() ? std::to_string(this->pressure.QfeHectopascals()) + : FormatInHg(this->pressure.QfeInHg()); + } + + auto PressureQueryMessage::Qnh() const -> std::string + { + return this->pressure.ReportedAsHectopascals() ? std::to_string(this->pressure.QnhHectopascals()) + : FormatInHg(this->pressure.QnhInHg()); + } + + auto PressureQueryMessage::FormatInHg(float pressure) -> std::string + { + char formatted[INHG_BUFFER_SIZE]; // NOLINT + sprintf_s(formatted, "%.2f", pressure); // NOLINT + return formatted; + } +} // namespace UKControllerPlugin::Metar diff --git a/src/plugin/metar/PressureQueryMessage.h b/src/plugin/metar/PressureQueryMessage.h new file mode 100644 index 000000000..95f74faff --- /dev/null +++ b/src/plugin/metar/PressureQueryMessage.h @@ -0,0 +1,35 @@ +#pragma once +#include "message/MessageSerializableInterface.h" + +namespace UKControllerPlugin::Metar { + class PressureComponent; + + /* + A message to be used when the pressure at an airfield changes. + */ + class PressureQueryMessage : public UKControllerPlugin::Message::MessageSerializableInterface + { + public: + PressureQueryMessage(std::string airfield, const PressureComponent& pressure); + + // Inherited via MessageSerializableInterface + [[nodiscard]] auto MessageHandler() const -> std::string override; + [[nodiscard]] auto MessageSender() const -> std::string override; + [[nodiscard]] auto MessageString() const -> std::string override; + [[nodiscard]] auto MessageShowHandler() const -> bool override; + [[nodiscard]] auto MessageMarkUnread() const -> bool override; + [[nodiscard]] auto MessageOverrideBusy() const -> bool override; + [[nodiscard]] auto MessageFlashHandler() const -> bool override; + [[nodiscard]] auto MessageRequiresConfirm() const -> bool override; + + private: + [[nodiscard]] auto Qnh() const -> std::string; + [[nodiscard]] auto Qfe() const -> std::string; + [[nodiscard]] static auto FormatInHg(float pressure) -> std::string; + + std::string airfield; + const PressureComponent& pressure; + + static inline const int INHG_BUFFER_SIZE = 6; + }; +} // namespace UKControllerPlugin::Metar diff --git a/src/plugin/metar/PressureUnit.h b/src/plugin/metar/PressureUnit.h new file mode 100644 index 000000000..c9cd1c375 --- /dev/null +++ b/src/plugin/metar/PressureUnit.h @@ -0,0 +1,12 @@ +#pragma once + +namespace UKControllerPlugin::Metar { + /** + * The units in which pressure canbe recorded. + */ + enum class PressureUnit : unsigned int + { + Hectopascals = 0, + InHg = 1, + }; +} // namespace UKControllerPlugin::Metar diff --git a/src/plugin/plugin/UKPlugin.cpp b/src/plugin/plugin/UKPlugin.cpp index 26c09d18a..f096a4145 100644 --- a/src/plugin/plugin/UKPlugin.cpp +++ b/src/plugin/plugin/UKPlugin.cpp @@ -1,7 +1,5 @@ -#include "pch/pch.h" - -#include "plugin/UKPlugin.h" - +#include "FunctionCallEventHandler.h" +#include "UKPlugin.h" #include "command/CommandHandlerCollection.h" #include "controller/ControllerStatusEventHandlerCollection.h" #include "controller/HandoffEventHandlerCollection.h" @@ -13,8 +11,6 @@ #include "euroscope/EuroscopeSectorFileElementWrapper.h" #include "euroscope/RadarTargetEventHandlerCollection.h" #include "flightplan/FlightPlanEventHandlerCollection.h" -#include "metar/MetarEventHandlerCollection.h" -#include "plugin/FunctionCallEventHandler.h" #include "radarscreen/UKRadarScreen.h" #include "tag/TagData.h" #include "tag/TagItemCollection.h" @@ -37,7 +33,6 @@ using UKControllerPlugin::Euroscope::EuroscopeSectorFileElementWrapper; using UKControllerPlugin::Euroscope::RadarTargetEventHandlerCollection; using UKControllerPlugin::Euroscope::RunwayDialogAwareCollection; using UKControllerPlugin::Flightplan::FlightPlanEventHandlerCollection; -using UKControllerPlugin::Metar::MetarEventHandlerCollection; using UKControllerPlugin::Plugin::FunctionCallEventHandler; using UKControllerPlugin::Plugin::PluginVersion; using UKControllerPlugin::RadarScreen::RadarScreenFactory; @@ -56,7 +51,6 @@ namespace UKControllerPlugin { const TimedEventCollection& timedEvents, const TagItemCollection& tagEvents, RadarScreenFactory radarScreenFactory, - const MetarEventHandlerCollection& metarHandlers, const FunctionCallEventHandler& functionCallHandler, const CommandHandlerCollection& commandHandlers, const RunwayDialogAwareCollection& runwayDialogHandlers, @@ -69,7 +63,7 @@ namespace UKControllerPlugin { PluginVersion::copyright), radarTargetEventHandler(radarTargetEventHandler), flightplanEventHandler(flightplanEventHandler), statusEventHandler(statusEventHandler), timedEvents(timedEvents), - radarScreenFactory(std::move(radarScreenFactory)), tagEvents(tagEvents), metarHandlers(metarHandlers), + radarScreenFactory(std::move(radarScreenFactory)), tagEvents(tagEvents), functionCallHandler(functionCallHandler), commandHandlers(commandHandlers), runwayDialogHandlers(runwayDialogHandlers), controllerHandoffHandlers(controllerHandoffHandlers) @@ -438,14 +432,6 @@ namespace UKControllerPlugin { this->tagEvents.TagItemUpdate(tagData); } - /* - Called when EuroScope receives a new METAR. - */ - void UKPlugin::OnNewMetarReceived(const char* sStation, const char* sFullMetar) - { - this->metarHandlers.NewMetarEvent(sStation, sFullMetar); - } - /* Called when someone clicks OK on the runway settings dialog. */ diff --git a/src/plugin/plugin/UKPlugin.h b/src/plugin/plugin/UKPlugin.h index 3c5645276..31b43adf6 100644 --- a/src/plugin/plugin/UKPlugin.h +++ b/src/plugin/plugin/UKPlugin.h @@ -69,7 +69,6 @@ namespace UKControllerPlugin { const TimedEvent::TimedEventCollection& timedEvents, const Tag::TagItemCollection& tagEvents, RadarScreen::RadarScreenFactory radarScreenFactory, - const Metar::MetarEventHandlerCollection& metarHandlers, const Plugin::FunctionCallEventHandler& functionCallHandler, const Command::CommandHandlerCollection& commandHandlers, const Euroscope::RunwayDialogAwareCollection& runwayDialogHandlers, @@ -109,7 +108,6 @@ namespace UKControllerPlugin { int* pColorCode, COLORREF* pRGB, double* pFontSize) override; - void OnNewMetarReceived(const char* sStation, const char* sFullMetar) override; void OnAirportRunwayActivityChanged() override; void OnTimer(int time) override; auto OnRadarScreenCreated( @@ -174,9 +172,6 @@ namespace UKControllerPlugin { // Events involving tag items const Tag::TagItemCollection& tagEvents; - // Collection of handlers for METAR events - const Metar::MetarEventHandlerCollection& metarHandlers; - // Handler for function calls const Plugin::FunctionCallEventHandler& functionCallHandler; diff --git a/src/plugin/plugin/UkPluginBootstrap.cpp b/src/plugin/plugin/UkPluginBootstrap.cpp index 5ce3da2f5..f7111f183 100644 --- a/src/plugin/plugin/UkPluginBootstrap.cpp +++ b/src/plugin/plugin/UkPluginBootstrap.cpp @@ -1,35 +1,28 @@ -#include "pch/pch.h" -#include "plugin/UkPluginBootstrap.h" +#include "UKPlugin.h" +#include "UkPluginBootstrap.h" #include "bootstrap/PersistenceContainer.h" -#include "plugin/UKPlugin.h" #include "radarscreen/RadarScreenFactory.h" -using UKControllerPlugin::Bootstrap::PersistenceContainer; using UKControllerPlugin::UKPlugin; +using UKControllerPlugin::Bootstrap::PersistenceContainer; using UKControllerPlugin::RadarScreen::RadarScreenFactory; -namespace UKControllerPlugin { - namespace Bootstrap { +namespace UKControllerPlugin::Bootstrap { - /* - Add the plugin to the persistence container. - */ - void UkPluginBootstrap::BootstrapPlugin(PersistenceContainer & persistence) - { - persistence.plugin.reset( - new UKPlugin( - *persistence.radarTargetHandler, - *persistence.flightplanHandler, - *persistence.controllerHandler, - *persistence.timedHandler, - *persistence.tagHandler, - RadarScreenFactory(persistence), - *persistence.metarEventHandler, - *persistence.pluginFunctionHandlers, - *persistence.commandHandlers, - *persistence.runwayDialogEventHandlers, - *persistence.controllerHandoffHandlers - ) - ); - } - } // namespace Bootstrap -} // namespace UKControllerPlugin + /* + Add the plugin to the persistence container. + */ + void UkPluginBootstrap::BootstrapPlugin(PersistenceContainer& persistence) + { + persistence.plugin.reset(new UKPlugin( + *persistence.radarTargetHandler, + *persistence.flightplanHandler, + *persistence.controllerHandler, + *persistence.timedHandler, + *persistence.tagHandler, + RadarScreenFactory(persistence), + *persistence.pluginFunctionHandlers, + *persistence.commandHandlers, + *persistence.runwayDialogEventHandlers, + *persistence.controllerHandoffHandlers)); + } +} // namespace UKControllerPlugin::Bootstrap diff --git a/src/plugin/plugin/UkPluginBootstrap.h b/src/plugin/plugin/UkPluginBootstrap.h index 29a425d9a..8268ebbe5 100644 --- a/src/plugin/plugin/UkPluginBootstrap.h +++ b/src/plugin/plugin/UkPluginBootstrap.h @@ -1,22 +1,14 @@ #pragma once -// Forward declare -namespace UKControllerPlugin { - namespace Bootstrap { - struct PersistenceContainer; - } // namespace Bootstrap -} // namespace UKControllerPlugin -// END -namespace UKControllerPlugin { - namespace Bootstrap { +namespace UKControllerPlugin::Bootstrap { + struct PersistenceContainer; - /* - Factory to create the plugin. - */ - class UkPluginBootstrap - { - public: - static void BootstrapPlugin(UKControllerPlugin::Bootstrap::PersistenceContainer & persistence); - }; - } // namespace Bootstrap -} // namespace UKControllerPlugin + /* + Factory to create the plugin. + */ + class UkPluginBootstrap + { + public: + static void BootstrapPlugin(UKControllerPlugin::Bootstrap::PersistenceContainer& persistence); + }; +} // namespace UKControllerPlugin::Bootstrap diff --git a/src/utils/api/ApiHelper.cpp b/src/utils/api/ApiHelper.cpp index 32950795c..0622e4154 100644 --- a/src/utils/api/ApiHelper.cpp +++ b/src/utils/api/ApiHelper.cpp @@ -1,385 +1,391 @@ -#include "api/ApiException.h" -#include "api/ApiHelper.h" -#include "api/ApiNotAuthorisedException.h" -#include "api/ApiNotFoundException.h" -#include "api/ApiResponseFactory.h" -#include "curl/CurlInterface.h" -#include "squawk/SquawkValidator.h" - -using UKControllerPlugin::Api::ApiException; -using UKControllerPlugin::Api::ApiNotAuthorisedException; -using UKControllerPlugin::Api::ApiNotFoundException; -using UKControllerPlugin::Api::ApiRequestBuilder; -using UKControllerPlugin::Api::ApiResponseFactory; -using UKControllerPlugin::Curl::CurlInterface; -using UKControllerPlugin::Curl::CurlRequest; -using UKControllerPlugin::Curl::CurlResponse; -using UKControllerPlugin::Squawk::ApiSquawkAllocation; -using UKControllerPlugin::Squawk::SquawkValidator; -using UKControllerPlugin::Srd::SrdSearchParameters; - -namespace UKControllerPlugin::Api { - - ApiHelper::ApiHelper(CurlInterface& curlApi, ApiRequestBuilder requestBuilder) - : requestBuilder(std::move(requestBuilder)), curlApi(curlApi) - { - } - - /* - Makes a request to the API. - */ - auto ApiHelper::MakeApiRequest(const CurlRequest& request) const -> ApiResponse - { - CurlResponse response = this->curlApi.MakeCurlRequest(request); - - if (response.IsCurlError()) { - LogError("cURL error when making API request, route: " + std::string(request.GetUri())); - throw ApiException("ApiException when calling " + std::string(request.GetUri())); - } - - if (response.GetStatusCode() == STATUS_SERVER_ERROR || response.GetStatusCode() == STATUS_SERVICE_UNAVAILBLE) { - LogError("Internal server error when calling " + std::string(request.GetUri())); - throw ApiException("ApiException, internal server error"); - } - - if (response.GetStatusCode() == STATUS_UNAUTHORISED || response.GetStatusCode() == STATUS_FORBIDDEN) { - LogError("The API returned unauthorised when calling " + std::string(request.GetUri())); - throw ApiNotAuthorisedException("The API returned 401 or 403"); - } - - if (response.GetStatusCode() == STATUS_BAD_REQUEST) { - LogError("The API responed with bad request when calling " + std::string(request.GetUri())); - throw ApiException("The API returned 400"); - } - - if (response.GetStatusCode() == STATUS_NOT_FOUND) { - throw ApiNotFoundException("The API returned 404 for " + std::string(request.GetUri())); - } - - // These are the only codes the API should be sending on success - if (response.GetStatusCode() != STATUS_CREATED && response.GetStatusCode() != STATUS_NO_CONTENT && - response.GetStatusCode() != STATUS_OK) { - LogError("Unknown API response occured, HTTP status was " + std::to_string(response.GetStatusCode())); - throw ApiException("Unknown response"); - } - - return ApiResponseFactory::Create(response); - } - - auto ApiHelper::ProcessSquawkResponse(const ApiResponse& response, const std::string& callsign) - -> ApiSquawkAllocation - { - nlohmann::json responseJson = response.GetRawData(); - - if (responseJson.count("squawk") != 1 || !responseJson["squawk"].is_string()) { - LogError("No squawk in API response for " + callsign); - throw ApiException("Invalid response returned from API"); - } - - if (!SquawkValidator::ValidSquawk(responseJson["squawk"])) { - LogError("Invalid API response when requesting squawk assignment for " + callsign); - throw ApiException("Invalid response returned from API"); - } - - // We should return false here, because if the API has a bad squawk assigned, we should try replacing it - if (!SquawkValidator::AllowedSquawk(responseJson["squawk"])) { - LogError("API returned invalid squawk when requesting squawk assignment for " + callsign); - throw ApiException("Invalid squawk returned from API"); - } - - return ApiSquawkAllocation{callsign, responseJson["squawk"]}; - } - - /* - Creates or updates a general squawk assignment (generates a new squawk for the aircraft). - */ - auto - ApiHelper::CreateGeneralSquawkAssignment(std::string callsign, std::string origin, std::string destination) const - -> ApiSquawkAllocation - { - - return this->ProcessSquawkResponse( - this->MakeApiRequest( - this->requestBuilder.BuildGeneralSquawkAssignmentRequest(callsign, origin, destination)), - callsign); - } - - /* - Creates or updates a local squawk assignment (generates a new squawk for the aircraft). - */ - auto ApiHelper::CreateLocalSquawkAssignment(std::string callsign, std::string unit, std::string flightRules) const - -> ApiSquawkAllocation - { - return this->ProcessSquawkResponse( - this->MakeApiRequest(this->requestBuilder.BuildLocalSquawkAssignmentRequest(callsign, unit, flightRules)), - callsign); - } - - /* - Hits the API root to find out whether we're allowed in. - */ - auto ApiHelper::CheckApiAuthorisation() const -> bool - { - return this->MakeApiRequest(this->requestBuilder.BuildAuthCheckRequest()).GetStatusCode() == STATUS_OK; - } - - /* - De allocate a squawk from a given aircraft. - */ - void ApiHelper::DeleteSquawkAssignment(std::string callsign) const - { - static_cast(this->MakeApiRequest(this->requestBuilder.BuildSquawkAssignmentDeletionRequest(callsign))); - } - - /* - Fetches the file at the given URI. - */ - auto ApiHelper::FetchRemoteFile(std::string uri) const -> std::string - { - return this->MakeApiRequest(ApiRequestBuilder::BuildRemoteFileRequest(uri)).GetRawData().dump(); - } - - /* - Get any currently assigned squawk for the aircraft - */ - auto ApiHelper::GetAssignedSquawk(std::string callsign) const -> ApiSquawkAllocation - { - return this->ProcessSquawkResponse( - this->MakeApiRequest(this->requestBuilder.BuildSquawkAssignmentCheckRequest(callsign)), callsign); - } - - /* - Returns the API domain being used by the request builder - */ - auto ApiHelper::GetApiDomain() const -> std::string - { - return this->requestBuilder.GetApiDomain(); - } - - /* - Returns the API key being used to authenticate requests - */ - auto ApiHelper::GetApiKey() const -> std::string - { - return this->requestBuilder.GetApiKey(); - } - - auto ApiHelper::GetDependencyList() const -> nlohmann::json - { - return this->MakeApiRequest(this->requestBuilder.BuildDependencyListRequest()).GetRawData(); - } - - /* - Returns the hold data dependency - */ - auto ApiHelper::GetHoldDependency() const -> nlohmann::json - { - return this->MakeApiRequest(this->requestBuilder.BuildHoldDependencyRequest()).GetRawData(); - } - - auto ApiHelper::GetAssignedHolds() const -> nlohmann::json - { - return this->MakeApiRequest(this->requestBuilder.BuildAllAssignedHoldsRequest()).GetRawData(); - } - - void ApiHelper::AssignAircraftToHold(std::string callsign, std::string navaid) const - { - static_cast(this->MakeApiRequest(this->requestBuilder.BuildSetAssignedHoldRequest(callsign, navaid))); - } - - void ApiHelper::UnassignAircraftHold(std::string callsign) const - { - static_cast(this->MakeApiRequest(this->requestBuilder.BuildDeleteAssignedHoldRequest(callsign))); - } - - /* - Request all the min stack levels - */ - auto ApiHelper::GetMinStackLevels() const -> nlohmann::json - { - return this->MakeApiRequest(this->requestBuilder.BuildMinStackLevelRequest()).GetRawData(); - } - - auto ApiHelper::GetRegionalPressures() const -> nlohmann::json - { - return this->MakeApiRequest(this->requestBuilder.BuildRegionalPressureRequest()).GetRawData(); - } - - auto ApiHelper::GetUri(std::string uri) const -> nlohmann::json - { - if (uri.find(this->GetApiDomain()) == std::string::npos) { - LogCritical("Attempted to get URI on non-ukcp route"); - throw ApiException("Attempted to get URI on non-ukcp route"); - } - - return this->MakeApiRequest(this->requestBuilder.BuildGetUriRequest(uri)).GetRawData(); - } - - auto ApiHelper::SearchSrd(SrdSearchParameters params) const -> nlohmann::json - { - return this->MakeApiRequest(this->requestBuilder.BuildSrdQueryRequest(params)).GetRawData(); - } - - auto ApiHelper::GetAssignedStands() const -> nlohmann::json - { - return this->MakeApiRequest(this->requestBuilder.BuildGetStandAssignmentsRequest()).GetRawData(); - } - - void ApiHelper::AssignStandToAircraft(std::string callsign, int standId) const - { - static_cast( - this->MakeApiRequest(this->requestBuilder.BuildAssignStandToAircraftRequest(callsign, standId))); - } - - void ApiHelper::DeleteStandAssignmentForAircraft(std::string callsign) const - { - static_cast( - this->MakeApiRequest(this->requestBuilder.BuildDeleteStandAssignmentForAircraftRequest(callsign))); - } - - void ApiHelper::SendEnrouteRelease( - std::string aircraftCallsign, - std::string sendingController, - std::string targetController, - int releaseType) const - { - static_cast(this->MakeApiRequest(this->requestBuilder.BuildEnrouteReleaseRequest( - aircraftCallsign, sendingController, targetController, releaseType))); - } - - void ApiHelper::SendEnrouteReleaseWithReleasePoint( - std::string aircraftCallsign, - std::string sendingController, - std::string targetController, - int releaseType, - std::string releasePoint) const - { - static_cast(this->MakeApiRequest(this->requestBuilder.BuildEnrouteReleaseRequestWithReleasePoint( - aircraftCallsign, sendingController, targetController, releaseType, releasePoint))); - } - - auto ApiHelper::GetUpdateDetails() const -> nlohmann::json - { - return this->MakeApiRequest(this->requestBuilder.BuildLatestGithubVersionRequest()).GetRawData(); - } - - auto ApiHelper::GetAllNotifications() const -> nlohmann::json - { - return this->MakeApiRequest(this->requestBuilder.BuildGetAllNotificationsRequest()).GetRawData(); - } - - auto ApiHelper::GetUnreadNotifications() const -> nlohmann::json - { - return this->MakeApiRequest(this->requestBuilder.BuildGetUnreadNotificationsRequest()).GetRawData(); - } - - auto ApiHelper::SyncPluginEvents() const -> nlohmann::json - { - return this->MakeApiRequest(this->requestBuilder.BuildPluginEventSyncRequest()).GetRawData(); - } - - auto ApiHelper::GetLatestPluginEvents(int lastEventId) const -> nlohmann::json - { - return this->MakeApiRequest(this->requestBuilder.BuildGetLatestPluginEventsRequest(lastEventId)).GetRawData(); - } - - void ApiHelper::AcknowledgeDepartureReleaseRequest(int releaseId, int controllerPositionId) const - { - static_cast(this->MakeApiRequest( - this->requestBuilder.BuildAcknowledgeDepartureReleaseRequest(releaseId, controllerPositionId))); - } - - void ApiHelper::RejectDepartureReleaseRequest(int releaseId, int controllerPositionId) const - { - static_cast(this->MakeApiRequest( - this->requestBuilder.BuildRejectDepartureReleaseRequest(releaseId, controllerPositionId))); - } - - void ApiHelper::ApproveDepartureReleaseRequest( - int releaseId, - int controllerPositionId, - std::chrono::system_clock::time_point releasedAt, - int expiresInSeconds) const - { - static_cast(this->MakeApiRequest(this->requestBuilder.BuildApproveDepartureReleaseRequest( - releaseId, controllerPositionId, releasedAt, expiresInSeconds))); - } - - auto ApiHelper::RequestDepartureRelease( - std::string callsign, int requestingControllerId, int targetControllerId, int expiresInSeconds) const - -> nlohmann::json - { - return this - ->MakeApiRequest(this->requestBuilder.BuildDepartureReleaseRequest( - callsign, requestingControllerId, targetControllerId, expiresInSeconds)) - .GetRawData(); - } - - void ApiHelper::CancelDepartureReleaseRequest(int releaseId) const - { - static_cast(this->MakeApiRequest(this->requestBuilder.BuildCancelReleaseRequest(releaseId))); - } - - void ApiHelper::ReadNotification(int id) const - { - static_cast(this->MakeApiRequest(this->requestBuilder.BuildReadNotificationRequest(id))); - } - - /* - Set api key on the request builder - */ - void ApiHelper::SetApiKey(std::string key) - { - this->requestBuilder.SetApiKey(key); - } - - /* - Set api domain on the request builder - */ - void ApiHelper::SetApiDomain(std::string domain) - { - this->requestBuilder.SetApiDomain(domain); - } - - auto ApiHelper::CreatePrenoteMessage( - const std::string& callsign, - const std::string& departureAirfield, - const std::string& departureSid, - const std::string& destinationAirfield, - int requestingController, - int targetController, - int requestExpiry) const -> nlohmann::json - { - return this - ->MakeApiRequest(this->requestBuilder.BuildCreatePrenoteMessageRequest( - callsign, - departureAirfield, - departureSid, - destinationAirfield, - requestingController, - targetController, - requestExpiry)) - .GetRawData(); - } - - void ApiHelper::AcknowledgePrenoteMessage(int messageId, int controllerId) const - { - static_cast( - this->MakeApiRequest(this->requestBuilder.BuildAcknowledgePrenoteMessageRequest(messageId, controllerId))); - } - - void ApiHelper::DeletePrenoteMessage(int messageId) const - { - static_cast(this->MakeApiRequest(this->requestBuilder.BuildDeletePrenoteMessageRequest(messageId))); - } - - auto ApiHelper::CreateMissedApproach(const std::string& callsign) const -> nlohmann::json - { - return this->MakeApiRequest(this->requestBuilder.BuildMissedApproachMessage(callsign)).GetRawData(); - } - void ApiHelper::AcknowledgeMissedApproach(int id, const std::string& remarks) const - { - static_cast( - this->MakeApiRequest(this->requestBuilder.BuildMissedApproachAcknowledgeMessage(id, remarks))); - } -} // namespace UKControllerPlugin::Api +#include "api/ApiException.h" +#include "api/ApiHelper.h" +#include "api/ApiNotAuthorisedException.h" +#include "api/ApiNotFoundException.h" +#include "api/ApiResponseFactory.h" +#include "curl/CurlInterface.h" +#include "squawk/SquawkValidator.h" + +using UKControllerPlugin::Api::ApiException; +using UKControllerPlugin::Api::ApiNotAuthorisedException; +using UKControllerPlugin::Api::ApiNotFoundException; +using UKControllerPlugin::Api::ApiRequestBuilder; +using UKControllerPlugin::Api::ApiResponseFactory; +using UKControllerPlugin::Curl::CurlInterface; +using UKControllerPlugin::Curl::CurlRequest; +using UKControllerPlugin::Curl::CurlResponse; +using UKControllerPlugin::Squawk::ApiSquawkAllocation; +using UKControllerPlugin::Squawk::SquawkValidator; +using UKControllerPlugin::Srd::SrdSearchParameters; + +namespace UKControllerPlugin::Api { + + ApiHelper::ApiHelper(CurlInterface& curlApi, ApiRequestBuilder requestBuilder) + : requestBuilder(std::move(requestBuilder)), curlApi(curlApi) + { + } + + /* + Makes a request to the API. + */ + auto ApiHelper::MakeApiRequest(const CurlRequest& request) const -> ApiResponse + { + CurlResponse response = this->curlApi.MakeCurlRequest(request); + + if (response.IsCurlError()) { + LogError("cURL error when making API request, route: " + std::string(request.GetUri())); + throw ApiException("ApiException when calling " + std::string(request.GetUri())); + } + + if (response.GetStatusCode() == STATUS_SERVER_ERROR || response.GetStatusCode() == STATUS_SERVICE_UNAVAILBLE) { + LogError("Internal server error when calling " + std::string(request.GetUri())); + throw ApiException("ApiException, internal server error"); + } + + if (response.GetStatusCode() == STATUS_UNAUTHORISED || response.GetStatusCode() == STATUS_FORBIDDEN) { + LogError("The API returned unauthorised when calling " + std::string(request.GetUri())); + throw ApiNotAuthorisedException("The API returned 401 or 403"); + } + + if (response.GetStatusCode() == STATUS_BAD_REQUEST) { + LogError("The API responed with bad request when calling " + std::string(request.GetUri())); + throw ApiException("The API returned 400"); + } + + if (response.GetStatusCode() == STATUS_NOT_FOUND) { + throw ApiNotFoundException("The API returned 404 for " + std::string(request.GetUri())); + } + + // These are the only codes the API should be sending on success + if (response.GetStatusCode() != STATUS_CREATED && response.GetStatusCode() != STATUS_NO_CONTENT && + response.GetStatusCode() != STATUS_OK) { + LogError("Unknown API response occured, HTTP status was " + std::to_string(response.GetStatusCode())); + throw ApiException("Unknown response"); + } + + return ApiResponseFactory::Create(response); + } + + auto ApiHelper::ProcessSquawkResponse(const ApiResponse& response, const std::string& callsign) + -> ApiSquawkAllocation + { + nlohmann::json responseJson = response.GetRawData(); + + if (responseJson.count("squawk") != 1 || !responseJson["squawk"].is_string()) { + LogError("No squawk in API response for " + callsign); + throw ApiException("Invalid response returned from API"); + } + + if (!SquawkValidator::ValidSquawk(responseJson["squawk"])) { + LogError("Invalid API response when requesting squawk assignment for " + callsign); + throw ApiException("Invalid response returned from API"); + } + + // We should return false here, because if the API has a bad squawk assigned, we should try replacing it + if (!SquawkValidator::AllowedSquawk(responseJson["squawk"])) { + LogError("API returned invalid squawk when requesting squawk assignment for " + callsign); + throw ApiException("Invalid squawk returned from API"); + } + + return ApiSquawkAllocation{callsign, responseJson["squawk"]}; + } + + /* + Creates or updates a general squawk assignment (generates a new squawk for the aircraft). + */ + auto + ApiHelper::CreateGeneralSquawkAssignment(std::string callsign, std::string origin, std::string destination) const + -> ApiSquawkAllocation + { + + return this->ProcessSquawkResponse( + this->MakeApiRequest( + this->requestBuilder.BuildGeneralSquawkAssignmentRequest(callsign, origin, destination)), + callsign); + } + + /* + Creates or updates a local squawk assignment (generates a new squawk for the aircraft). + */ + auto ApiHelper::CreateLocalSquawkAssignment(std::string callsign, std::string unit, std::string flightRules) const + -> ApiSquawkAllocation + { + return this->ProcessSquawkResponse( + this->MakeApiRequest(this->requestBuilder.BuildLocalSquawkAssignmentRequest(callsign, unit, flightRules)), + callsign); + } + + /* + Hits the API root to find out whether we're allowed in. + */ + auto ApiHelper::CheckApiAuthorisation() const -> bool + { + return this->MakeApiRequest(this->requestBuilder.BuildAuthCheckRequest()).GetStatusCode() == STATUS_OK; + } + + /* + De allocate a squawk from a given aircraft. + */ + void ApiHelper::DeleteSquawkAssignment(std::string callsign) const + { + static_cast(this->MakeApiRequest(this->requestBuilder.BuildSquawkAssignmentDeletionRequest(callsign))); + } + + /* + Fetches the file at the given URI. + */ + auto ApiHelper::FetchRemoteFile(std::string uri) const -> std::string + { + return this->MakeApiRequest(ApiRequestBuilder::BuildRemoteFileRequest(uri)).GetRawData().dump(); + } + + /* + Get any currently assigned squawk for the aircraft + */ + auto ApiHelper::GetAssignedSquawk(std::string callsign) const -> ApiSquawkAllocation + { + return this->ProcessSquawkResponse( + this->MakeApiRequest(this->requestBuilder.BuildSquawkAssignmentCheckRequest(callsign)), callsign); + } + + /* + Returns the API domain being used by the request builder + */ + auto ApiHelper::GetApiDomain() const -> std::string + { + return this->requestBuilder.GetApiDomain(); + } + + /* + Returns the API key being used to authenticate requests + */ + auto ApiHelper::GetApiKey() const -> std::string + { + return this->requestBuilder.GetApiKey(); + } + + auto ApiHelper::GetDependencyList() const -> nlohmann::json + { + return this->MakeApiRequest(this->requestBuilder.BuildDependencyListRequest()).GetRawData(); + } + + /* + Returns the hold data dependency + */ + auto ApiHelper::GetHoldDependency() const -> nlohmann::json + { + return this->MakeApiRequest(this->requestBuilder.BuildHoldDependencyRequest()).GetRawData(); + } + + auto ApiHelper::GetAssignedHolds() const -> nlohmann::json + { + return this->MakeApiRequest(this->requestBuilder.BuildAllAssignedHoldsRequest()).GetRawData(); + } + + void ApiHelper::AssignAircraftToHold(std::string callsign, std::string navaid) const + { + static_cast(this->MakeApiRequest(this->requestBuilder.BuildSetAssignedHoldRequest(callsign, navaid))); + } + + void ApiHelper::UnassignAircraftHold(std::string callsign) const + { + static_cast(this->MakeApiRequest(this->requestBuilder.BuildDeleteAssignedHoldRequest(callsign))); + } + + /* + Request all the min stack levels + */ + auto ApiHelper::GetMinStackLevels() const -> nlohmann::json + { + return this->MakeApiRequest(this->requestBuilder.BuildMinStackLevelRequest()).GetRawData(); + } + + auto ApiHelper::GetRegionalPressures() const -> nlohmann::json + { + return this->MakeApiRequest(this->requestBuilder.BuildRegionalPressureRequest()).GetRawData(); + } + + auto ApiHelper::GetUri(std::string uri) const -> nlohmann::json + { + if (uri.find(this->GetApiDomain()) == std::string::npos) { + LogCritical("Attempted to get URI on non-ukcp route"); + throw ApiException("Attempted to get URI on non-ukcp route"); + } + + return this->MakeApiRequest(this->requestBuilder.BuildGetUriRequest(uri)).GetRawData(); + } + + auto ApiHelper::SearchSrd(SrdSearchParameters params) const -> nlohmann::json + { + return this->MakeApiRequest(this->requestBuilder.BuildSrdQueryRequest(params)).GetRawData(); + } + + auto ApiHelper::GetAssignedStands() const -> nlohmann::json + { + return this->MakeApiRequest(this->requestBuilder.BuildGetStandAssignmentsRequest()).GetRawData(); + } + + void ApiHelper::AssignStandToAircraft(std::string callsign, int standId) const + { + static_cast( + this->MakeApiRequest(this->requestBuilder.BuildAssignStandToAircraftRequest(callsign, standId))); + } + + void ApiHelper::DeleteStandAssignmentForAircraft(std::string callsign) const + { + static_cast( + this->MakeApiRequest(this->requestBuilder.BuildDeleteStandAssignmentForAircraftRequest(callsign))); + } + + void ApiHelper::SendEnrouteRelease( + std::string aircraftCallsign, + std::string sendingController, + std::string targetController, + int releaseType) const + { + static_cast(this->MakeApiRequest(this->requestBuilder.BuildEnrouteReleaseRequest( + aircraftCallsign, sendingController, targetController, releaseType))); + } + + void ApiHelper::SendEnrouteReleaseWithReleasePoint( + std::string aircraftCallsign, + std::string sendingController, + std::string targetController, + int releaseType, + std::string releasePoint) const + { + static_cast(this->MakeApiRequest(this->requestBuilder.BuildEnrouteReleaseRequestWithReleasePoint( + aircraftCallsign, sendingController, targetController, releaseType, releasePoint))); + } + + auto ApiHelper::GetUpdateDetails() const -> nlohmann::json + { + return this->MakeApiRequest(this->requestBuilder.BuildLatestGithubVersionRequest()).GetRawData(); + } + + auto ApiHelper::GetAllNotifications() const -> nlohmann::json + { + return this->MakeApiRequest(this->requestBuilder.BuildGetAllNotificationsRequest()).GetRawData(); + } + + auto ApiHelper::GetUnreadNotifications() const -> nlohmann::json + { + return this->MakeApiRequest(this->requestBuilder.BuildGetUnreadNotificationsRequest()).GetRawData(); + } + + auto ApiHelper::SyncPluginEvents() const -> nlohmann::json + { + return this->MakeApiRequest(this->requestBuilder.BuildPluginEventSyncRequest()).GetRawData(); + } + + auto ApiHelper::GetLatestPluginEvents(int lastEventId) const -> nlohmann::json + { + return this->MakeApiRequest(this->requestBuilder.BuildGetLatestPluginEventsRequest(lastEventId)).GetRawData(); + } + + void ApiHelper::AcknowledgeDepartureReleaseRequest(int releaseId, int controllerPositionId) const + { + static_cast(this->MakeApiRequest( + this->requestBuilder.BuildAcknowledgeDepartureReleaseRequest(releaseId, controllerPositionId))); + } + + void ApiHelper::RejectDepartureReleaseRequest(int releaseId, int controllerPositionId) const + { + static_cast(this->MakeApiRequest( + this->requestBuilder.BuildRejectDepartureReleaseRequest(releaseId, controllerPositionId))); + } + + void ApiHelper::ApproveDepartureReleaseRequest( + int releaseId, + int controllerPositionId, + std::chrono::system_clock::time_point releasedAt, + int expiresInSeconds) const + { + static_cast(this->MakeApiRequest(this->requestBuilder.BuildApproveDepartureReleaseRequest( + releaseId, controllerPositionId, releasedAt, expiresInSeconds))); + } + + auto ApiHelper::RequestDepartureRelease( + std::string callsign, int requestingControllerId, int targetControllerId, int expiresInSeconds) const + -> nlohmann::json + { + return this + ->MakeApiRequest(this->requestBuilder.BuildDepartureReleaseRequest( + callsign, requestingControllerId, targetControllerId, expiresInSeconds)) + .GetRawData(); + } + + void ApiHelper::CancelDepartureReleaseRequest(int releaseId) const + { + static_cast(this->MakeApiRequest(this->requestBuilder.BuildCancelReleaseRequest(releaseId))); + } + + void ApiHelper::ReadNotification(int id) const + { + static_cast(this->MakeApiRequest(this->requestBuilder.BuildReadNotificationRequest(id))); + } + + /* + Set api key on the request builder + */ + void ApiHelper::SetApiKey(std::string key) + { + this->requestBuilder.SetApiKey(key); + } + + /* + Set api domain on the request builder + */ + void ApiHelper::SetApiDomain(std::string domain) + { + this->requestBuilder.SetApiDomain(domain); + } + + auto ApiHelper::CreatePrenoteMessage( + const std::string& callsign, + const std::string& departureAirfield, + const std::string& departureSid, + const std::string& destinationAirfield, + int requestingController, + int targetController, + int requestExpiry) const -> nlohmann::json + { + return this + ->MakeApiRequest(this->requestBuilder.BuildCreatePrenoteMessageRequest( + callsign, + departureAirfield, + departureSid, + destinationAirfield, + requestingController, + targetController, + requestExpiry)) + .GetRawData(); + } + + void ApiHelper::AcknowledgePrenoteMessage(int messageId, int controllerId) const + { + static_cast( + this->MakeApiRequest(this->requestBuilder.BuildAcknowledgePrenoteMessageRequest(messageId, controllerId))); + } + + void ApiHelper::DeletePrenoteMessage(int messageId) const + { + static_cast(this->MakeApiRequest(this->requestBuilder.BuildDeletePrenoteMessageRequest(messageId))); + } + + auto ApiHelper::CreateMissedApproach(const std::string& callsign) const -> nlohmann::json + { + return this->MakeApiRequest(this->requestBuilder.BuildMissedApproachMessage(callsign)).GetRawData(); + } + + void ApiHelper::AcknowledgeMissedApproach(int id, const std::string& remarks) const + { + static_cast( + this->MakeApiRequest(this->requestBuilder.BuildMissedApproachAcknowledgeMessage(id, remarks))); + } + + auto ApiHelper::GetAllMetars() const -> nlohmann::json + { + return this->MakeApiRequest(this->requestBuilder.BuildGetAllMetarsRequest()).GetRawData(); + } +} // namespace UKControllerPlugin::Api diff --git a/src/utils/api/ApiHelper.h b/src/utils/api/ApiHelper.h index 513fd7411..f7168517b 100644 --- a/src/utils/api/ApiHelper.h +++ b/src/utils/api/ApiHelper.h @@ -1,112 +1,113 @@ -#pragma once -#include "api/ApiInterface.h" -#include "api/ApiRequestBuilder.h" -#include "api/ApiResponse.h" - -namespace UKControllerPlugin::Curl { - class CurlInterface; - class CurlRequest; -} // namespace UKControllerPlugin::Curl - -namespace UKControllerPlugin::Api { - - /* - A class for making requests to the UKCP API. - */ - class ApiHelper : public UKControllerPlugin::Api::ApiInterface - { - public: - ApiHelper(UKControllerPlugin::Curl::CurlInterface& curlApi, ApiRequestBuilder requestBuilder); - - [[nodiscard]] auto - CreateGeneralSquawkAssignment(std::string callsign, std::string origin, std::string destination) const - -> UKControllerPlugin::Squawk::ApiSquawkAllocation override; - [[nodiscard]] auto - CreateLocalSquawkAssignment(std::string callsign, std::string unit, std::string flightRules) const - -> UKControllerPlugin::Squawk::ApiSquawkAllocation override; - [[nodiscard]] auto CheckApiAuthorisation() const -> bool override; - void DeleteSquawkAssignment(std::string callsign) const override; - [[nodiscard]] auto FetchRemoteFile(std::string uri) const -> std::string override; - [[nodiscard]] auto GetAssignedSquawk(std::string callsign) const - -> UKControllerPlugin::Squawk::ApiSquawkAllocation override; - [[nodiscard]] auto GetApiDomain() const -> std::string override; - [[nodiscard]] auto GetApiKey() const -> std::string override; - [[nodiscard]] auto GetDependencyList() const -> nlohmann::json override; - [[nodiscard]] auto GetHoldDependency() const -> nlohmann::json override; - [[nodiscard]] auto GetAssignedHolds() const -> nlohmann::json override; - ; - void AssignAircraftToHold(std::string callsign, std::string navaid) const override; - void UnassignAircraftHold(std::string callsign) const override; - [[nodiscard]] auto GetMinStackLevels() const -> nlohmann::json override; - [[nodiscard]] auto GetRegionalPressures() const -> nlohmann::json override; - [[nodiscard]] auto GetUri(std::string uri) const -> nlohmann::json override; - [[nodiscard]] auto SearchSrd(Srd::SrdSearchParameters params) const -> nlohmann::json override; - [[nodiscard]] auto GetAssignedStands() const -> nlohmann::json override; - void AssignStandToAircraft(std::string callsign, int standId) const override; - void DeleteStandAssignmentForAircraft(std::string callsign) const override; - void SendEnrouteRelease( - std::string aircraftCallsign, - std::string sendingController, - std::string targetController, - int releaseType) const override; - void SendEnrouteReleaseWithReleasePoint( - std::string aircraftCallsign, - std::string sendingController, - std::string targetController, - int releaseType, - std::string releasePoint) const override; - [[nodiscard]] auto GetUpdateDetails() const -> nlohmann::json override; - [[nodiscard]] auto GetAllNotifications() const -> nlohmann::json override; - [[nodiscard]] auto GetUnreadNotifications() const -> nlohmann::json override; - [[nodiscard]] auto SyncPluginEvents() const -> nlohmann::json override; - [[nodiscard]] auto GetLatestPluginEvents(int lastEventId) const -> nlohmann::json override; - void AcknowledgeDepartureReleaseRequest(int releaseId, int controllerPositionId) const override; - void RejectDepartureReleaseRequest(int releaseId, int controllerPositionId) const override; - void ApproveDepartureReleaseRequest( - int releaseId, - int controllerPositionId, - std::chrono::system_clock::time_point releasedAt, - int expiresInSeconds) const override; - [[nodiscard]] auto RequestDepartureRelease( - std::string callsign, int requestingControllerId, int targetControllerId, int expiresInSeconds) const - -> nlohmann::json override; - void CancelDepartureReleaseRequest(int releaseId) const override; - void ReadNotification(int id) const override; - void SetApiKey(std::string key) override; - void SetApiDomain(std::string domain) override; - [[nodiscard]] auto CreatePrenoteMessage( - const std::string& callsign, - const std::string& departureAirfield, - const std::string& departureSid, - const std::string& destinationAirfield, - int requestingController, - int targetController, - int requestExpiry) const -> nlohmann::json override; - void AcknowledgePrenoteMessage(int messageId, int controllerId) const override; - void DeletePrenoteMessage(int messageId) const override; - [[nodiscard]] auto CreateMissedApproach(const std::string& callsign) const -> nlohmann::json override; - void AcknowledgeMissedApproach(int id, const std::string& remarks) const override; - - // The HTTP status codes that may be returned by the API - static const uint64_t STATUS_OK = 200L; - static const uint64_t STATUS_CREATED = 201L; - static const uint64_t STATUS_NO_CONTENT = 204L; - static const uint64_t STATUS_BAD_REQUEST = 400L; - static const uint64_t STATUS_UNAUTHORISED = 401L; - static const uint64_t STATUS_FORBIDDEN = 403L; - static const uint64_t STATUS_NOT_FOUND = 404L; - static const uint64_t STATUS_SERVER_ERROR = 500L; - static const uint64_t STATUS_SERVICE_UNAVAILBLE = 503L; - - private: - [[nodiscard]] auto MakeApiRequest(const UKControllerPlugin::Curl::CurlRequest& request) const -> ApiResponse; - [[nodiscard]] static auto ProcessSquawkResponse(const ApiResponse& response, const std::string& callsign) - -> UKControllerPlugin::Squawk::ApiSquawkAllocation; - - // The API request builder, that builds our CurlRequests - UKControllerPlugin::Api::ApiRequestBuilder requestBuilder; - - // An interface to the Curl library. - UKControllerPlugin::Curl::CurlInterface& curlApi; - }; -} // namespace UKControllerPlugin::Api +#pragma once +#include "api/ApiInterface.h" +#include "api/ApiRequestBuilder.h" +#include "api/ApiResponse.h" + +namespace UKControllerPlugin::Curl { + class CurlInterface; + class CurlRequest; +} // namespace UKControllerPlugin::Curl + +namespace UKControllerPlugin::Api { + + /* + A class for making requests to the UKCP API. + */ + class ApiHelper : public UKControllerPlugin::Api::ApiInterface + { + public: + ApiHelper(UKControllerPlugin::Curl::CurlInterface& curlApi, ApiRequestBuilder requestBuilder); + + [[nodiscard]] auto + CreateGeneralSquawkAssignment(std::string callsign, std::string origin, std::string destination) const + -> UKControllerPlugin::Squawk::ApiSquawkAllocation override; + [[nodiscard]] auto + CreateLocalSquawkAssignment(std::string callsign, std::string unit, std::string flightRules) const + -> UKControllerPlugin::Squawk::ApiSquawkAllocation override; + [[nodiscard]] auto CheckApiAuthorisation() const -> bool override; + void DeleteSquawkAssignment(std::string callsign) const override; + [[nodiscard]] auto FetchRemoteFile(std::string uri) const -> std::string override; + [[nodiscard]] auto GetAssignedSquawk(std::string callsign) const + -> UKControllerPlugin::Squawk::ApiSquawkAllocation override; + [[nodiscard]] auto GetApiDomain() const -> std::string override; + [[nodiscard]] auto GetApiKey() const -> std::string override; + [[nodiscard]] auto GetDependencyList() const -> nlohmann::json override; + [[nodiscard]] auto GetHoldDependency() const -> nlohmann::json override; + [[nodiscard]] auto GetAssignedHolds() const -> nlohmann::json override; + ; + void AssignAircraftToHold(std::string callsign, std::string navaid) const override; + void UnassignAircraftHold(std::string callsign) const override; + [[nodiscard]] auto GetMinStackLevels() const -> nlohmann::json override; + [[nodiscard]] auto GetRegionalPressures() const -> nlohmann::json override; + [[nodiscard]] auto GetUri(std::string uri) const -> nlohmann::json override; + [[nodiscard]] auto SearchSrd(Srd::SrdSearchParameters params) const -> nlohmann::json override; + [[nodiscard]] auto GetAssignedStands() const -> nlohmann::json override; + void AssignStandToAircraft(std::string callsign, int standId) const override; + void DeleteStandAssignmentForAircraft(std::string callsign) const override; + void SendEnrouteRelease( + std::string aircraftCallsign, + std::string sendingController, + std::string targetController, + int releaseType) const override; + void SendEnrouteReleaseWithReleasePoint( + std::string aircraftCallsign, + std::string sendingController, + std::string targetController, + int releaseType, + std::string releasePoint) const override; + [[nodiscard]] auto GetUpdateDetails() const -> nlohmann::json override; + [[nodiscard]] auto GetAllNotifications() const -> nlohmann::json override; + [[nodiscard]] auto GetUnreadNotifications() const -> nlohmann::json override; + [[nodiscard]] auto SyncPluginEvents() const -> nlohmann::json override; + [[nodiscard]] auto GetLatestPluginEvents(int lastEventId) const -> nlohmann::json override; + void AcknowledgeDepartureReleaseRequest(int releaseId, int controllerPositionId) const override; + void RejectDepartureReleaseRequest(int releaseId, int controllerPositionId) const override; + void ApproveDepartureReleaseRequest( + int releaseId, + int controllerPositionId, + std::chrono::system_clock::time_point releasedAt, + int expiresInSeconds) const override; + [[nodiscard]] auto RequestDepartureRelease( + std::string callsign, int requestingControllerId, int targetControllerId, int expiresInSeconds) const + -> nlohmann::json override; + void CancelDepartureReleaseRequest(int releaseId) const override; + void ReadNotification(int id) const override; + void SetApiKey(std::string key) override; + void SetApiDomain(std::string domain) override; + [[nodiscard]] auto CreatePrenoteMessage( + const std::string& callsign, + const std::string& departureAirfield, + const std::string& departureSid, + const std::string& destinationAirfield, + int requestingController, + int targetController, + int requestExpiry) const -> nlohmann::json override; + void AcknowledgePrenoteMessage(int messageId, int controllerId) const override; + void DeletePrenoteMessage(int messageId) const override; + [[nodiscard]] auto CreateMissedApproach(const std::string& callsign) const -> nlohmann::json override; + void AcknowledgeMissedApproach(int id, const std::string& remarks) const override; + [[nodiscard]] auto GetAllMetars() const -> nlohmann::json override; + + // The HTTP status codes that may be returned by the API + static const uint64_t STATUS_OK = 200L; + static const uint64_t STATUS_CREATED = 201L; + static const uint64_t STATUS_NO_CONTENT = 204L; + static const uint64_t STATUS_BAD_REQUEST = 400L; + static const uint64_t STATUS_UNAUTHORISED = 401L; + static const uint64_t STATUS_FORBIDDEN = 403L; + static const uint64_t STATUS_NOT_FOUND = 404L; + static const uint64_t STATUS_SERVER_ERROR = 500L; + static const uint64_t STATUS_SERVICE_UNAVAILBLE = 503L; + + private: + [[nodiscard]] auto MakeApiRequest(const UKControllerPlugin::Curl::CurlRequest& request) const -> ApiResponse; + [[nodiscard]] static auto ProcessSquawkResponse(const ApiResponse& response, const std::string& callsign) + -> UKControllerPlugin::Squawk::ApiSquawkAllocation; + + // The API request builder, that builds our CurlRequests + UKControllerPlugin::Api::ApiRequestBuilder requestBuilder; + + // An interface to the Curl library. + UKControllerPlugin::Curl::CurlInterface& curlApi; + }; +} // namespace UKControllerPlugin::Api diff --git a/src/utils/api/ApiInterface.h b/src/utils/api/ApiInterface.h index bc7d94421..17de83f6c 100644 --- a/src/utils/api/ApiInterface.h +++ b/src/utils/api/ApiInterface.h @@ -1,90 +1,91 @@ -#pragma once -#include "squawk/ApiSquawkAllocation.h" -#include "srd/SrdSearchParameters.h" - -namespace UKControllerPlugin::Api { - - /** - * An abstract class for the web API. - */ - class ApiInterface - { - public: - ApiInterface() = default; - ApiInterface(const ApiInterface&) = default; - ApiInterface(ApiInterface&&) = default; - auto operator=(const ApiInterface&) -> ApiInterface& = default; - auto operator=(ApiInterface&&) -> ApiInterface& = default; - virtual ~ApiInterface() = default; - [[nodiscard]] virtual auto - CreateGeneralSquawkAssignment(std::string callsign, std::string origin, std::string destination) const - -> UKControllerPlugin::Squawk::ApiSquawkAllocation = 0; - [[nodiscard]] virtual auto - CreateLocalSquawkAssignment(std::string callsign, std::string unit, std::string flightRules) const - -> UKControllerPlugin::Squawk::ApiSquawkAllocation = 0; - [[nodiscard]] virtual auto CheckApiAuthorisation() const -> bool = 0; - virtual void DeleteSquawkAssignment(std::string callsign) const = 0; - [[nodiscard]] virtual auto GetDependencyList() const -> nlohmann::json = 0; - [[nodiscard]] virtual auto FetchRemoteFile(std::string uri) const -> std::string = 0; - [[nodiscard]] virtual auto GetAssignedSquawk(std::string callsign) const - -> UKControllerPlugin::Squawk::ApiSquawkAllocation = 0; - [[nodiscard]] virtual auto GetApiDomain() const -> std::string = 0; - [[nodiscard]] virtual auto GetApiKey() const -> std::string = 0; - [[nodiscard]] virtual auto GetHoldDependency() const -> nlohmann::json = 0; - [[nodiscard]] virtual auto GetAssignedHolds() const -> nlohmann::json = 0; - virtual void AssignAircraftToHold(std::string callsign, std::string navaid) const = 0; - virtual void UnassignAircraftHold(std::string callsign) const = 0; - [[nodiscard]] virtual auto GetMinStackLevels() const -> nlohmann::json = 0; - [[nodiscard]] virtual auto GetRegionalPressures() const -> nlohmann::json = 0; - [[nodiscard]] virtual auto GetUri(std::string uri) const -> nlohmann::json = 0; - [[nodiscard]] virtual auto SearchSrd(UKControllerPlugin::Srd::SrdSearchParameters params) const - -> nlohmann::json = 0; - [[nodiscard]] virtual auto GetAssignedStands() const -> nlohmann::json = 0; - virtual void AssignStandToAircraft(std::string callsign, int standId) const = 0; - virtual void DeleteStandAssignmentForAircraft(std::string callsign) const = 0; - virtual void SendEnrouteRelease( - std::string aircraftCallsign, - std::string sendingController, - std::string targetController, - int releaseType) const = 0; - virtual void SendEnrouteReleaseWithReleasePoint( - std::string aircraftCallsign, - std::string sendingController, - std::string targetController, - int releaseType, - std::string releasePoint) const = 0; - [[nodiscard]] virtual auto GetAllNotifications() const -> nlohmann::json = 0; - [[nodiscard]] virtual auto GetUnreadNotifications() const -> nlohmann::json = 0; - [[nodiscard]] virtual auto SyncPluginEvents() const -> nlohmann::json = 0; - [[nodiscard]] virtual auto GetLatestPluginEvents(int lastEventId) const -> nlohmann::json = 0; - virtual void AcknowledgeDepartureReleaseRequest(int releaseId, int controllerPositionId) const = 0; - virtual void RejectDepartureReleaseRequest(int releaseId, int controllerPositionId) const = 0; - virtual void ApproveDepartureReleaseRequest( - int releaseId, - int controllerPositionId, - std::chrono::system_clock::time_point releasedAt, - int expiresInSeconds) const = 0; - [[nodiscard]] virtual auto RequestDepartureRelease( - std::string callsign, int requestingControllerId, int targetControllerId, int expiresInSeconds) const - -> nlohmann::json = 0; - virtual void CancelDepartureReleaseRequest(int releaseId) const = 0; - virtual void ReadNotification(int id) const = 0; - [[nodiscard]] virtual auto GetUpdateDetails() const -> nlohmann::json = 0; - - [[nodiscard]] virtual auto CreatePrenoteMessage( - const std::string& callsign, - const std::string& departureAirfield, - const std::string& departureSid, - const std::string& destinationAirfield, - int requestingController, - int targetController, - int requestExpiry) const -> nlohmann::json = 0; - virtual void AcknowledgePrenoteMessage(int messageId, int controllerId) const = 0; - virtual void DeletePrenoteMessage(int messageId) const = 0; - [[nodiscard]] virtual auto CreateMissedApproach(const std::string& callsign) const -> nlohmann::json = 0; - virtual void AcknowledgeMissedApproach(int id, const std::string& remarks) const = 0; - - virtual void SetApiKey(std::string key) = 0; - virtual void SetApiDomain(std::string domain) = 0; - }; -} // namespace UKControllerPlugin::Api +#pragma once +#include "squawk/ApiSquawkAllocation.h" +#include "srd/SrdSearchParameters.h" + +namespace UKControllerPlugin::Api { + + /** + * An abstract class for the web API. + */ + class ApiInterface + { + public: + ApiInterface() = default; + ApiInterface(const ApiInterface&) = default; + ApiInterface(ApiInterface&&) = default; + auto operator=(const ApiInterface&) -> ApiInterface& = default; + auto operator=(ApiInterface&&) -> ApiInterface& = default; + virtual ~ApiInterface() = default; + [[nodiscard]] virtual auto + CreateGeneralSquawkAssignment(std::string callsign, std::string origin, std::string destination) const + -> UKControllerPlugin::Squawk::ApiSquawkAllocation = 0; + [[nodiscard]] virtual auto + CreateLocalSquawkAssignment(std::string callsign, std::string unit, std::string flightRules) const + -> UKControllerPlugin::Squawk::ApiSquawkAllocation = 0; + [[nodiscard]] virtual auto CheckApiAuthorisation() const -> bool = 0; + virtual void DeleteSquawkAssignment(std::string callsign) const = 0; + [[nodiscard]] virtual auto GetDependencyList() const -> nlohmann::json = 0; + [[nodiscard]] virtual auto FetchRemoteFile(std::string uri) const -> std::string = 0; + [[nodiscard]] virtual auto GetAssignedSquawk(std::string callsign) const + -> UKControllerPlugin::Squawk::ApiSquawkAllocation = 0; + [[nodiscard]] virtual auto GetApiDomain() const -> std::string = 0; + [[nodiscard]] virtual auto GetApiKey() const -> std::string = 0; + [[nodiscard]] virtual auto GetHoldDependency() const -> nlohmann::json = 0; + [[nodiscard]] virtual auto GetAssignedHolds() const -> nlohmann::json = 0; + virtual void AssignAircraftToHold(std::string callsign, std::string navaid) const = 0; + virtual void UnassignAircraftHold(std::string callsign) const = 0; + [[nodiscard]] virtual auto GetMinStackLevels() const -> nlohmann::json = 0; + [[nodiscard]] virtual auto GetRegionalPressures() const -> nlohmann::json = 0; + [[nodiscard]] virtual auto GetUri(std::string uri) const -> nlohmann::json = 0; + [[nodiscard]] virtual auto SearchSrd(UKControllerPlugin::Srd::SrdSearchParameters params) const + -> nlohmann::json = 0; + [[nodiscard]] virtual auto GetAssignedStands() const -> nlohmann::json = 0; + virtual void AssignStandToAircraft(std::string callsign, int standId) const = 0; + virtual void DeleteStandAssignmentForAircraft(std::string callsign) const = 0; + virtual void SendEnrouteRelease( + std::string aircraftCallsign, + std::string sendingController, + std::string targetController, + int releaseType) const = 0; + virtual void SendEnrouteReleaseWithReleasePoint( + std::string aircraftCallsign, + std::string sendingController, + std::string targetController, + int releaseType, + std::string releasePoint) const = 0; + [[nodiscard]] virtual auto GetAllNotifications() const -> nlohmann::json = 0; + [[nodiscard]] virtual auto GetUnreadNotifications() const -> nlohmann::json = 0; + [[nodiscard]] virtual auto SyncPluginEvents() const -> nlohmann::json = 0; + [[nodiscard]] virtual auto GetLatestPluginEvents(int lastEventId) const -> nlohmann::json = 0; + virtual void AcknowledgeDepartureReleaseRequest(int releaseId, int controllerPositionId) const = 0; + virtual void RejectDepartureReleaseRequest(int releaseId, int controllerPositionId) const = 0; + virtual void ApproveDepartureReleaseRequest( + int releaseId, + int controllerPositionId, + std::chrono::system_clock::time_point releasedAt, + int expiresInSeconds) const = 0; + [[nodiscard]] virtual auto RequestDepartureRelease( + std::string callsign, int requestingControllerId, int targetControllerId, int expiresInSeconds) const + -> nlohmann::json = 0; + virtual void CancelDepartureReleaseRequest(int releaseId) const = 0; + virtual void ReadNotification(int id) const = 0; + [[nodiscard]] virtual auto GetUpdateDetails() const -> nlohmann::json = 0; + + [[nodiscard]] virtual auto CreatePrenoteMessage( + const std::string& callsign, + const std::string& departureAirfield, + const std::string& departureSid, + const std::string& destinationAirfield, + int requestingController, + int targetController, + int requestExpiry) const -> nlohmann::json = 0; + virtual void AcknowledgePrenoteMessage(int messageId, int controllerId) const = 0; + virtual void DeletePrenoteMessage(int messageId) const = 0; + [[nodiscard]] virtual auto CreateMissedApproach(const std::string& callsign) const -> nlohmann::json = 0; + virtual void AcknowledgeMissedApproach(int id, const std::string& remarks) const = 0; + [[nodiscard]] virtual auto GetAllMetars() const -> nlohmann::json = 0; + + virtual void SetApiKey(std::string key) = 0; + virtual void SetApiDomain(std::string domain) = 0; + }; +} // namespace UKControllerPlugin::Api diff --git a/src/utils/api/ApiRequestBuilder.cpp b/src/utils/api/ApiRequestBuilder.cpp index 6f9d57d39..e1b2fe233 100644 --- a/src/utils/api/ApiRequestBuilder.cpp +++ b/src/utils/api/ApiRequestBuilder.cpp @@ -1,462 +1,467 @@ -#include "api/ApiRequestBuilder.h" - -using UKControllerPlugin::Curl::CurlRequest; -using UKControllerPlugin::Srd::SrdSearchParameters; - -namespace UKControllerPlugin::Api { - ApiRequestBuilder::ApiRequestBuilder(std::string apiDomain, std::string apiKey) - : apiDomain(std::move(apiDomain)), apiKey(std::move(apiKey)) - { - } - - /* - Adds common headers such as the auth headers. - */ - auto ApiRequestBuilder::AddCommonHeaders(CurlRequest request) const -> CurlRequest - { - request.AddHeader("Authorization", "Bearer " + this->apiKey); - request.AddHeader("Accept", "application/json"); - request.AddHeader("Content-Type", "application/json"); - return request; - } - - /* - Builds a request to hit the root of the API just to check if we can authenticate and reach the API. - */ - auto ApiRequestBuilder::BuildAuthCheckRequest() const -> CurlRequest - { - return this->AddCommonHeaders(CurlRequest(apiDomain + "/authorise", CurlRequest::METHOD_GET)); - } - - /* - Builds a request to get the dependency manifest file - */ - auto ApiRequestBuilder::BuildDependencyListRequest() const -> CurlRequest - { - return this->AddCommonHeaders(CurlRequest(apiDomain + "/dependency", CurlRequest::METHOD_GET)); - } - - /* - Method for querying any API URI. On this method only we disable the - CURLOPT_TIMEOUT as this method is used to download dependencies and may - be quite big. - */ - auto ApiRequestBuilder::BuildGetUriRequest(std::string uri) const -> CurlRequest - { - CurlRequest request(std::move(uri), CurlRequest::METHOD_GET); - request.SetMaxRequestTime(0L); - return this->AddCommonHeaders(request); - } - - /* - Builds a request to return a static file stored on the API. This request - does not need authentication headers. - */ - auto ApiRequestBuilder::BuildRemoteFileRequest(std::string uri) -> CurlRequest - { - return {std::move(uri), CurlRequest::METHOD_GET}; - } - - /* - Builds a request for getting minimum stack levels. - */ - auto ApiRequestBuilder::BuildMinStackLevelRequest() const -> CurlRequest - { - return this->AddCommonHeaders(CurlRequest(apiDomain + "/msl", CurlRequest::METHOD_GET)); - } - - /* - Builds a request for all the regional pressures - */ - auto ApiRequestBuilder::BuildRegionalPressureRequest() const -> CurlRequest - { - return this->AddCommonHeaders(CurlRequest(apiDomain + "/regional-pressure", CurlRequest::METHOD_GET)); - } - - /* - Builds a request for querying the SRD - */ - auto ApiRequestBuilder::BuildSrdQueryRequest(const SrdSearchParameters& parameters) const -> CurlRequest - { - std::string uri = apiDomain + "/srd/route/search?"; - uri += "origin=" + parameters.origin; - uri += "&destination=" + parameters.destination; - - if (parameters.requestedLevel != NULL) { - uri += "&requestedLevel=" + std::to_string(parameters.requestedLevel); - } - - return this->AddCommonHeaders(CurlRequest(uri, CurlRequest::METHOD_GET)); - } - - /* - Builds a request for getting all the stand assignments - */ - auto ApiRequestBuilder::BuildGetStandAssignmentsRequest() const -> CurlRequest - { - return this->AddCommonHeaders(CurlRequest(apiDomain + "/stand/assignment", CurlRequest::METHOD_GET)); - } - - /* - Builds a request for assigning a stand to an aircraft - */ - auto ApiRequestBuilder::BuildAssignStandToAircraftRequest(const std::string& callsign, int standId) const - -> CurlRequest - { - CurlRequest request(apiDomain + "/stand/assignment", CurlRequest::METHOD_PUT); - nlohmann::json body; - body["callsign"] = callsign; - body["stand_id"] = standId; - request.SetBody(body.dump()); - - return this->AddCommonHeaders(request); - } - - /* - Builds a request for deleting an aircrafts stand assignment - */ - auto ApiRequestBuilder::BuildDeleteStandAssignmentForAircraftRequest(const std::string& callsign) const - -> CurlRequest - { - return this->AddCommonHeaders( - CurlRequest(apiDomain + "/stand/assignment/" + callsign, CurlRequest::METHOD_DELETE)); - } - - /* - Builds a request to check whether or not the given aircraft has been assigned a squawk. - */ - auto ApiRequestBuilder::BuildSquawkAssignmentCheckRequest(const std::string& callsign) const -> CurlRequest - { - return this->AddCommonHeaders( - CurlRequest(apiDomain + "/squawk-assignment/" + callsign, CurlRequest::METHOD_GET)); - } - - /* - Builds a request to delete (deallocate) an assigned squawk - */ - auto ApiRequestBuilder::BuildSquawkAssignmentDeletionRequest(const std::string& callsign) const -> CurlRequest - { - return this->AddCommonHeaders( - CurlRequest(apiDomain + "/squawk-assignment/" + callsign, CurlRequest::METHOD_DELETE)); - } - - /* - Builds a request to request a squawk that is local to a particular unit. - */ - auto ApiRequestBuilder::BuildLocalSquawkAssignmentRequest( - const std::string& callsign, const std::string& unit, const std::string& flightRules) const -> CurlRequest - { - CurlRequest request(apiDomain + "/squawk-assignment/" + callsign, CurlRequest::METHOD_PUT); - - nlohmann::json body; - body["type"] = this->localSquawkAssignmentType; - body["unit"] = unit; - body["rules"] = flightRules; - - request.SetBody(body.dump()); - - return this->AddCommonHeaders(request); - } - - /* - Builds a request to request a general use squawk. - */ - auto ApiRequestBuilder::BuildGeneralSquawkAssignmentRequest( - const std::string& callsign, const std::string& origin, const std::string& destination) const -> CurlRequest - { - CurlRequest request(apiDomain + "/squawk-assignment/" + callsign, CurlRequest::METHOD_PUT); - - nlohmann::json body; - body["type"] = this->generalSquawkAssignmentType; - body["origin"] = origin; - body["destination"] = destination; - - request.SetBody(body.dump()); - - return this->AddCommonHeaders(request); - } - - /* - Builds a request to download the hold data dependency - */ - auto ApiRequestBuilder::BuildHoldDependencyRequest() const -> CurlRequest - { - return this->AddCommonHeaders(CurlRequest(apiDomain + "/hold", CurlRequest::METHOD_GET)); - } - - /* - Builds a request to get all the currently assigned holds - */ - auto ApiRequestBuilder::BuildAllAssignedHoldsRequest() const -> CurlRequest - { - return this->AddCommonHeaders(CurlRequest(apiDomain + "/hold/assigned", CurlRequest::METHOD_GET)); - } - - /* - Build request to assign an aircraft to a hold - */ - auto ApiRequestBuilder::BuildSetAssignedHoldRequest(std::string callsign, std::string navaid) const -> CurlRequest - { - CurlRequest request(this->apiDomain + "/hold/assigned", CurlRequest::METHOD_PUT); - nlohmann::json data{{"callsign", callsign}, {"navaid", navaid}}; - request.SetBody(data.dump()); - - return this->AddCommonHeaders(request); - } - - /* - Build request to unassign an aircraft from all holds - */ - auto ApiRequestBuilder::BuildDeleteAssignedHoldRequest(const std::string& callsign) const -> CurlRequest - { - return this->AddCommonHeaders( - CurlRequest(apiDomain + "/hold/assigned/" + callsign, CurlRequest::METHOD_DELETE)); - } - - auto ApiRequestBuilder::BuildEnrouteReleaseRequestWithReleasePoint( - std::string aircraftCallsign, - std::string sendingController, - std::string targetController, - int releaseType, - std::string releasePoint) const -> CurlRequest - { - CurlRequest request(this->apiDomain + "/release/enroute", CurlRequest::METHOD_POST); - nlohmann::json data{ - {"callsign", aircraftCallsign}, - {"type", releaseType}, - {"initiating_controller", sendingController}, - {"target_controller", targetController}, - {"release_point", releasePoint}, - }; - request.SetBody(data.dump()); - - return this->AddCommonHeaders(request); - } - - auto ApiRequestBuilder::BuildGetAllNotificationsRequest() const -> CurlRequest - { - return this->AddCommonHeaders(CurlRequest(this->apiDomain + "/notifications", CurlRequest::METHOD_GET)); - } - - auto ApiRequestBuilder::BuildGetUnreadNotificationsRequest() const -> CurlRequest - { - return this->AddCommonHeaders(CurlRequest(this->apiDomain + "/notifications/unread", CurlRequest::METHOD_GET)); - } - - auto ApiRequestBuilder::BuildReadNotificationRequest(int id) const -> CurlRequest - { - return this->AddCommonHeaders( - CurlRequest(this->apiDomain + "/notifications/read/" + std::to_string(id), CurlRequest::METHOD_PUT)); - } - - auto ApiRequestBuilder::BuildLatestGithubVersionRequest() const -> CurlRequest - { - return this->AddCommonHeaders(CurlRequest(this->apiDomain + "/version/latest", CurlRequest::METHOD_GET)); - } - - auto ApiRequestBuilder::BuildPluginEventSyncRequest() const -> CurlRequest - { - return this->AddCommonHeaders(CurlRequest(this->apiDomain + "/plugin-events/sync", CurlRequest::METHOD_GET)); - } - - auto ApiRequestBuilder::BuildGetLatestPluginEventsRequest(int lastEventId) const -> CurlRequest - { - return this->AddCommonHeaders(CurlRequest( - this->apiDomain + "/plugin-events/recent?previous=" + std::to_string(lastEventId), - CurlRequest::METHOD_GET)); - } - - auto ApiRequestBuilder::BuildAcknowledgeDepartureReleaseRequest(int releaseId, int controllerPositionId) const - -> CurlRequest - { - nlohmann::json body; - body["controller_position_id"] = controllerPositionId; - - CurlRequest request( - this->apiDomain + "/departure/release/request/" + std::to_string(releaseId) + "/acknowledge", - CurlRequest::METHOD_PATCH); - request.SetBody(body.dump()); - return this->AddCommonHeaders(request); - } - - auto ApiRequestBuilder::BuildRejectDepartureReleaseRequest(int releaseId, int controllerPositionId) const - -> CurlRequest - { - nlohmann::json body; - body["controller_position_id"] = controllerPositionId; - - CurlRequest request( - this->apiDomain + "/departure/release/request/" + std::to_string(releaseId) + "/reject", - CurlRequest::METHOD_PATCH); - request.SetBody(body.dump()); - return this->AddCommonHeaders(request); - } - - /* - * Build an approve departure release request, pass -1 seconds for never expires. - */ - auto ApiRequestBuilder::BuildApproveDepartureReleaseRequest( - int releaseId, - int controllerPositionId, - std::chrono::system_clock::time_point releasedAt, - int expiresInSeconds) const -> CurlRequest - { - nlohmann::json body; - body["controller_position_id"] = controllerPositionId; - body["released_at"] = date::format("%Y-%m-%d %H:%M:%S", date::floor(releasedAt)); - if (expiresInSeconds == -1) { - body["expires_in_seconds"] = nlohmann::json::value_t::null; - } else { - body["expires_in_seconds"] = expiresInSeconds; - } - - CurlRequest request( - this->apiDomain + "/departure/release/request/" + std::to_string(releaseId) + "/approve", - CurlRequest::METHOD_PATCH); - request.SetBody(body.dump()); - return this->AddCommonHeaders(request); - } - - auto ApiRequestBuilder::BuildDepartureReleaseRequest( - const std::string& callsign, int requestingControllerId, int targetController, int expiresInSeconds) const - -> CurlRequest - { - CurlRequest request(this->apiDomain + "/departure/release/request", CurlRequest::METHOD_POST); - - nlohmann::json body; - body["callsign"] = callsign; - body["requesting_controller_id"] = requestingControllerId; - body["target_controller_id"] = targetController; - body["expires_in_seconds"] = expiresInSeconds; - request.SetBody(body.dump()); - - return this->AddCommonHeaders(request); - } - - auto ApiRequestBuilder::BuildCancelReleaseRequest(int releaseId) const -> CurlRequest - { - return this->AddCommonHeaders(CurlRequest( - this->apiDomain + "/departure/release/request/" + std::to_string(releaseId), CurlRequest::METHOD_DELETE)); - } - - auto ApiRequestBuilder::BuildEnrouteReleaseRequest( - std::string aircraftCallsign, - std::string sendingController, - std::string targetController, - int releaseType) const -> CurlRequest - { - CurlRequest request(this->apiDomain + "/release/enroute", CurlRequest::METHOD_POST); - nlohmann::json data{ - {"callsign", aircraftCallsign}, - {"type", releaseType}, - {"initiating_controller", sendingController}, - {"target_controller", targetController}, - }; - request.SetBody(data.dump()); - - return this->AddCommonHeaders(request); - } - - /* - Returns the API Domain that the builder is using - */ - auto ApiRequestBuilder::GetApiDomain() const -> std::string - { - return this->apiDomain; - } - - /* - Returns the API key that is used to authorize requests - */ - auto ApiRequestBuilder::GetApiKey() const -> std::string - { - return this->apiKey; - } - - /* - Set the API key - */ - void ApiRequestBuilder::SetApiDomain(std::string domain) - { - this->apiDomain = std::move(domain); - } - - /* - Set the API domain - */ - void ApiRequestBuilder::SetApiKey(std::string key) - { - this->apiKey = std::move(key); - } - - auto ApiRequestBuilder::BuildCreatePrenoteMessageRequest( - const std::string& callsign, - const std::string& departureAirfield, - const std::string& departureSid, - const std::string& destinationAirfield, - int requestingController, - int targetController, - int requestExpiry) const -> UKControllerPlugin::Curl::CurlRequest - { - CurlRequest request(this->apiDomain + "/prenotes/messages", CurlRequest::METHOD_POST); - nlohmann::json data{ - {"callsign", callsign}, - {"departure_airfield", departureAirfield}, - {"departure_sid", - departureSid.empty() ? nlohmann::json(nlohmann::json::value_t::null) : nlohmann::json(departureSid)}, - {"destination_airfield", - destinationAirfield.empty() ? nlohmann::json(nlohmann::json::value_t::null) - : nlohmann::json(destinationAirfield)}, - {"requesting_controller_id", requestingController}, - {"target_controller_id", targetController}, - {"expires_in_seconds", requestExpiry}, - }; - request.SetBody(data.dump()); - - return this->AddCommonHeaders(request); - } - - auto ApiRequestBuilder::BuildAcknowledgePrenoteMessageRequest(int messageId, int controllerId) const - -> UKControllerPlugin::Curl::CurlRequest - { - CurlRequest request( - this->apiDomain + "/prenotes/messages/" + std::to_string(messageId) + "/acknowledge", - CurlRequest::METHOD_PATCH); - nlohmann::json data{ - {"controller_position_id", controllerId}, - }; - request.SetBody(data.dump()); - - return this->AddCommonHeaders(request); - } - auto ApiRequestBuilder::BuildDeletePrenoteMessageRequest(int messageId) const - -> UKControllerPlugin::Curl::CurlRequest - { - return this->AddCommonHeaders( - {this->apiDomain + "/prenotes/messages/" + std::to_string(messageId), CurlRequest::METHOD_DELETE}); - } - - auto ApiRequestBuilder::BuildMissedApproachMessage(const std::string& callsign) const - -> UKControllerPlugin::Curl::CurlRequest - { - CurlRequest request(this->apiDomain + "/missed-approaches", CurlRequest::METHOD_POST); - nlohmann::json data{ - {"callsign", callsign}, - }; - request.SetBody(data.dump()); - - return this->AddCommonHeaders(request); - } - - auto ApiRequestBuilder::BuildMissedApproachAcknowledgeMessage(int id, const std::string& remarks) const - -> UKControllerPlugin::Curl::CurlRequest - { - CurlRequest request(this->apiDomain + "/missed-approaches/" + std::to_string(id), CurlRequest::METHOD_PATCH); - nlohmann::json data{ - {"remarks", remarks}, - }; - request.SetBody(data.dump()); - - return this->AddCommonHeaders(request); - } -} // namespace UKControllerPlugin::Api +#include "api/ApiRequestBuilder.h" + +using UKControllerPlugin::Curl::CurlRequest; +using UKControllerPlugin::Srd::SrdSearchParameters; + +namespace UKControllerPlugin::Api { + ApiRequestBuilder::ApiRequestBuilder(std::string apiDomain, std::string apiKey) + : apiDomain(std::move(apiDomain)), apiKey(std::move(apiKey)) + { + } + + /* + Adds common headers such as the auth headers. + */ + auto ApiRequestBuilder::AddCommonHeaders(CurlRequest request) const -> CurlRequest + { + request.AddHeader("Authorization", "Bearer " + this->apiKey); + request.AddHeader("Accept", "application/json"); + request.AddHeader("Content-Type", "application/json"); + return request; + } + + /* + Builds a request to hit the root of the API just to check if we can authenticate and reach the API. + */ + auto ApiRequestBuilder::BuildAuthCheckRequest() const -> CurlRequest + { + return this->AddCommonHeaders(CurlRequest(apiDomain + "/authorise", CurlRequest::METHOD_GET)); + } + + /* + Builds a request to get the dependency manifest file + */ + auto ApiRequestBuilder::BuildDependencyListRequest() const -> CurlRequest + { + return this->AddCommonHeaders(CurlRequest(apiDomain + "/dependency", CurlRequest::METHOD_GET)); + } + + /* + Method for querying any API URI. On this method only we disable the + CURLOPT_TIMEOUT as this method is used to download dependencies and may + be quite big. + */ + auto ApiRequestBuilder::BuildGetUriRequest(std::string uri) const -> CurlRequest + { + CurlRequest request(std::move(uri), CurlRequest::METHOD_GET); + request.SetMaxRequestTime(0L); + return this->AddCommonHeaders(request); + } + + /* + Builds a request to return a static file stored on the API. This request + does not need authentication headers. + */ + auto ApiRequestBuilder::BuildRemoteFileRequest(std::string uri) -> CurlRequest + { + return {std::move(uri), CurlRequest::METHOD_GET}; + } + + /* + Builds a request for getting minimum stack levels. + */ + auto ApiRequestBuilder::BuildMinStackLevelRequest() const -> CurlRequest + { + return this->AddCommonHeaders(CurlRequest(apiDomain + "/msl", CurlRequest::METHOD_GET)); + } + + /* + Builds a request for all the regional pressures + */ + auto ApiRequestBuilder::BuildRegionalPressureRequest() const -> CurlRequest + { + return this->AddCommonHeaders(CurlRequest(apiDomain + "/regional-pressure", CurlRequest::METHOD_GET)); + } + + /* + Builds a request for querying the SRD + */ + auto ApiRequestBuilder::BuildSrdQueryRequest(const SrdSearchParameters& parameters) const -> CurlRequest + { + std::string uri = apiDomain + "/srd/route/search?"; + uri += "origin=" + parameters.origin; + uri += "&destination=" + parameters.destination; + + if (parameters.requestedLevel != NULL) { + uri += "&requestedLevel=" + std::to_string(parameters.requestedLevel); + } + + return this->AddCommonHeaders(CurlRequest(uri, CurlRequest::METHOD_GET)); + } + + /* + Builds a request for getting all the stand assignments + */ + auto ApiRequestBuilder::BuildGetStandAssignmentsRequest() const -> CurlRequest + { + return this->AddCommonHeaders(CurlRequest(apiDomain + "/stand/assignment", CurlRequest::METHOD_GET)); + } + + /* + Builds a request for assigning a stand to an aircraft + */ + auto ApiRequestBuilder::BuildAssignStandToAircraftRequest(const std::string& callsign, int standId) const + -> CurlRequest + { + CurlRequest request(apiDomain + "/stand/assignment", CurlRequest::METHOD_PUT); + nlohmann::json body; + body["callsign"] = callsign; + body["stand_id"] = standId; + request.SetBody(body.dump()); + + return this->AddCommonHeaders(request); + } + + /* + Builds a request for deleting an aircrafts stand assignment + */ + auto ApiRequestBuilder::BuildDeleteStandAssignmentForAircraftRequest(const std::string& callsign) const + -> CurlRequest + { + return this->AddCommonHeaders( + CurlRequest(apiDomain + "/stand/assignment/" + callsign, CurlRequest::METHOD_DELETE)); + } + + /* + Builds a request to check whether or not the given aircraft has been assigned a squawk. + */ + auto ApiRequestBuilder::BuildSquawkAssignmentCheckRequest(const std::string& callsign) const -> CurlRequest + { + return this->AddCommonHeaders( + CurlRequest(apiDomain + "/squawk-assignment/" + callsign, CurlRequest::METHOD_GET)); + } + + /* + Builds a request to delete (deallocate) an assigned squawk + */ + auto ApiRequestBuilder::BuildSquawkAssignmentDeletionRequest(const std::string& callsign) const -> CurlRequest + { + return this->AddCommonHeaders( + CurlRequest(apiDomain + "/squawk-assignment/" + callsign, CurlRequest::METHOD_DELETE)); + } + + /* + Builds a request to request a squawk that is local to a particular unit. + */ + auto ApiRequestBuilder::BuildLocalSquawkAssignmentRequest( + const std::string& callsign, const std::string& unit, const std::string& flightRules) const -> CurlRequest + { + CurlRequest request(apiDomain + "/squawk-assignment/" + callsign, CurlRequest::METHOD_PUT); + + nlohmann::json body; + body["type"] = this->localSquawkAssignmentType; + body["unit"] = unit; + body["rules"] = flightRules; + + request.SetBody(body.dump()); + + return this->AddCommonHeaders(request); + } + + /* + Builds a request to request a general use squawk. + */ + auto ApiRequestBuilder::BuildGeneralSquawkAssignmentRequest( + const std::string& callsign, const std::string& origin, const std::string& destination) const -> CurlRequest + { + CurlRequest request(apiDomain + "/squawk-assignment/" + callsign, CurlRequest::METHOD_PUT); + + nlohmann::json body; + body["type"] = this->generalSquawkAssignmentType; + body["origin"] = origin; + body["destination"] = destination; + + request.SetBody(body.dump()); + + return this->AddCommonHeaders(request); + } + + /* + Builds a request to download the hold data dependency + */ + auto ApiRequestBuilder::BuildHoldDependencyRequest() const -> CurlRequest + { + return this->AddCommonHeaders(CurlRequest(apiDomain + "/hold", CurlRequest::METHOD_GET)); + } + + /* + Builds a request to get all the currently assigned holds + */ + auto ApiRequestBuilder::BuildAllAssignedHoldsRequest() const -> CurlRequest + { + return this->AddCommonHeaders(CurlRequest(apiDomain + "/hold/assigned", CurlRequest::METHOD_GET)); + } + + /* + Build request to assign an aircraft to a hold + */ + auto ApiRequestBuilder::BuildSetAssignedHoldRequest(std::string callsign, std::string navaid) const -> CurlRequest + { + CurlRequest request(this->apiDomain + "/hold/assigned", CurlRequest::METHOD_PUT); + nlohmann::json data{{"callsign", callsign}, {"navaid", navaid}}; + request.SetBody(data.dump()); + + return this->AddCommonHeaders(request); + } + + /* + Build request to unassign an aircraft from all holds + */ + auto ApiRequestBuilder::BuildDeleteAssignedHoldRequest(const std::string& callsign) const -> CurlRequest + { + return this->AddCommonHeaders( + CurlRequest(apiDomain + "/hold/assigned/" + callsign, CurlRequest::METHOD_DELETE)); + } + + auto ApiRequestBuilder::BuildEnrouteReleaseRequestWithReleasePoint( + std::string aircraftCallsign, + std::string sendingController, + std::string targetController, + int releaseType, + std::string releasePoint) const -> CurlRequest + { + CurlRequest request(this->apiDomain + "/release/enroute", CurlRequest::METHOD_POST); + nlohmann::json data{ + {"callsign", aircraftCallsign}, + {"type", releaseType}, + {"initiating_controller", sendingController}, + {"target_controller", targetController}, + {"release_point", releasePoint}, + }; + request.SetBody(data.dump()); + + return this->AddCommonHeaders(request); + } + + auto ApiRequestBuilder::BuildGetAllNotificationsRequest() const -> CurlRequest + { + return this->AddCommonHeaders(CurlRequest(this->apiDomain + "/notifications", CurlRequest::METHOD_GET)); + } + + auto ApiRequestBuilder::BuildGetUnreadNotificationsRequest() const -> CurlRequest + { + return this->AddCommonHeaders(CurlRequest(this->apiDomain + "/notifications/unread", CurlRequest::METHOD_GET)); + } + + auto ApiRequestBuilder::BuildReadNotificationRequest(int id) const -> CurlRequest + { + return this->AddCommonHeaders( + CurlRequest(this->apiDomain + "/notifications/read/" + std::to_string(id), CurlRequest::METHOD_PUT)); + } + + auto ApiRequestBuilder::BuildLatestGithubVersionRequest() const -> CurlRequest + { + return this->AddCommonHeaders(CurlRequest(this->apiDomain + "/version/latest", CurlRequest::METHOD_GET)); + } + + auto ApiRequestBuilder::BuildPluginEventSyncRequest() const -> CurlRequest + { + return this->AddCommonHeaders(CurlRequest(this->apiDomain + "/plugin-events/sync", CurlRequest::METHOD_GET)); + } + + auto ApiRequestBuilder::BuildGetLatestPluginEventsRequest(int lastEventId) const -> CurlRequest + { + return this->AddCommonHeaders(CurlRequest( + this->apiDomain + "/plugin-events/recent?previous=" + std::to_string(lastEventId), + CurlRequest::METHOD_GET)); + } + + auto ApiRequestBuilder::BuildAcknowledgeDepartureReleaseRequest(int releaseId, int controllerPositionId) const + -> CurlRequest + { + nlohmann::json body; + body["controller_position_id"] = controllerPositionId; + + CurlRequest request( + this->apiDomain + "/departure/release/request/" + std::to_string(releaseId) + "/acknowledge", + CurlRequest::METHOD_PATCH); + request.SetBody(body.dump()); + return this->AddCommonHeaders(request); + } + + auto ApiRequestBuilder::BuildRejectDepartureReleaseRequest(int releaseId, int controllerPositionId) const + -> CurlRequest + { + nlohmann::json body; + body["controller_position_id"] = controllerPositionId; + + CurlRequest request( + this->apiDomain + "/departure/release/request/" + std::to_string(releaseId) + "/reject", + CurlRequest::METHOD_PATCH); + request.SetBody(body.dump()); + return this->AddCommonHeaders(request); + } + + /* + * Build an approve departure release request, pass -1 seconds for never expires. + */ + auto ApiRequestBuilder::BuildApproveDepartureReleaseRequest( + int releaseId, + int controllerPositionId, + std::chrono::system_clock::time_point releasedAt, + int expiresInSeconds) const -> CurlRequest + { + nlohmann::json body; + body["controller_position_id"] = controllerPositionId; + body["released_at"] = date::format("%Y-%m-%d %H:%M:%S", date::floor(releasedAt)); + if (expiresInSeconds == -1) { + body["expires_in_seconds"] = nlohmann::json::value_t::null; + } else { + body["expires_in_seconds"] = expiresInSeconds; + } + + CurlRequest request( + this->apiDomain + "/departure/release/request/" + std::to_string(releaseId) + "/approve", + CurlRequest::METHOD_PATCH); + request.SetBody(body.dump()); + return this->AddCommonHeaders(request); + } + + auto ApiRequestBuilder::BuildDepartureReleaseRequest( + const std::string& callsign, int requestingControllerId, int targetController, int expiresInSeconds) const + -> CurlRequest + { + CurlRequest request(this->apiDomain + "/departure/release/request", CurlRequest::METHOD_POST); + + nlohmann::json body; + body["callsign"] = callsign; + body["requesting_controller_id"] = requestingControllerId; + body["target_controller_id"] = targetController; + body["expires_in_seconds"] = expiresInSeconds; + request.SetBody(body.dump()); + + return this->AddCommonHeaders(request); + } + + auto ApiRequestBuilder::BuildCancelReleaseRequest(int releaseId) const -> CurlRequest + { + return this->AddCommonHeaders(CurlRequest( + this->apiDomain + "/departure/release/request/" + std::to_string(releaseId), CurlRequest::METHOD_DELETE)); + } + + auto ApiRequestBuilder::BuildEnrouteReleaseRequest( + std::string aircraftCallsign, + std::string sendingController, + std::string targetController, + int releaseType) const -> CurlRequest + { + CurlRequest request(this->apiDomain + "/release/enroute", CurlRequest::METHOD_POST); + nlohmann::json data{ + {"callsign", aircraftCallsign}, + {"type", releaseType}, + {"initiating_controller", sendingController}, + {"target_controller", targetController}, + }; + request.SetBody(data.dump()); + + return this->AddCommonHeaders(request); + } + + /* + Returns the API Domain that the builder is using + */ + auto ApiRequestBuilder::GetApiDomain() const -> std::string + { + return this->apiDomain; + } + + /* + Returns the API key that is used to authorize requests + */ + auto ApiRequestBuilder::GetApiKey() const -> std::string + { + return this->apiKey; + } + + /* + Set the API key + */ + void ApiRequestBuilder::SetApiDomain(std::string domain) + { + this->apiDomain = std::move(domain); + } + + /* + Set the API domain + */ + void ApiRequestBuilder::SetApiKey(std::string key) + { + this->apiKey = std::move(key); + } + + auto ApiRequestBuilder::BuildCreatePrenoteMessageRequest( + const std::string& callsign, + const std::string& departureAirfield, + const std::string& departureSid, + const std::string& destinationAirfield, + int requestingController, + int targetController, + int requestExpiry) const -> UKControllerPlugin::Curl::CurlRequest + { + CurlRequest request(this->apiDomain + "/prenotes/messages", CurlRequest::METHOD_POST); + nlohmann::json data{ + {"callsign", callsign}, + {"departure_airfield", departureAirfield}, + {"departure_sid", + departureSid.empty() ? nlohmann::json(nlohmann::json::value_t::null) : nlohmann::json(departureSid)}, + {"destination_airfield", + destinationAirfield.empty() ? nlohmann::json(nlohmann::json::value_t::null) + : nlohmann::json(destinationAirfield)}, + {"requesting_controller_id", requestingController}, + {"target_controller_id", targetController}, + {"expires_in_seconds", requestExpiry}, + }; + request.SetBody(data.dump()); + + return this->AddCommonHeaders(request); + } + + auto ApiRequestBuilder::BuildAcknowledgePrenoteMessageRequest(int messageId, int controllerId) const + -> UKControllerPlugin::Curl::CurlRequest + { + CurlRequest request( + this->apiDomain + "/prenotes/messages/" + std::to_string(messageId) + "/acknowledge", + CurlRequest::METHOD_PATCH); + nlohmann::json data{ + {"controller_position_id", controllerId}, + }; + request.SetBody(data.dump()); + + return this->AddCommonHeaders(request); + } + auto ApiRequestBuilder::BuildDeletePrenoteMessageRequest(int messageId) const + -> UKControllerPlugin::Curl::CurlRequest + { + return this->AddCommonHeaders( + {this->apiDomain + "/prenotes/messages/" + std::to_string(messageId), CurlRequest::METHOD_DELETE}); + } + + auto ApiRequestBuilder::BuildMissedApproachMessage(const std::string& callsign) const + -> UKControllerPlugin::Curl::CurlRequest + { + CurlRequest request(this->apiDomain + "/missed-approaches", CurlRequest::METHOD_POST); + nlohmann::json data{ + {"callsign", callsign}, + }; + request.SetBody(data.dump()); + + return this->AddCommonHeaders(request); + } + + auto ApiRequestBuilder::BuildMissedApproachAcknowledgeMessage(int id, const std::string& remarks) const + -> UKControllerPlugin::Curl::CurlRequest + { + CurlRequest request(this->apiDomain + "/missed-approaches/" + std::to_string(id), CurlRequest::METHOD_PATCH); + nlohmann::json data{ + {"remarks", remarks}, + }; + request.SetBody(data.dump()); + + return this->AddCommonHeaders(request); + } + + auto ApiRequestBuilder::BuildGetAllMetarsRequest() const -> UKControllerPlugin::Curl::CurlRequest + { + return this->AddCommonHeaders(CurlRequest(this->apiDomain + "/metar", CurlRequest::METHOD_GET)); + } +} // namespace UKControllerPlugin::Api diff --git a/src/utils/api/ApiRequestBuilder.h b/src/utils/api/ApiRequestBuilder.h index 1b4be3011..71c3f0a29 100644 --- a/src/utils/api/ApiRequestBuilder.h +++ b/src/utils/api/ApiRequestBuilder.h @@ -1,114 +1,115 @@ -#pragma once -#include "curl/CurlRequest.h" -#include "srd/SrdSearchParameters.h" - -namespace UKControllerPlugin::Api { - - /* - Responsible for converting a set of input data into a cURL request - that can sent to the API. - */ - class ApiRequestBuilder - { - public: - ApiRequestBuilder(std::string apiDomain, std::string apiKey); - [[nodiscard]] auto BuildAuthCheckRequest() const -> UKControllerPlugin::Curl::CurlRequest; - [[nodiscard]] auto BuildDependencyListRequest() const -> UKControllerPlugin::Curl::CurlRequest; - [[nodiscard]] auto BuildGetUriRequest(std::string uri) const -> UKControllerPlugin::Curl::CurlRequest; - [[nodiscard]] auto BuildSquawkAssignmentCheckRequest(const std::string& callsign) const - -> UKControllerPlugin::Curl::CurlRequest; - [[nodiscard]] auto BuildSquawkAssignmentDeletionRequest(const std::string& callsign) const - -> UKControllerPlugin::Curl::CurlRequest; - [[nodiscard]] auto BuildLocalSquawkAssignmentRequest( - const std::string& callsign, const std::string& unit, const std::string& flightRules) const - -> UKControllerPlugin::Curl::CurlRequest; - [[nodiscard]] auto BuildGeneralSquawkAssignmentRequest( - const std::string& callsign, const std::string& origin, const std::string& destination) const - -> UKControllerPlugin::Curl::CurlRequest; - [[nodiscard]] auto BuildHoldDependencyRequest() const -> UKControllerPlugin::Curl::CurlRequest; - [[nodiscard]] auto BuildAllAssignedHoldsRequest() const -> UKControllerPlugin::Curl::CurlRequest; - [[nodiscard]] auto BuildSetAssignedHoldRequest(std::string callsign, std::string navaid) const - -> UKControllerPlugin::Curl::CurlRequest; - [[nodiscard]] auto BuildDeleteAssignedHoldRequest(const std::string& callsign) const - -> UKControllerPlugin::Curl::CurlRequest; - [[nodiscard]] static auto BuildRemoteFileRequest(std::string uri) -> UKControllerPlugin::Curl::CurlRequest; - [[nodiscard]] auto BuildMinStackLevelRequest() const -> UKControllerPlugin::Curl::CurlRequest; - [[nodiscard]] auto BuildRegionalPressureRequest() const -> UKControllerPlugin::Curl::CurlRequest; - [[nodiscard]] auto BuildSrdQueryRequest(const UKControllerPlugin::Srd::SrdSearchParameters& parameters) const - -> UKControllerPlugin::Curl::CurlRequest; - [[nodiscard]] auto BuildGetStandAssignmentsRequest() const -> UKControllerPlugin::Curl::CurlRequest; - [[nodiscard]] auto BuildAssignStandToAircraftRequest(const std::string& callsign, int standId) const - -> UKControllerPlugin::Curl::CurlRequest; - [[nodiscard]] auto BuildDeleteStandAssignmentForAircraftRequest(const std::string& callsign) const - -> UKControllerPlugin::Curl::CurlRequest; - [[nodiscard]] auto BuildEnrouteReleaseRequest( - std::string aircraftCallsign, - std::string sendingController, - std::string targetController, - int releaseType) const -> UKControllerPlugin::Curl::CurlRequest; - - [[nodiscard]] auto BuildEnrouteReleaseRequestWithReleasePoint( - std::string aircraftCallsign, - std::string sendingController, - std::string targetController, - int releaseType, - std::string releasePoint) const -> UKControllerPlugin::Curl::CurlRequest; - [[nodiscard]] auto BuildGetAllNotificationsRequest() const -> UKControllerPlugin::Curl::CurlRequest; - [[nodiscard]] auto BuildGetUnreadNotificationsRequest() const -> UKControllerPlugin::Curl::CurlRequest; - [[nodiscard]] auto BuildReadNotificationRequest(int id) const -> UKControllerPlugin::Curl::CurlRequest; - [[nodiscard]] auto BuildLatestGithubVersionRequest() const -> UKControllerPlugin::Curl::CurlRequest; - [[nodiscard]] auto BuildPluginEventSyncRequest() const -> UKControllerPlugin::Curl::CurlRequest; - [[nodiscard]] auto BuildGetLatestPluginEventsRequest(int lastEventId) const - -> UKControllerPlugin::Curl::CurlRequest; - [[nodiscard]] auto BuildAcknowledgeDepartureReleaseRequest(int releaseId, int controllerPositionId) const - -> UKControllerPlugin::Curl::CurlRequest; - [[nodiscard]] auto BuildRejectDepartureReleaseRequest(int releaseId, int controllerPositionId) const - -> UKControllerPlugin::Curl::CurlRequest; - [[nodiscard]] auto BuildApproveDepartureReleaseRequest( - int releaseId, - int controllerPositionId, - std::chrono::system_clock::time_point releasedAt, - int expiresInSeconds) const -> UKControllerPlugin::Curl::CurlRequest; - [[nodiscard]] auto BuildDepartureReleaseRequest( - const std::string& callsign, int requestingControllerId, int targetControllerId, int expiresInSeconds) const - -> UKControllerPlugin::Curl::CurlRequest; - [[nodiscard]] auto BuildCancelReleaseRequest(int releaseId) const -> UKControllerPlugin::Curl::CurlRequest; - [[nodiscard]] auto BuildCreatePrenoteMessageRequest( - const std::string& callsign, - const std::string& departureAirfield, - const std::string& departureSid, - const std::string& destinationAirfield, - int requestingController, - int targetController, - int requestExpiry) const -> UKControllerPlugin::Curl::CurlRequest; - [[nodiscard]] auto BuildAcknowledgePrenoteMessageRequest(int messageId, int controllerId) const - -> UKControllerPlugin::Curl::CurlRequest; - [[nodiscard]] auto BuildDeletePrenoteMessageRequest(int messageId) const - -> UKControllerPlugin::Curl::CurlRequest; - [[nodiscard]] auto BuildMissedApproachMessage(const std::string& callsign) const - -> UKControllerPlugin::Curl::CurlRequest; - [[nodiscard]] auto BuildMissedApproachAcknowledgeMessage(int id, const std::string& remarks) const - -> UKControllerPlugin::Curl::CurlRequest; - - [[nodiscard]] auto GetApiDomain() const -> std::string; - [[nodiscard]] auto GetApiKey() const -> std::string; - void SetApiDomain(std::string domain); - void SetApiKey(std::string key); - - private: - [[nodiscard]] auto AddCommonHeaders(UKControllerPlugin::Curl::CurlRequest request) const - -> UKControllerPlugin::Curl::CurlRequest; - - // The type string to send in the payload if we want a general squawk - const std::string generalSquawkAssignmentType = "general"; - - // The type string to send in the payload if we want a local squawk - const std::string localSquawkAssignmentType = "local"; - - // The base URL of the API - std::string apiDomain; - - // Our API key - std::string apiKey; - }; -} // namespace UKControllerPlugin::Api +#pragma once +#include "curl/CurlRequest.h" +#include "srd/SrdSearchParameters.h" + +namespace UKControllerPlugin::Api { + + /* + Responsible for converting a set of input data into a cURL request + that can sent to the API. + */ + class ApiRequestBuilder + { + public: + ApiRequestBuilder(std::string apiDomain, std::string apiKey); + [[nodiscard]] auto BuildAuthCheckRequest() const -> UKControllerPlugin::Curl::CurlRequest; + [[nodiscard]] auto BuildDependencyListRequest() const -> UKControllerPlugin::Curl::CurlRequest; + [[nodiscard]] auto BuildGetUriRequest(std::string uri) const -> UKControllerPlugin::Curl::CurlRequest; + [[nodiscard]] auto BuildSquawkAssignmentCheckRequest(const std::string& callsign) const + -> UKControllerPlugin::Curl::CurlRequest; + [[nodiscard]] auto BuildSquawkAssignmentDeletionRequest(const std::string& callsign) const + -> UKControllerPlugin::Curl::CurlRequest; + [[nodiscard]] auto BuildLocalSquawkAssignmentRequest( + const std::string& callsign, const std::string& unit, const std::string& flightRules) const + -> UKControllerPlugin::Curl::CurlRequest; + [[nodiscard]] auto BuildGeneralSquawkAssignmentRequest( + const std::string& callsign, const std::string& origin, const std::string& destination) const + -> UKControllerPlugin::Curl::CurlRequest; + [[nodiscard]] auto BuildHoldDependencyRequest() const -> UKControllerPlugin::Curl::CurlRequest; + [[nodiscard]] auto BuildAllAssignedHoldsRequest() const -> UKControllerPlugin::Curl::CurlRequest; + [[nodiscard]] auto BuildSetAssignedHoldRequest(std::string callsign, std::string navaid) const + -> UKControllerPlugin::Curl::CurlRequest; + [[nodiscard]] auto BuildDeleteAssignedHoldRequest(const std::string& callsign) const + -> UKControllerPlugin::Curl::CurlRequest; + [[nodiscard]] static auto BuildRemoteFileRequest(std::string uri) -> UKControllerPlugin::Curl::CurlRequest; + [[nodiscard]] auto BuildMinStackLevelRequest() const -> UKControllerPlugin::Curl::CurlRequest; + [[nodiscard]] auto BuildRegionalPressureRequest() const -> UKControllerPlugin::Curl::CurlRequest; + [[nodiscard]] auto BuildSrdQueryRequest(const UKControllerPlugin::Srd::SrdSearchParameters& parameters) const + -> UKControllerPlugin::Curl::CurlRequest; + [[nodiscard]] auto BuildGetStandAssignmentsRequest() const -> UKControllerPlugin::Curl::CurlRequest; + [[nodiscard]] auto BuildAssignStandToAircraftRequest(const std::string& callsign, int standId) const + -> UKControllerPlugin::Curl::CurlRequest; + [[nodiscard]] auto BuildDeleteStandAssignmentForAircraftRequest(const std::string& callsign) const + -> UKControllerPlugin::Curl::CurlRequest; + [[nodiscard]] auto BuildEnrouteReleaseRequest( + std::string aircraftCallsign, + std::string sendingController, + std::string targetController, + int releaseType) const -> UKControllerPlugin::Curl::CurlRequest; + + [[nodiscard]] auto BuildEnrouteReleaseRequestWithReleasePoint( + std::string aircraftCallsign, + std::string sendingController, + std::string targetController, + int releaseType, + std::string releasePoint) const -> UKControllerPlugin::Curl::CurlRequest; + [[nodiscard]] auto BuildGetAllNotificationsRequest() const -> UKControllerPlugin::Curl::CurlRequest; + [[nodiscard]] auto BuildGetUnreadNotificationsRequest() const -> UKControllerPlugin::Curl::CurlRequest; + [[nodiscard]] auto BuildReadNotificationRequest(int id) const -> UKControllerPlugin::Curl::CurlRequest; + [[nodiscard]] auto BuildLatestGithubVersionRequest() const -> UKControllerPlugin::Curl::CurlRequest; + [[nodiscard]] auto BuildPluginEventSyncRequest() const -> UKControllerPlugin::Curl::CurlRequest; + [[nodiscard]] auto BuildGetLatestPluginEventsRequest(int lastEventId) const + -> UKControllerPlugin::Curl::CurlRequest; + [[nodiscard]] auto BuildAcknowledgeDepartureReleaseRequest(int releaseId, int controllerPositionId) const + -> UKControllerPlugin::Curl::CurlRequest; + [[nodiscard]] auto BuildRejectDepartureReleaseRequest(int releaseId, int controllerPositionId) const + -> UKControllerPlugin::Curl::CurlRequest; + [[nodiscard]] auto BuildApproveDepartureReleaseRequest( + int releaseId, + int controllerPositionId, + std::chrono::system_clock::time_point releasedAt, + int expiresInSeconds) const -> UKControllerPlugin::Curl::CurlRequest; + [[nodiscard]] auto BuildDepartureReleaseRequest( + const std::string& callsign, int requestingControllerId, int targetControllerId, int expiresInSeconds) const + -> UKControllerPlugin::Curl::CurlRequest; + [[nodiscard]] auto BuildCancelReleaseRequest(int releaseId) const -> UKControllerPlugin::Curl::CurlRequest; + [[nodiscard]] auto BuildCreatePrenoteMessageRequest( + const std::string& callsign, + const std::string& departureAirfield, + const std::string& departureSid, + const std::string& destinationAirfield, + int requestingController, + int targetController, + int requestExpiry) const -> UKControllerPlugin::Curl::CurlRequest; + [[nodiscard]] auto BuildAcknowledgePrenoteMessageRequest(int messageId, int controllerId) const + -> UKControllerPlugin::Curl::CurlRequest; + [[nodiscard]] auto BuildDeletePrenoteMessageRequest(int messageId) const + -> UKControllerPlugin::Curl::CurlRequest; + [[nodiscard]] auto BuildMissedApproachMessage(const std::string& callsign) const + -> UKControllerPlugin::Curl::CurlRequest; + [[nodiscard]] auto BuildMissedApproachAcknowledgeMessage(int id, const std::string& remarks) const + -> UKControllerPlugin::Curl::CurlRequest; + [[nodiscard]] auto BuildGetAllMetarsRequest() const -> UKControllerPlugin::Curl::CurlRequest; + + [[nodiscard]] auto GetApiDomain() const -> std::string; + [[nodiscard]] auto GetApiKey() const -> std::string; + void SetApiDomain(std::string domain); + void SetApiKey(std::string key); + + private: + [[nodiscard]] auto AddCommonHeaders(UKControllerPlugin::Curl::CurlRequest request) const + -> UKControllerPlugin::Curl::CurlRequest; + + // The type string to send in the payload if we want a general squawk + const std::string generalSquawkAssignmentType = "general"; + + // The type string to send in the payload if we want a local squawk + const std::string localSquawkAssignmentType = "local"; + + // The base URL of the API + std::string apiDomain; + + // Our API key + std::string apiKey; + }; +} // namespace UKControllerPlugin::Api diff --git a/test/plugin/CMakeLists.txt b/test/plugin/CMakeLists.txt index b724cbda4..26d1fda9d 100644 --- a/test/plugin/CMakeLists.txt +++ b/test/plugin/CMakeLists.txt @@ -228,11 +228,20 @@ source_group("test\\message" FILES ${test__message}) set(test__metar "metar/MetarEventHandlerCollectionTest.cpp" - "metar/MetarParsingFunctionsTest.cpp" "metar/PressureChangeMessageTest.cpp" "metar/PressureMonitorBootstrapTest.cpp" "metar/PressureMonitorTest.cpp" -) + metar/PressureComponentTest.cpp + metar/ParsedMetarTest.cpp + metar/ParsedMetarCollectionTest.cpp + metar/PressureComponentFactoryTest.cpp + metar/MetarComponentsFactoryTest.cpp + metar/ParsedMetarFactoryTest.cpp + metar/MetarsUpdatedPushEventProcessorTest.cpp + metar/MetarModuleTest.cpp + metar/PressureQueryMessageTest.cpp + metar/PressureNotFoundMessageTest.cpp + metar/PressureQueryCommandHandlerTest.cpp) source_group("test\\metar" FILES ${test__metar}) set(test__minstack @@ -294,7 +303,6 @@ set(test__mock "mock/MockHistoryTrailDialog.h" "mock/MockHistoryTrailRepository.h" "mock/MockIntegrationActionProcessor.h" - "mock/MockMinStack.h" "mock/MockOutboundIntegrationEventHandler.h" "mock/MockPushEventConnection.h" "mock/MockPushEventProcessor.h" diff --git a/test/plugin/airfield/AirfieldCollectionTest.cpp b/test/plugin/airfield/AirfieldCollectionTest.cpp index d2a22c97b..122ce7785 100644 --- a/test/plugin/airfield/AirfieldCollectionTest.cpp +++ b/test/plugin/airfield/AirfieldCollectionTest.cpp @@ -61,6 +61,20 @@ namespace UKControllerPluginTest::Airfield { EXPECT_EQ(nullptr, collection.FetchAirfieldByIcao("EGBB")); } + TEST_F(AirfieldCollectionTest, ItFetchesAirfieldById) + { + collection.AddAirfield(airfield1); + collection.AddAirfield(airfield2); + EXPECT_EQ(airfield1, collection.FetchById(1)); + } + + TEST_F(AirfieldCollectionTest, FetchAirfieldByIdReturnsNullPtrIfNotFound) + { + collection.AddAirfield(airfield1); + collection.AddAirfield(airfield2); + EXPECT_EQ(nullptr, collection.FetchById(3)); + } + TEST_F(AirfieldCollectionTest, ItIteratesTheCollection) { collection.AddAirfield(airfield1); diff --git a/test/plugin/bootstrap/EventHandlerCollectionBootstrapTest.cpp b/test/plugin/bootstrap/EventHandlerCollectionBootstrapTest.cpp index 3b5526788..639fd2606 100644 --- a/test/plugin/bootstrap/EventHandlerCollectionBootstrapTest.cpp +++ b/test/plugin/bootstrap/EventHandlerCollectionBootstrapTest.cpp @@ -7,7 +7,6 @@ #include "euroscope/RunwayDialogAwareCollection.h" #include "euroscope/UserSettingAwareCollection.h" #include "flightplan/FlightPlanEventHandlerCollection.h" -#include "metar/MetarEventHandlerCollection.h" #include "plugin/FunctionCallEventHandler.h" #include "tag/TagItemCollection.h" #include "timedevent/TimedEventCollection.h" @@ -60,11 +59,6 @@ namespace UKControllerPluginTest::Bootstrap { EXPECT_EQ(0, this->container.pluginFunctionHandlers->CountCallbacks()); } - TEST_F(EventHandlerCollectionBootstrapTest, BootstrapPluginCreatesMetarEventHandler) - { - EXPECT_EQ(0, this->container.metarEventHandler->CountHandlers()); - } - TEST_F(EventHandlerCollectionBootstrapTest, BootstrapPluginCreatesUserSettingAwareHandler) { EXPECT_EQ(0, this->container.userSettingHandlers->Count()); diff --git a/test/plugin/metar/MetarComponentsFactoryTest.cpp b/test/plugin/metar/MetarComponentsFactoryTest.cpp new file mode 100644 index 000000000..025f06198 --- /dev/null +++ b/test/plugin/metar/MetarComponentsFactoryTest.cpp @@ -0,0 +1,48 @@ +#include "metar/MetarComponents.h" +#include "metar/MetarComponentsFactory.h" +#include "metar/MetarComponentsFactoryFactory.h" +#include "metar/PressureComponent.h" + +using UKControllerPlugin::Metar::BuildComponentsFactory; +using UKControllerPlugin::Metar::MetarComponents; +using UKControllerPlugin::Metar::MetarComponentsFactory; +using UKControllerPlugin::Metar::PressureComponent; +using UKControllerPlugin::Metar::PressureUnit; + +namespace UKControllerPluginTest::Metar { + class MetarComponentsFactoryTest : public testing::Test + { + public: + MetarComponentsFactoryTest() : factory(BuildComponentsFactory()) + { + } + + MetarComponents components; + std::shared_ptr factory; + }; + + TEST_F(MetarComponentsFactoryTest, ItReturnsComponents) + { + nlohmann::json json{ + {"qnh", 1013}, {"qfe", 1007}, {"qnh_inhg", 29.92}, {"qfe_inhg", 28.21}, {"pressure_format", "hpa"}}; + auto components = factory->FromJson(json); + + EXPECT_NE(nullptr, components); + EXPECT_EQ(1013, components->pressure->QnhHectopascals()); + } + + TEST_F(MetarComponentsFactoryTest, ItReturnsComponentsEmptyData) + { + auto components = factory->FromJson(nlohmann::json::object()); + EXPECT_NE(nullptr, components); + EXPECT_EQ(nullptr, components->pressure); + } + + TEST_F(MetarComponentsFactoryTest, ItReturnsNullptrIfJsonNotArray) + { + nlohmann::json json{ + {"qnh", 1013}, {"qfe", 1007}, {"qnh_inhg", 29.92}, {"qfe_inhg", 28.21}, {"pressure_format", "hpa"}}; + + EXPECT_NE(nullptr, factory->FromJson(nlohmann::json::array())); + } +} // namespace UKControllerPluginTest::Metar diff --git a/test/plugin/metar/MetarEventHandlerCollectionTest.cpp b/test/plugin/metar/MetarEventHandlerCollectionTest.cpp index ccac6432b..67674cad1 100644 --- a/test/plugin/metar/MetarEventHandlerCollectionTest.cpp +++ b/test/plugin/metar/MetarEventHandlerCollectionTest.cpp @@ -1,70 +1,68 @@ -#include "pch/pch.h" #include "metar/MetarEventHandlerCollection.h" #include "metar/MetarEventHandlerInterface.h" +#include "metar/ParsedMetar.h" using UKControllerPlugin::Metar::MetarEventHandlerCollection; using UKControllerPlugin::Metar::MetarEventHandlerInterface; +using UKControllerPlugin::Metar::ParsedMetar; -namespace UKControllerPluginTest { - namespace EventHandler { +namespace UKControllerPluginTest::Metar { - class TestMetarEventHandler : public UKControllerPlugin::Metar::MetarEventHandlerInterface + class TestMetarEventHandler : public MetarEventHandlerInterface + { + public: + void MetarUpdated(const ParsedMetar& metar) override { - public: - void NewMetar(std::string station, std::string metar) override - { - this->station = station; - this->metar = metar; - } + this->called = true; + } - std::string station; - std::string metar; - }; + bool called = false; + }; - TEST(MetarEventHandlerCollection, NewMetarEventCallsAllHandlers) + class MetarEventHandlerCollectionTest : public testing::Test + { + public: + MetarEventHandlerCollectionTest() + : handler1(std::make_shared()), handler2(std::make_shared()) { - MetarEventHandlerCollection collection; - std::shared_ptr test1 = std::make_shared(); - std::shared_ptr test2 = std::make_shared(); - collection.RegisterHandler(test1); - collection.RegisterHandler(test2); + } + std::shared_ptr handler1; + std::shared_ptr handler2; + MetarEventHandlerCollection collection; + }; - std::string testStation = "EGKK"; - std::string testMetar = "METARRRRRR"; + TEST_F(MetarEventHandlerCollectionTest, ItStartsEmpty) + { + EXPECT_EQ(0, collection.CountHandlers()); + } - collection.NewMetarEvent(testStation, testMetar); + TEST_F(MetarEventHandlerCollectionTest, ItAddsHandlers) + { + collection.RegisterHandler(handler1); + collection.RegisterHandler(handler2); + EXPECT_EQ(2, collection.CountHandlers()); + } - EXPECT_TRUE(test1->station == testStation); - EXPECT_TRUE(test1->metar == testMetar); - EXPECT_TRUE(test2->station == testStation); - EXPECT_TRUE(test2->metar == testMetar); - } + TEST_F(MetarEventHandlerCollectionTest, ItDoesntDuplicateHandlers) + { + collection.RegisterHandler(handler1); + collection.RegisterHandler(handler1); + collection.RegisterHandler(handler1); + collection.RegisterHandler(handler2); + collection.RegisterHandler(handler2); + collection.RegisterHandler(handler2); + EXPECT_EQ(2, collection.CountHandlers()); + } - TEST(MetarEventHandlerCollection, StartsEmpty) - { - MetarEventHandlerCollection collection; - EXPECT_EQ(0, collection.CountHandlers()); - } + TEST_F(MetarEventHandlerCollectionTest, NewMetarEventCallsHandlers) + { + collection.RegisterHandler(handler1); + collection.RegisterHandler(handler2); - TEST(MetarEventHandlerCollection, CountHandlersReturnsNumberOfRegisteredHandlers) - { - MetarEventHandlerCollection collection; - std::shared_ptr test = std::make_shared(); - collection.RegisterHandler(test); - EXPECT_EQ(1, collection.CountHandlers()); - collection.RegisterHandler(test); - EXPECT_EQ(1, collection.CountHandlers()); - } + std::shared_ptr parsed; + collection.UpdatedMetarEvent(*parsed); - TEST(MetarEventHandlerCollection, RegisterHandlerDoesNotAddDuplicates) - { - MetarEventHandlerCollection collection; - collection.RegisterHandler(std::make_shared()); - EXPECT_EQ(1, collection.CountHandlers()); - collection.RegisterHandler(std::make_shared()); - EXPECT_EQ(2, collection.CountHandlers()); - collection.RegisterHandler(std::make_shared()); - EXPECT_EQ(3, collection.CountHandlers()); - } - } // namespace EventHandler -} // namespace UKControllerPluginTest + EXPECT_TRUE(handler1->called); + EXPECT_TRUE(handler2->called); + } +} // namespace UKControllerPluginTest::Metar diff --git a/test/plugin/metar/MetarModuleTest.cpp b/test/plugin/metar/MetarModuleTest.cpp new file mode 100644 index 000000000..97838f826 --- /dev/null +++ b/test/plugin/metar/MetarModuleTest.cpp @@ -0,0 +1,42 @@ +#include "bootstrap/PersistenceContainer.h" +#include "command/CommandHandlerCollection.h" +#include "metar/MetarEventHandlerCollection.h" +#include "metar/MetarModule.h" +#include "push/PushEventProcessorCollection.h" + +using UKControllerPlugin::Bootstrap::PersistenceContainer; +using UKControllerPlugin::Command::CommandHandlerCollection; +using UKControllerPlugin::Metar::BootstrapPlugin; +using UKControllerPlugin::Push::PushEventProcessorCollection; + +namespace UKControllerPluginTest::Metar { + class MetarModuleTest : public testing::Test + { + public: + MetarModuleTest() + { + container.pushEventProcessors = std::make_unique(); + container.commandHandlers = std::make_unique(); + } + + PersistenceContainer container; + }; + + TEST_F(MetarModuleTest, BootstrapPluginCreatesMetarEventHandlerCollection) + { + BootstrapPlugin(container); + EXPECT_EQ(0, container.metarEventHandler->CountHandlers()); + } + + TEST_F(MetarModuleTest, BootstrapPluginRegistersMetarPushEvents) + { + BootstrapPlugin(container); + EXPECT_EQ(1, container.pushEventProcessors->CountProcessorsForEvent("metars.updated")); + } + + TEST_F(MetarModuleTest, BootstrapPluginRegistersForCommands) + { + BootstrapPlugin(container); + EXPECT_EQ(1, container.commandHandlers->CountHandlers()); + } +} // namespace UKControllerPluginTest::Metar diff --git a/test/plugin/metar/MetarParsingFunctionsTest.cpp b/test/plugin/metar/MetarParsingFunctionsTest.cpp deleted file mode 100644 index 008d71094..000000000 --- a/test/plugin/metar/MetarParsingFunctionsTest.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include "pch/pch.h" -#include "metar/MetarParsingFunctions.h" - -using UKControllerPlugin::Metar::noQnh; -using UKControllerPlugin::Metar::GetQnhString; - -namespace UKControllerPluginTest { - namespace Metar { - - TEST(MetarParsingFunctionsTest, GetQnhStringReturnsNothingIfNoQnhInMetar) - { - EXPECT_EQ(noQnh, GetQnhString("EGKK 05010KT SCT010")); - } - - TEST(MetarParsingFunctionsTest, GetQnhStringReturnsNothingIfQnhNotSeparatedBySpacesLeft) - { - EXPECT_EQ(noQnh, GetQnhString("EGKK 05010KTQ1010 SCT010")); - } - - TEST(MetarParsingFunctionsTest, GetQnhStringReturnsNothingIfQnhNotSeparatedBySpacesRight) - { - EXPECT_EQ(noQnh, GetQnhString("EGKK 05010KT Q1010SCT010")); - } - - TEST(MetarParsingFunctionsTest, GetQnhStringReturnsFourDigitQnh) - { - EXPECT_EQ("1010", GetQnhString("EGKK 05010KT Q1010 Q1010SCT010")); - } - - TEST(MetarParsingFunctionsTest, GetQnhStringReturnsThreeDigitQnh) - { - EXPECT_EQ("0987", GetQnhString("EGKK 05010KT Q0987 Q1010SCT010")); - } - - TEST(MetarParsingFunctionsTest, GetQnhStringHandlesQnhAtEndOfMetar) - { - EXPECT_EQ("0987", GetQnhString("EGKK 05010KT Q0987")); - } - } // namespace Metar -} // namespace UKControllerPluginTest diff --git a/test/plugin/metar/MetarsUpdatedPushEventProcessorTest.cpp b/test/plugin/metar/MetarsUpdatedPushEventProcessorTest.cpp new file mode 100644 index 000000000..c56e6c8b0 --- /dev/null +++ b/test/plugin/metar/MetarsUpdatedPushEventProcessorTest.cpp @@ -0,0 +1,129 @@ +#include "airfield/AirfieldCollection.h" +#include "airfield/AirfieldModel.h" +#include "api/ApiException.h" +#include "controller/ControllerPositionHierarchy.h" +#include "metar/MetarsUpdatedPushEventProcessor.h" +#include "metar/MetarComponents.h" +#include "metar/MetarComponentsFactory.h" +#include "metar/MetarComponentsFactoryFactory.h" +#include "metar/ParsedMetar.h" +#include "metar/ParsedMetarCollection.h" +#include "metar/ParsedMetarFactory.h" +#include "metar/PressureComponent.h" +#include "push/PushEvent.h" +#include "push/PushEventSubscription.h" + +using UKControllerPlugin::Airfield::AirfieldCollection; +using UKControllerPlugin::Airfield::AirfieldModel; +using UKControllerPlugin::Api::ApiException; +using UKControllerPlugin::Metar::BuildComponentsFactory; +using UKControllerPlugin::Metar::MetarComponentsFactory; +using UKControllerPlugin::Metar::MetarsUpdatedPushEventProcessor; +using UKControllerPlugin::Metar::ParsedMetarCollection; +using UKControllerPlugin::Metar::ParsedMetarFactory; +using UKControllerPlugin::Push::PushEvent; +using UKControllerPlugin::Push::PushEventSubscription; + +namespace UKControllerPluginTest::Metar { + class MetarsUpdatedPushEventProcessorTest : public testing::Test + { + public: + MetarsUpdatedPushEventProcessorTest() + : componentsFactory(BuildComponentsFactory()), factory(*componentsFactory, airfields), + processor(collection, factory, mockApi) + { + this->airfields.AddAirfield(std::make_shared(1, "EGKK", nullptr)); + this->airfields.AddAirfield(std::make_shared(2, "EGLL", nullptr)); + } + + /* + * Make an event based on the merge of some base data and overriding data so we dont + * have to repeat ourselves + */ + static PushEvent MakePushEvent(const nlohmann::json& data) + { + return {"metars,updated.created", "test", data, data.dump()}; + }; + + testing::NiceMock mockApi; + std::shared_ptr componentsFactory; + AirfieldCollection airfields; + ParsedMetarFactory factory; + ParsedMetarCollection collection; + MetarsUpdatedPushEventProcessor processor; + }; + + TEST_F(MetarsUpdatedPushEventProcessorTest, ItHasPushEventSubscriptions) + { + std::set expected{{PushEventSubscription::SUB_TYPE_EVENT, "metars.updated"}}; + EXPECT_EQ(expected, processor.GetPushEventSubscriptions()); + } + + TEST_F(MetarsUpdatedPushEventProcessorTest, ItHandlesNonArrayData) + { + nlohmann::json data = { + {"airfield_id", 1}, + {"raw", "foo"}, + {"parsed", + {{"qnh", 1013}, {"qfe", 1007}, {"qnh_inhg", 29.92}, {"qfe_inhg", 28.21}, {"pressure_format", "hpa"}}}}; + processor.ProcessPushEvent(MakePushEvent(data)); + + EXPECT_EQ(0, collection.Count()); + } + + TEST_F(MetarsUpdatedPushEventProcessorTest, ItCreatesParsedMetars) + { + nlohmann::json data = nlohmann::json::array(); + data.push_back( + {{"airfield_id", 1}, + {"raw", "foo"}, + {"parsed", + {{"qnh", 1013}, {"qfe", 1007}, {"qnh_inhg", 29.92}, {"qfe_inhg", 28.21}, {"pressure_format", "hpa"}}}}); + data.push_back( + {{"airfield_id", 2}, + {"raw", "bar"}, + {"parsed", + {{"qnh", 1013}, {"qfe", 1007}, {"qnh_inhg", 29.92}, {"qfe_inhg", 28.21}, {"pressure_format", "hpa"}}}}); + + processor.ProcessPushEvent(MakePushEvent(data)); + + EXPECT_EQ(2, collection.Count()); + EXPECT_NE(nullptr, collection.GetForAirfield("EGKK")); + EXPECT_EQ("foo", collection.GetForAirfield("EGKK")->Raw()); + EXPECT_NE(nullptr, collection.GetForAirfield("EGLL")); + EXPECT_EQ("bar", collection.GetForAirfield("EGLL")->Raw()); + } + + TEST_F(MetarsUpdatedPushEventProcessorTest, ItLoadsMetarsOnPluginEventSync) + { + nlohmann::json data = nlohmann::json::array(); + data.push_back( + {{"airfield_id", 1}, + {"raw", "foo"}, + {"parsed", + {{"qnh", 1013}, {"qfe", 1007}, {"qnh_inhg", 29.92}, {"qfe_inhg", 28.21}, {"pressure_format", "hpa"}}}}); + data.push_back( + {{"airfield_id", 2}, + {"raw", "bar"}, + {"parsed", + {{"qnh", 1013}, {"qfe", 1007}, {"qnh_inhg", 29.92}, {"qfe_inhg", 28.21}, {"pressure_format", "hpa"}}}}); + + ON_CALL(mockApi, GetAllMetars).WillByDefault(testing::Return(data)); + + processor.PluginEventsSynced(); + + EXPECT_EQ(2, collection.Count()); + EXPECT_NE(nullptr, collection.GetForAirfield("EGKK")); + EXPECT_EQ("foo", collection.GetForAirfield("EGKK")->Raw()); + EXPECT_NE(nullptr, collection.GetForAirfield("EGLL")); + EXPECT_EQ("bar", collection.GetForAirfield("EGLL")->Raw()); + } + + TEST_F(MetarsUpdatedPushEventProcessorTest, ItHandlesApiExceptionFromEventSync) + { + ON_CALL(mockApi, GetAllMetars).WillByDefault(testing::Throw(ApiException("foo"))); + + EXPECT_NO_THROW(processor.PluginEventsSynced()); + EXPECT_EQ(0, collection.Count()); + } +} // namespace UKControllerPluginTest::Metar diff --git a/test/plugin/metar/ParsedMetarCollectionTest.cpp b/test/plugin/metar/ParsedMetarCollectionTest.cpp new file mode 100644 index 000000000..87c3e942f --- /dev/null +++ b/test/plugin/metar/ParsedMetarCollectionTest.cpp @@ -0,0 +1,68 @@ +#include "metar/MetarComponents.h" +#include "metar/ParsedMetar.h" +#include "metar/ParsedMetarCollection.h" + +using UKControllerPlugin::Metar::ParsedMetar; +using UKControllerPlugin::Metar::ParsedMetarCollection; + +namespace UKControllerPluginTest::Metar { + class ParsedMetarCollectionTest : public testing::Test + { + public: + ParsedMetarCollectionTest() + : metar1(std::make_shared("EGKK", "foo", nullptr)), + metar2(std::make_shared("EGLL", "bar", nullptr)), + metar1a(std::make_shared("EGKK", "baz", nullptr)) + { + } + + std::shared_ptr metar1; + std::shared_ptr metar2; + std::shared_ptr metar1a; + ParsedMetarCollection metars; + }; + + TEST_F(ParsedMetarCollectionTest, ItStartsEmpty) + { + EXPECT_EQ(0, metars.Count()); + } + + TEST_F(ParsedMetarCollectionTest, ItAddsMetars) + { + metars.UpdateMetar(metar1); + metars.UpdateMetar(metar2); + + EXPECT_EQ(2, metars.Count()); + EXPECT_EQ(metar1, metars.GetForAirfield("EGKK")); + EXPECT_EQ(metar2, metars.GetForAirfield("EGLL")); + } + + TEST_F(ParsedMetarCollectionTest, ItDoesntDuplicateMetars) + { + metars.UpdateMetar(metar1); + metars.UpdateMetar(metar1); + metars.UpdateMetar(metar1); + metars.UpdateMetar(metar2); + metars.UpdateMetar(metar2); + metars.UpdateMetar(metar2); + } + + TEST_F(ParsedMetarCollectionTest, ItUpdatesMetars) + { + metars.UpdateMetar(metar1); + metars.UpdateMetar(metar2); + metars.UpdateMetar(metar1a); + + EXPECT_EQ(2, metars.Count()); + EXPECT_EQ(metar1a, metars.GetForAirfield("EGKK")); + EXPECT_EQ(metar2, metars.GetForAirfield("EGLL")); + } + + TEST_F(ParsedMetarCollectionTest, ItReturnsNullptrIfMetarNotFound) + { + metars.UpdateMetar(metar1); + metars.UpdateMetar(metar2); + + EXPECT_EQ(nullptr, metars.GetForAirfield("EGBB")); + } +} // namespace UKControllerPluginTest::Metar diff --git a/test/plugin/metar/ParsedMetarFactoryTest.cpp b/test/plugin/metar/ParsedMetarFactoryTest.cpp new file mode 100644 index 000000000..67e018e1a --- /dev/null +++ b/test/plugin/metar/ParsedMetarFactoryTest.cpp @@ -0,0 +1,119 @@ +#include "airfield/AirfieldCollection.h" +#include "airfield/AirfieldModel.h" +#include "controller/ControllerPositionHierarchy.h" +#include "metar/MetarComponents.h" +#include "metar/MetarComponentsFactory.h" +#include "metar/MetarComponentsFactoryFactory.h" +#include "metar/ParsedMetar.h" +#include "metar/ParsedMetarFactory.h" +#include "metar/PressureComponent.h" + +using UKControllerPlugin::Airfield::AirfieldCollection; +using UKControllerPlugin::Airfield::AirfieldModel; +using UKControllerPlugin::Metar::BuildComponentsFactory; +using UKControllerPlugin::Metar::MetarComponentsFactory; +using UKControllerPlugin::Metar::ParsedMetarFactory; + +namespace UKControllerPluginTest::Metar { + class ParsedMetarFactoryTest : public testing::Test + { + public: + ParsedMetarFactoryTest() : componentsFactory(BuildComponentsFactory()), factory(*componentsFactory, airfields) + { + this->airfields.AddAirfield(std::make_shared(1, "EGKK", nullptr)); + } + + std::shared_ptr componentsFactory; + AirfieldCollection airfields; + ParsedMetarFactory factory; + }; + + TEST_F(ParsedMetarFactoryTest, ItReturnsParsedMetar) + { + nlohmann::json data = { + {"airfield_id", 1}, + {"raw", "foo"}, + {"parsed", + {{"qnh", 1013}, {"qfe", 1007}, {"qnh_inhg", 29.92}, {"qfe_inhg", 28.21}, {"pressure_format", "hpa"}}}}; + + auto metar = factory.FromJson(data); + EXPECT_EQ("EGKK", metar->Airfield()); + EXPECT_EQ("foo", metar->Raw()); + EXPECT_EQ(1013, metar->Components().pressure->QnhHectopascals()); + } + + TEST_F(ParsedMetarFactoryTest, ItReturnsNullPtrAirfieldMissing) + { + nlohmann::json data = { + {"raw", "foo"}, + {"parsed", + {{"qnh", 1013}, {"qfe", 1007}, {"qnh_inhg", 29.92}, {"qfe_inhg", 28.21}, {"pressure_format", "hpa"}}}}; + + EXPECT_EQ(nullptr, factory.FromJson(data)); + } + + TEST_F(ParsedMetarFactoryTest, ItReturnsNullPtrAirfieldNotInteger) + { + nlohmann::json data = { + {"airfield_id", "abc"}, + {"raw", "foo"}, + {"parsed", + {{"qnh", 1013}, {"qfe", 1007}, {"qnh_inhg", 29.92}, {"qfe_inhg", 28.21}, {"pressure_format", "hpa"}}}}; + + EXPECT_EQ(nullptr, factory.FromJson(data)); + } + + TEST_F(ParsedMetarFactoryTest, ItReturnsNullPtrAirfieldNotValid) + { + nlohmann::json data = { + {"airfield_id", 2}, + {"raw", "foo"}, + {"parsed", + {{"qnh", 1013}, {"qfe", 1007}, {"qnh_inhg", 29.92}, {"qfe_inhg", 28.21}, {"pressure_format", "hpa"}}}}; + + EXPECT_EQ(nullptr, factory.FromJson(data)); + } + + TEST_F(ParsedMetarFactoryTest, ItReturnsNullPtrRawMissing) + { + nlohmann::json data = { + {"airfield_id", 1}, + {"parsed", + {{"qnh", 1013}, {"qfe", 1007}, {"qnh_inhg", 29.92}, {"qfe_inhg", 28.21}, {"pressure_format", "hpa"}}}}; + + EXPECT_EQ(nullptr, factory.FromJson(data)); + } + + TEST_F(ParsedMetarFactoryTest, ItReturnsNullPtrRawNotString) + { + nlohmann::json data = { + {"airfield_id", 1}, + {"raw", 123}, + {"parsed", + {{"qnh", 1013}, {"qfe", 1007}, {"qnh_inhg", 29.92}, {"qfe_inhg", 28.21}, {"pressure_format", "hpa"}}}}; + + EXPECT_EQ(nullptr, factory.FromJson(data)); + } + + TEST_F(ParsedMetarFactoryTest, ItReturnsNullPtrParsedMissing) + { + nlohmann::json data = { + {"airfield_id", 1}, + {"raw", "foo"}, + }; + + EXPECT_EQ(nullptr, factory.FromJson(data)); + } + + TEST_F(ParsedMetarFactoryTest, ItReturnsNullPtrParsedNotObject) + { + nlohmann::json data = {{"airfield_id", 1}, {"raw", "foo"}, {"parsed", nlohmann::json::array()}}; + + EXPECT_EQ(nullptr, factory.FromJson(data)); + } + + TEST_F(ParsedMetarFactoryTest, ItReturnsNullPtrMessageNotObject) + { + EXPECT_EQ(nullptr, factory.FromJson(nlohmann::json::array())); + } +} // namespace UKControllerPluginTest::Metar diff --git a/test/plugin/metar/ParsedMetarTest.cpp b/test/plugin/metar/ParsedMetarTest.cpp new file mode 100644 index 000000000..7484d3fcd --- /dev/null +++ b/test/plugin/metar/ParsedMetarTest.cpp @@ -0,0 +1,36 @@ +#include "metar/MetarComponents.h" +#include "metar/ParsedMetar.h" + +using UKControllerPlugin::Metar::MetarComponents; +using UKControllerPlugin::Metar::ParsedMetar; + +namespace UKControllerPluginTest::Metar { + class ParsedMetarTest : public testing::Test + { + public: + ParsedMetarTest() + : components(std::make_unique()), componentsRaw(components.get()), + metar("EGKK", "foo", std::move(components)) + { + } + + std::unique_ptr components; + MetarComponents* componentsRaw; + ParsedMetar metar; + }; + + TEST_F(ParsedMetarTest, ItHasAnAirfield) + { + EXPECT_EQ("EGKK", metar.Airfield()); + } + + TEST_F(ParsedMetarTest, ItHasRawForm) + { + EXPECT_EQ("foo", metar.Raw()); + } + + TEST_F(ParsedMetarTest, ItHasComponents) + { + EXPECT_EQ(componentsRaw, &metar.Components()); + } +} // namespace UKControllerPluginTest::Metar diff --git a/test/plugin/metar/PressureChangeMessageTest.cpp b/test/plugin/metar/PressureChangeMessageTest.cpp index 99672d868..b07afe982 100644 --- a/test/plugin/metar/PressureChangeMessageTest.cpp +++ b/test/plugin/metar/PressureChangeMessageTest.cpp @@ -1,62 +1,80 @@ -#include "pch/pch.h" #include "metar/PressureChangeMessage.h" +#include "metar/PressureComponent.h" -using UKControllerPlugin::Metar::PressureChangeMessage; using testing::Test; +using UKControllerPlugin::Metar::PressureChangeMessage; +using UKControllerPlugin::Metar::PressureComponent; +using UKControllerPlugin::Metar::PressureUnit; -namespace UKControllerPluginTest { - namespace Metar { +namespace UKControllerPluginTest::Metar { - class PressureChangeMessageTest : public Test + class PressureChangeMessageTest : public Test + { + public: + PressureChangeMessageTest() + : hectopascalsComponentBefore(1013, 1011, 29.92, 29.84, PressureUnit::Hectopascals), + hectopascalsComponentAfter(1014, 1012, 29.94, 29.86, PressureUnit::Hectopascals), + inHgComponentBefore(1013, 1011, 29.92, 29.84, PressureUnit::InHg), + inHgComponentAfter(1014, 1012, 29.94, 29.86, PressureUnit::InHg), + messageHectopascals("EGKK", hectopascalsComponentBefore, hectopascalsComponentAfter), + messageInhg("EGLL", inHgComponentBefore, inHgComponentAfter) { - public: - PressureChangeMessageTest() - : message("EGKK", "1012", "1013") - { + } - } + PressureComponent hectopascalsComponentBefore; + PressureComponent hectopascalsComponentAfter; + PressureComponent inHgComponentBefore; + PressureComponent inHgComponentAfter; + PressureChangeMessage messageHectopascals; + PressureChangeMessage messageInhg; + }; - PressureChangeMessage message; - }; + TEST_F(PressureChangeMessageTest, ItHasAMessageHandler) + { + EXPECT_EQ("UKCP_PRESSURE_MONITOR", messageHectopascals.MessageHandler()); + } - TEST_F(PressureChangeMessageTest, ItHasAMessageHandler) - { - EXPECT_EQ("UKCP_QNH", message.MessageHandler()); - } + TEST_F(PressureChangeMessageTest, ItHasASender) + { + EXPECT_EQ("UKCP", messageHectopascals.MessageSender()); + } - TEST_F(PressureChangeMessageTest, ItHasASender) - { - EXPECT_EQ("UKCP", message.MessageSender()); - } + TEST_F(PressureChangeMessageTest, ItShowsTheHandler) + { + EXPECT_TRUE(messageHectopascals.MessageShowHandler()); + } - TEST_F(PressureChangeMessageTest, ItShowsTheHandler) - { - EXPECT_TRUE(message.MessageShowHandler()); - } + TEST_F(PressureChangeMessageTest, ItMarksItAsUnread) + { + EXPECT_TRUE(messageHectopascals.MessageMarkUnread()); + } - TEST_F(PressureChangeMessageTest, ItMarksItAsUnread) - { - EXPECT_TRUE(message.MessageMarkUnread()); - } + TEST_F(PressureChangeMessageTest, ItOverridesBusy) + { + EXPECT_TRUE(messageHectopascals.MessageOverrideBusy()); + } - TEST_F(PressureChangeMessageTest, ItOverridesBusy) - { - EXPECT_TRUE(message.MessageOverrideBusy()); - } + TEST_F(PressureChangeMessageTest, ItFlashesTheHandler) + { + EXPECT_TRUE(messageHectopascals.MessageFlashHandler()); + } - TEST_F(PressureChangeMessageTest, ItFlashesTheHandler) - { - EXPECT_TRUE(message.MessageFlashHandler()); - } + TEST_F(PressureChangeMessageTest, ItRequiresConfirmation) + { + EXPECT_TRUE(messageHectopascals.MessageRequiresConfirm()); + } - TEST_F(PressureChangeMessageTest, ItRequiresConfirmation) - { - EXPECT_TRUE(message.MessageRequiresConfirm()); - } + TEST_F(PressureChangeMessageTest, ItHasAMessageHectopascals) + { + EXPECT_EQ( + "Pressure change at EGKK. QNH was 1013, now 1014. QFE was 1011, now 1012.", + messageHectopascals.MessageString()); + } - TEST_F(PressureChangeMessageTest, ItHasAMessage) - { - EXPECT_EQ("New QNH at EGKK, Was: 1012, Now: 1013", message.MessageString()); - } - } // namespace Metar -} // namespace UKControllerPluginTest + TEST_F(PressureChangeMessageTest, ItHasAMessageInHg) + { + EXPECT_EQ( + "Pressure change at EGLL. QNH was 29.92, now 29.94. QFE was 29.84, now 29.86.", + messageInhg.MessageString()); + } +} // namespace UKControllerPluginTest::Metar diff --git a/test/plugin/metar/PressureComponentFactoryTest.cpp b/test/plugin/metar/PressureComponentFactoryTest.cpp new file mode 100644 index 000000000..584e55bb3 --- /dev/null +++ b/test/plugin/metar/PressureComponentFactoryTest.cpp @@ -0,0 +1,136 @@ +#include "metar/MetarComponents.h" +#include "metar/PressureComponent.h" +#include "metar/PressureComponentFactory.h" + +using UKControllerPlugin::Metar::MetarComponents; +using UKControllerPlugin::Metar::PressureComponent; +using UKControllerPlugin::Metar::PressureComponentFactory; +using UKControllerPlugin::Metar::PressureUnit; + +namespace UKControllerPluginTest::Metar { + class PressureComponentFactoryTest : public testing::Test + { + public: + [[nodiscard]] static auto + GetJson(const nlohmann::json& overridingData = nlohmann::json::object(), const std::string& keyToRemove = "") + -> nlohmann::json + { + nlohmann::json json{ + {"qnh", 1013}, {"qfe", 1007}, {"qnh_inhg", 29.92}, {"qfe_inhg", 28.21}, {"pressure_format", "hpa"}}; + if (overridingData.is_object()) { + json.update(overridingData); + } else { + json = overridingData; + } + + if (!keyToRemove.empty()) { + json.erase(json.find(keyToRemove)); + } + + return json; + } + + MetarComponents components; + PressureComponentFactory factory; + }; + + TEST_F(PressureComponentFactoryTest, ItCreatesAHectopascalComponent) + { + factory.FromJson(GetJson(), components); + EXPECT_NE(nullptr, components.pressure); + EXPECT_EQ(1013, components.pressure->QnhHectopascals()); + EXPECT_EQ(1007, components.pressure->QfeHectopascals()); + EXPECT_FLOAT_EQ(29.92, components.pressure->QnhInHg()); + EXPECT_FLOAT_EQ(28.21, components.pressure->QfeInHg()); + EXPECT_TRUE(components.pressure->ReportedAsHectopascals()); + } + + TEST_F(PressureComponentFactoryTest, ItCreatesAnInhgComponent) + { + factory.FromJson(GetJson(nlohmann::json{{"pressure_format", "inhg"}}), components); + EXPECT_NE(nullptr, components.pressure); + EXPECT_EQ(1013, components.pressure->QnhHectopascals()); + EXPECT_EQ(1007, components.pressure->QfeHectopascals()); + EXPECT_FLOAT_EQ(29.92, components.pressure->QnhInHg()); + EXPECT_FLOAT_EQ(28.21, components.pressure->QfeInHg()); + EXPECT_TRUE(components.pressure->ReportedAsInHg()); + } + + TEST_F(PressureComponentFactoryTest, ItCreatesAnInhgComponentWithNonFloatValues) + { + factory.FromJson( + GetJson(nlohmann::json{{"pressure_format", "inhg"}, {"qnh_inhg", 29}, {"qfe_inhg", 28}}), components); + EXPECT_NE(nullptr, components.pressure); + EXPECT_EQ(1013, components.pressure->QnhHectopascals()); + EXPECT_EQ(1007, components.pressure->QfeHectopascals()); + EXPECT_FLOAT_EQ(29, components.pressure->QnhInHg()); + EXPECT_FLOAT_EQ(28, components.pressure->QfeInHg()); + EXPECT_TRUE(components.pressure->ReportedAsInHg()); + } + + TEST_F(PressureComponentFactoryTest, ItReturnsNullptrInvalidPressureFormat) + { + factory.FromJson(GetJson(nlohmann::json{{"pressure_format", "bar"}}), components); + EXPECT_EQ(nullptr, components.pressure); + } + + TEST_F(PressureComponentFactoryTest, ItReturnsNullptrPressureFormatNotString) + { + factory.FromJson(GetJson(nlohmann::json{{"pressure_format", 123}}), components); + EXPECT_EQ(nullptr, components.pressure); + } + + TEST_F(PressureComponentFactoryTest, ItReturnsNullptrPressureFormatMissing) + { + factory.FromJson(GetJson(nlohmann::json::object(), "pressure_format"), components); + EXPECT_EQ(nullptr, components.pressure); + } + + TEST_F(PressureComponentFactoryTest, ItReturnsNullptrQnhHectopascalsNotInteger) + { + factory.FromJson(GetJson(nlohmann::json{{"qnh", 12.23}}), components); + EXPECT_EQ(nullptr, components.pressure); + } + + TEST_F(PressureComponentFactoryTest, ItReturnsNullptrQnhHectopascalsMissing) + { + factory.FromJson(GetJson(nlohmann::json::object(), "qnh"), components); + EXPECT_EQ(nullptr, components.pressure); + } + + TEST_F(PressureComponentFactoryTest, ItReturnsNullptrQfeHectopascalsNotInteger) + { + factory.FromJson(GetJson(nlohmann::json{{"qfe", 12.23}}), components); + EXPECT_EQ(nullptr, components.pressure); + } + + TEST_F(PressureComponentFactoryTest, ItReturnsNullptrQfeHectopascalsMissing) + { + factory.FromJson(GetJson(nlohmann::json::object(), "qfe"), components); + EXPECT_EQ(nullptr, components.pressure); + } + + TEST_F(PressureComponentFactoryTest, ItReturnsNullptrQnhInhgNotNumber) + { + factory.FromJson(GetJson(nlohmann::json{{"qnh_inhg", "abc"}}), components); + EXPECT_EQ(nullptr, components.pressure); + } + + TEST_F(PressureComponentFactoryTest, ItReturnsNullptrQnhInHgMissing) + { + factory.FromJson(GetJson(nlohmann::json::object(), "qnh_inhg"), components); + EXPECT_EQ(nullptr, components.pressure); + } + + TEST_F(PressureComponentFactoryTest, ItReturnsNullptrQfeInhgNotNumber) + { + factory.FromJson(GetJson(nlohmann::json{{"qfe_inhg", "abc"}}), components); + EXPECT_EQ(nullptr, components.pressure); + } + + TEST_F(PressureComponentFactoryTest, ItReturnsNullptrQfeInHgMissing) + { + factory.FromJson(GetJson(nlohmann::json::object(), "qfe_inhg"), components); + EXPECT_EQ(nullptr, components.pressure); + } +} // namespace UKControllerPluginTest::Metar diff --git a/test/plugin/metar/PressureComponentTest.cpp b/test/plugin/metar/PressureComponentTest.cpp new file mode 100644 index 000000000..b262820d1 --- /dev/null +++ b/test/plugin/metar/PressureComponentTest.cpp @@ -0,0 +1,53 @@ +#include "metar/PressureComponent.h" + +using UKControllerPlugin::Metar::PressureComponent; +using UKControllerPlugin::Metar::PressureUnit; + +namespace UKControllerPluginTest::Metar { + class PressureComponentTest : public testing::Test + { + public: + PressureComponentTest() + : hectopascalComponent(1013, 1001, 29.92, 28.24, PressureUnit::Hectopascals), + inHgComponent(1013, 1001, 29.92, 28.24, PressureUnit::InHg) + { + } + + PressureComponent hectopascalComponent; + PressureComponent inHgComponent; + }; + + TEST_F(PressureComponentTest, ItHasAHectopascalQnhValue) + { + EXPECT_EQ(1013, hectopascalComponent.QnhHectopascals()); + } + + TEST_F(PressureComponentTest, ItHasAHectopascalQfeValue) + { + EXPECT_EQ(1001, hectopascalComponent.QfeHectopascals()); + } + + TEST_F(PressureComponentTest, ItHasAnInHgQnhValue) + { + EXPECT_FLOAT_EQ(29.92, hectopascalComponent.QnhInHg()); + } + + TEST_F(PressureComponentTest, ItHasAnInHgQfeValue) + { + EXPECT_FLOAT_EQ(28.24, hectopascalComponent.QfeInHg()); + } + + TEST_F(PressureComponentTest, ItIsReportedInHectopascals) + { + EXPECT_EQ(PressureUnit::Hectopascals, hectopascalComponent.ReportedIn()); + EXPECT_TRUE(hectopascalComponent.ReportedAsHectopascals()); + EXPECT_FALSE(hectopascalComponent.ReportedAsInHg()); + } + + TEST_F(PressureComponentTest, ItIsReportedInInHg) + { + EXPECT_EQ(PressureUnit::InHg, inHgComponent.ReportedIn()); + EXPECT_FALSE(inHgComponent.ReportedAsHectopascals()); + EXPECT_TRUE(inHgComponent.ReportedAsInHg()); + } +} // namespace UKControllerPluginTest::Metar diff --git a/test/plugin/metar/PressureMonitorTest.cpp b/test/plugin/metar/PressureMonitorTest.cpp index a6f272a0d..6e60f5f05 100644 --- a/test/plugin/metar/PressureMonitorTest.cpp +++ b/test/plugin/metar/PressureMonitorTest.cpp @@ -1,3 +1,6 @@ +#include "metar/MetarComponents.h" +#include "metar/ParsedMetar.h" +#include "metar/PressureComponent.h" #include "metar/PressureMonitor.h" #include "euroscope/GeneralSettingsEntries.h" #include "controller/ControllerPosition.h" @@ -12,139 +15,155 @@ using UKControllerPlugin::Controller::ControllerPosition; using UKControllerPlugin::Euroscope::GeneralSettingsEntries; using UKControllerPlugin::Euroscope::UserSetting; using UKControllerPlugin::Message::UserMessager; +using UKControllerPlugin::Metar::MetarComponents; +using UKControllerPlugin::Metar::ParsedMetar; +using UKControllerPlugin::Metar::PressureComponent; using UKControllerPlugin::Metar::PressureMonitor; +using UKControllerPlugin::Metar::PressureUnit; using UKControllerPluginTest::Euroscope::MockEuroscopePluginLoopbackInterface; using UKControllerPluginTest::Euroscope::MockUserSettingProviderInterface; -namespace UKControllerPluginTest { - namespace Metar { +namespace UKControllerPluginTest::Metar { - class PressureMonitorTest : public Test + class PressureMonitorTest : public Test + { + public: + PressureMonitorTest() + : gatwickTower(1, "EGKK_TWR", 124.22, {"EGKK"}, true, false), messager(mockPlugin), + userSetting(mockUserSettingProvider), monitor(messager, activeCallsigns) { - public: - PressureMonitorTest() - : gatwickTower(1, "EGKK_TWR", 124.22, {"EGKK"}, true, false), messager(mockPlugin), - userSetting(mockUserSettingProvider), monitor(messager, activeCallsigns) - { - this->activeCallsigns.AddUserCallsign(ActiveCallsign("EGKK_TWR", "Testy", this->gatwickTower, true)); - } - NiceMock mockPlugin; - NiceMock mockUserSettingProvider; - ControllerPosition gatwickTower; - ActiveCallsignCollection activeCallsigns; - UserMessager messager; - UserSetting userSetting; - PressureMonitor monitor; - }; - - TEST_F(PressureMonitorTest, NotificationsDefaultToOff) - { - EXPECT_FALSE(this->monitor.NotificationsEnabled()); - } - - TEST_F(PressureMonitorTest, NotificationsCanBeTurnedOnAndOff) - { - this->monitor.SetNotficationsEnabled(true); - EXPECT_TRUE(this->monitor.NotificationsEnabled()); - this->monitor.SetNotficationsEnabled(false); - EXPECT_FALSE(this->monitor.NotificationsEnabled()); - } - - TEST_F(PressureMonitorTest, ItDoesNothingIfNoQnhInMetar) - { - this->monitor.NewMetar("EGKK", "EGKK 02012KT SCT002"); - EXPECT_EQ(this->monitor.qnhNotStored, this->monitor.GetStoredQnh("EGKK")); - } - - TEST_F(PressureMonitorTest, ItSetsQnhOnFirstTimeMetar) - { - this->monitor.NewMetar("EGKK", "EGKK 02012KT Q1011 SCT002"); - EXPECT_EQ("1011", this->monitor.GetStoredQnh("EGKK")); - } - - TEST_F(PressureMonitorTest, ItDoesntSendUpdateMessageOnFirstTimeQnh) - { - this->monitor.SetNotficationsEnabled(true); - - EXPECT_CALL(this->mockPlugin, ChatAreaMessage(_, _, _, _, _, _, _, _)).Times(0); - - this->monitor.NewMetar("EGKK", "EGKK 02012KT Q1011 SCT002"); - } - - TEST_F(PressureMonitorTest, ItHandlesSameQnh) - { - this->monitor.NewMetar("EGKK", "EGKK 02012KT Q1011 SCT002"); - this->monitor.NewMetar("EGKK", "EGKK 02012KT Q1011 SCT002"); - EXPECT_EQ("1011", this->monitor.GetStoredQnh("EGKK")); - } - - TEST_F(PressureMonitorTest, ItDoesntSendUpdateMessageOnSameQnh) - { - EXPECT_CALL(this->mockPlugin, ChatAreaMessage(_, _, _, _, _, _, _, _)).Times(0); - - this->monitor.SetNotficationsEnabled(true); - - this->monitor.NewMetar("EGKK", "EGKK 02012KT Q1011 SCT002"); - this->monitor.NewMetar("EGKK", "EGKK 02012KT Q1011 SCT002"); + this->activeCallsigns.AddUserCallsign(ActiveCallsign("EGKK_TWR", "Testy", this->gatwickTower, true)); } - TEST_F(PressureMonitorTest, ItSendsUpdateMessageOnNewQnh) + [[nodiscard]] static auto GetParsedMetar(int qnh, int qfe, std::string station = "EGKK") -> ParsedMetar { - EXPECT_CALL(this->mockPlugin, ChatAreaMessage(_, _, "New QNH at EGKK, Was: 1011, Now: 1012", _, _, _, _, _)) - .Times(1); - - this->monitor.SetNotficationsEnabled(true); - - this->monitor.NewMetar("EGKK", "EGKK 02012KT Q1011 SCT002"); - this->monitor.NewMetar("EGKK", "EGKK 02012KT Q1012 SCT002"); - } - - TEST_F(PressureMonitorTest, ItDoesntSendMessageIfNonConcernedStation) - { - EXPECT_CALL(this->mockPlugin, ChatAreaMessage(_, _, _, _, _, _, _, _)).Times(0); - - this->monitor.SetNotficationsEnabled(true); - - this->monitor.NewMetar("EGLL", "EGLL 02012KT Q1011 SCT002"); - this->monitor.NewMetar("EGLL", "EGLL 02012KT Q1012 SCT002"); - } - - TEST_F(PressureMonitorTest, ItDoesntSendUpdateMessageIfTurnedOff) - { - EXPECT_CALL(this->mockPlugin, ChatAreaMessage(_, _, _, _, _, _, _, _)).Times(0); - - this->monitor.NewMetar("EGKK", "EGKK 02012KT Q1011 SCT002"); - this->monitor.NewMetar("EGKK", "EGKK 02012KT Q1012 SCT002"); + auto components = std::make_unique(); + components->pressure = + std::make_shared(qnh, qfe, 29.92, 29.93, PressureUnit::Hectopascals); + return ParsedMetar(station, "foo", std::move(components)); } - TEST_F(PressureMonitorTest, ItStoresUpdatedQnh) - { - ON_CALL(this->mockUserSettingProvider, GetKey(GeneralSettingsEntries::pressureMonitorSendMessageKey)) - .WillByDefault(Return("0")); - - this->monitor.NewMetar("EGKK", "EGKK 02012KT Q1011 SCT002"); - this->monitor.NewMetar("EGKK", "EGKK 02012KT Q1012 SCT002"); - EXPECT_EQ("1012", this->monitor.GetStoredQnh("EGKK")); - } - - TEST_F(PressureMonitorTest, ItUpdatesFromUserSettings) - { - ON_CALL(this->mockUserSettingProvider, GetKey(GeneralSettingsEntries::pressureMonitorSendMessageKey)) - .WillByDefault(Return("1")); - - this->monitor.UserSettingsUpdated(this->userSetting); - EXPECT_TRUE(this->monitor.NotificationsEnabled()); - } - - TEST_F(PressureMonitorTest, ItDefaultsToOffIfNoUserSetting) - { - - ON_CALL(this->mockUserSettingProvider, GetKey(GeneralSettingsEntries::pressureMonitorSendMessageKey)) - .WillByDefault(Return("")); - - this->monitor.SetNotficationsEnabled(true); - this->monitor.UserSettingsUpdated(this->userSetting); - EXPECT_FALSE(this->monitor.NotificationsEnabled()); - } - } // namespace Metar -} // namespace UKControllerPluginTest + NiceMock mockPlugin; + NiceMock mockUserSettingProvider; + ControllerPosition gatwickTower; + ActiveCallsignCollection activeCallsigns; + UserMessager messager; + UserSetting userSetting; + PressureMonitor monitor; + }; + + TEST_F(PressureMonitorTest, NotificationsDefaultToOff) + { + EXPECT_FALSE(this->monitor.NotificationsEnabled()); + } + + TEST_F(PressureMonitorTest, NotificationsCanBeTurnedOnAndOff) + { + this->monitor.SetNotficationsEnabled(true); + EXPECT_TRUE(this->monitor.NotificationsEnabled()); + this->monitor.SetNotficationsEnabled(false); + EXPECT_FALSE(this->monitor.NotificationsEnabled()); + } + + TEST_F(PressureMonitorTest, ItDoesNothingIfNoPressureComponents) + { + this->monitor.MetarUpdated(ParsedMetar("EGKK", "foo", std::make_unique())); + EXPECT_EQ(nullptr, this->monitor.GetStoredPressure("EGKK")); + } + + TEST_F(PressureMonitorTest, ItSetsQnhOnFirstTimeMetar) + { + this->monitor.MetarUpdated(GetParsedMetar(1011, 1012)); + EXPECT_EQ(1011, monitor.GetStoredPressure("EGKK")->QnhHectopascals()); + EXPECT_EQ(1012, monitor.GetStoredPressure("EGKK")->QfeHectopascals()); + } + + TEST_F(PressureMonitorTest, ItDoesntSendUpdateMessageOnFirstTimeQnh) + { + this->monitor.SetNotficationsEnabled(true); + + EXPECT_CALL(this->mockPlugin, ChatAreaMessage(_, _, _, _, _, _, _, _)).Times(0); + + this->monitor.MetarUpdated(GetParsedMetar(1011, 1012)); + } + + TEST_F(PressureMonitorTest, ItHandlesSameQnh) + { + this->monitor.MetarUpdated(GetParsedMetar(1011, 1012)); + this->monitor.MetarUpdated(GetParsedMetar(1011, 1012)); + EXPECT_EQ(1011, monitor.GetStoredPressure("EGKK")->QnhHectopascals()); + EXPECT_EQ(1012, monitor.GetStoredPressure("EGKK")->QfeHectopascals()); + } + + TEST_F(PressureMonitorTest, ItDoesntSendUpdateMessageOnSameQnh) + { + EXPECT_CALL(this->mockPlugin, ChatAreaMessage(_, _, _, _, _, _, _, _)).Times(0); + + this->monitor.SetNotficationsEnabled(true); + + this->monitor.MetarUpdated(GetParsedMetar(1011, 1012)); + this->monitor.MetarUpdated(GetParsedMetar(1011, 1012)); + } + + TEST_F(PressureMonitorTest, ItSendsUpdateMessageOnNewQnh) + { + EXPECT_CALL( + this->mockPlugin, + ChatAreaMessage( + _, _, "Pressure change at EGKK. QNH was 1011, now 1012. QFE was 1012, now 1013.", _, _, _, _, _)) + .Times(1); + + this->monitor.SetNotficationsEnabled(true); + + this->monitor.MetarUpdated(GetParsedMetar(1011, 1012)); + this->monitor.MetarUpdated(GetParsedMetar(1012, 1013)); + } + + TEST_F(PressureMonitorTest, ItDoesntSendMessageIfNonConcernedStation) + { + EXPECT_CALL(this->mockPlugin, ChatAreaMessage(_, _, _, _, _, _, _, _)).Times(0); + + this->monitor.SetNotficationsEnabled(true); + + this->monitor.MetarUpdated(GetParsedMetar(1011, 1012, "EGLL")); + this->monitor.MetarUpdated(GetParsedMetar(1012, 1013, "EGLL")); + } + + TEST_F(PressureMonitorTest, ItDoesntSendUpdateMessageIfTurnedOff) + { + EXPECT_CALL(this->mockPlugin, ChatAreaMessage(_, _, _, _, _, _, _, _)).Times(0); + + this->monitor.MetarUpdated(GetParsedMetar(1011, 1012)); + this->monitor.MetarUpdated(GetParsedMetar(1012, 1013)); + } + + TEST_F(PressureMonitorTest, ItStoresUpdatedQnh) + { + ON_CALL(this->mockUserSettingProvider, GetKey(GeneralSettingsEntries::pressureMonitorSendMessageKey)) + .WillByDefault(Return("0")); + + this->monitor.MetarUpdated(GetParsedMetar(1011, 1012)); + this->monitor.MetarUpdated(GetParsedMetar(1012, 1013)); + EXPECT_EQ(1012, this->monitor.GetStoredPressure("EGKK")->QnhHectopascals()); + } + + TEST_F(PressureMonitorTest, ItUpdatesFromUserSettings) + { + ON_CALL(this->mockUserSettingProvider, GetKey(GeneralSettingsEntries::pressureMonitorSendMessageKey)) + .WillByDefault(Return("1")); + + this->monitor.UserSettingsUpdated(this->userSetting); + EXPECT_TRUE(this->monitor.NotificationsEnabled()); + } + + TEST_F(PressureMonitorTest, ItDefaultsToOffIfNoUserSetting) + { + + ON_CALL(this->mockUserSettingProvider, GetKey(GeneralSettingsEntries::pressureMonitorSendMessageKey)) + .WillByDefault(Return("")); + + this->monitor.SetNotficationsEnabled(true); + this->monitor.UserSettingsUpdated(this->userSetting); + EXPECT_FALSE(this->monitor.NotificationsEnabled()); + } +} // namespace UKControllerPluginTest::Metar diff --git a/test/plugin/metar/PressureNotFoundMessageTest.cpp b/test/plugin/metar/PressureNotFoundMessageTest.cpp new file mode 100644 index 000000000..e74e47043 --- /dev/null +++ b/test/plugin/metar/PressureNotFoundMessageTest.cpp @@ -0,0 +1,57 @@ +#include "metar/PressureNotFoundMessage.h" + +using testing::Test; +using UKControllerPlugin::Metar::PressureNotFoundMessage; + +namespace UKControllerPluginTest::Metar { + + class PressureNotFoundMessageTest : public Test + { + public: + PressureNotFoundMessageTest() : message("EGKK") + { + } + + PressureNotFoundMessage message; + }; + + TEST_F(PressureNotFoundMessageTest, ItHasAMessageHandler) + { + EXPECT_EQ("UKCP_PRESSURE_MONITOR", message.MessageHandler()); + } + + TEST_F(PressureNotFoundMessageTest, ItHasASender) + { + EXPECT_EQ("UKCP", message.MessageSender()); + } + + TEST_F(PressureNotFoundMessageTest, ItShowsTheHandler) + { + EXPECT_TRUE(message.MessageShowHandler()); + } + + TEST_F(PressureNotFoundMessageTest, ItMarksItAsUnread) + { + EXPECT_TRUE(message.MessageMarkUnread()); + } + + TEST_F(PressureNotFoundMessageTest, ItOverridesBusy) + { + EXPECT_TRUE(message.MessageOverrideBusy()); + } + + TEST_F(PressureNotFoundMessageTest, ItFlashesTheHandler) + { + EXPECT_TRUE(message.MessageFlashHandler()); + } + + TEST_F(PressureNotFoundMessageTest, ItRequiresConfirmation) + { + EXPECT_TRUE(message.MessageRequiresConfirm()); + } + + TEST_F(PressureNotFoundMessageTest, ItHasAMessage) + { + EXPECT_EQ("Pressure information is not available for EGKK.", message.MessageString()); + } +} // namespace UKControllerPluginTest::Metar diff --git a/test/plugin/metar/PressureQueryCommandHandlerTest.cpp b/test/plugin/metar/PressureQueryCommandHandlerTest.cpp new file mode 100644 index 000000000..6752b06a6 --- /dev/null +++ b/test/plugin/metar/PressureQueryCommandHandlerTest.cpp @@ -0,0 +1,107 @@ +#include "message/UserMessager.h" +#include "metar/MetarComponents.h" +#include "metar/ParsedMetar.h" +#include "metar/ParsedMetarCollection.h" +#include "metar/PressureComponent.h" +#include "metar/PressureQueryCommandHandler.h" + +using testing::_; +using UKControllerPlugin::Message::UserMessager; +using UKControllerPlugin::Metar::MetarComponents; +using UKControllerPlugin::Metar::ParsedMetar; +using UKControllerPlugin::Metar::ParsedMetarCollection; +using UKControllerPlugin::Metar::PressureComponent; +using UKControllerPlugin::Metar::PressureQueryCommandHandler; +using UKControllerPlugin::Metar::PressureUnit; + +namespace UKControllerPluginTest::Metar { + class PressureQueryCommandHandlerTest : public testing::Test + { + public: + PressureQueryCommandHandlerTest() : messager(mockPlugin), handler(metars, messager) + { + this->metars.UpdateMetar(std::make_shared("EGKK", "FOO", std::make_unique())); + + auto components = std::make_unique(); + components->pressure = + std::make_shared(1013, 1011, 29.92, 29.84, PressureUnit::Hectopascals); + this->metars.UpdateMetar(std::make_shared("EGLL", "FOO", std::move(components))); + } + + testing::NiceMock mockPlugin; + UserMessager messager; + ParsedMetarCollection metars; + PressureQueryCommandHandler handler; + }; + + TEST_F(PressureQueryCommandHandlerTest, ItReturnsFalseBadCommand) + { + EXPECT_FALSE(handler.ProcessCommand(".ukcp notpressure EGKK")); + } + + TEST_F(PressureQueryCommandHandlerTest, ItReturnsFalseCommandWithThingsAfter) + { + EXPECT_FALSE(handler.ProcessCommand(".ukcp pressure EGKK lol")); + } + + TEST_F(PressureQueryCommandHandlerTest, ItReturnsFalseCommandWithThingsBefore) + { + EXPECT_FALSE(handler.ProcessCommand(".ukcp lol pressure EGKK")); + } + + TEST_F(PressureQueryCommandHandlerTest, ItReturnsFalseCommandAirfieldTooLong) + { + EXPECT_FALSE(handler.ProcessCommand(".ukcp pressure EGKKS")); + } + + TEST_F(PressureQueryCommandHandlerTest, ItReturnsFalseCommandAirfieldTooShort) + { + EXPECT_FALSE(handler.ProcessCommand(".ukcp pressure EGK")); + } + + TEST_F(PressureQueryCommandHandlerTest, ItReturnsFalseCommandNoAirfield) + { + EXPECT_FALSE(handler.ProcessCommand(".ukcp pressure")); + } + + TEST_F(PressureQueryCommandHandlerTest, ItReturnsTrueAirfieldWithNoMetar) + { + EXPECT_TRUE(handler.ProcessCommand(".ukcp pressure EGXX")); + } + + TEST_F(PressureQueryCommandHandlerTest, ItSendsMessageNoMetar) + { + EXPECT_CALL( + this->mockPlugin, ChatAreaMessage(_, _, "Pressure information is not available for EGXX.", _, _, _, _, _)) + .Times(1); + + handler.ProcessCommand(".ukcp pressure EGXX"); + } + + TEST_F(PressureQueryCommandHandlerTest, ItReturnsTrueAirfieldWithNoPressureComponent) + { + EXPECT_TRUE(handler.ProcessCommand(".ukcp pressure EGLL")); + } + + TEST_F(PressureQueryCommandHandlerTest, ItSendsMessageNoPressureComponent) + { + EXPECT_CALL( + this->mockPlugin, ChatAreaMessage(_, _, "Pressure information is not available for EGKK.", _, _, _, _, _)) + .Times(1); + + handler.ProcessCommand(".ukcp pressure EGKK"); + } + + TEST_F(PressureQueryCommandHandlerTest, ItReturnsTrueAirfieldWithPressureComponent) + { + EXPECT_TRUE(handler.ProcessCommand(".ukcp pressure EGLL")); + } + + TEST_F(PressureQueryCommandHandlerTest, ItSendsMessageWithPressureComponent) + { + EXPECT_CALL(this->mockPlugin, ChatAreaMessage(_, _, "QNH at EGLL is 1013, QFE is 1011.", _, _, _, _, _)) + .Times(1); + + handler.ProcessCommand(".ukcp pressure EGLL"); + } +} // namespace UKControllerPluginTest::Metar diff --git a/test/plugin/metar/PressureQueryMessageTest.cpp b/test/plugin/metar/PressureQueryMessageTest.cpp new file mode 100644 index 000000000..76b046d04 --- /dev/null +++ b/test/plugin/metar/PressureQueryMessageTest.cpp @@ -0,0 +1,71 @@ +#include "metar/PressureComponent.h" +#include "metar/PressureQueryMessage.h" + +using testing::Test; +using UKControllerPlugin::Metar::PressureComponent; +using UKControllerPlugin::Metar::PressureQueryMessage; +using UKControllerPlugin::Metar::PressureUnit; + +namespace UKControllerPluginTest::Metar { + + class PressureQueryMessageTest : public Test + { + public: + PressureQueryMessageTest() + : hectopascalsComponent(1013, 1011, 29.92, 29.84, PressureUnit::Hectopascals), + inHgComponent(1013, 1011, 29.92, 29.84, PressureUnit::InHg), + messageHectopascals("EGKK", hectopascalsComponent), messageInhg("EGLL", inHgComponent) + { + } + + PressureComponent hectopascalsComponent; + PressureComponent inHgComponent; + PressureQueryMessage messageHectopascals; + PressureQueryMessage messageInhg; + }; + + TEST_F(PressureQueryMessageTest, ItHasAMessageHandler) + { + EXPECT_EQ("UKCP_PRESSURE_MONITOR", messageHectopascals.MessageHandler()); + } + + TEST_F(PressureQueryMessageTest, ItHasASender) + { + EXPECT_EQ("UKCP", messageHectopascals.MessageSender()); + } + + TEST_F(PressureQueryMessageTest, ItShowsTheHandler) + { + EXPECT_TRUE(messageHectopascals.MessageShowHandler()); + } + + TEST_F(PressureQueryMessageTest, ItMarksItAsUnread) + { + EXPECT_TRUE(messageHectopascals.MessageMarkUnread()); + } + + TEST_F(PressureQueryMessageTest, ItOverridesBusy) + { + EXPECT_TRUE(messageHectopascals.MessageOverrideBusy()); + } + + TEST_F(PressureQueryMessageTest, ItFlashesTheHandler) + { + EXPECT_TRUE(messageHectopascals.MessageFlashHandler()); + } + + TEST_F(PressureQueryMessageTest, ItRequiresConfirmation) + { + EXPECT_TRUE(messageHectopascals.MessageRequiresConfirm()); + } + + TEST_F(PressureQueryMessageTest, ItHasAMessageHectopascals) + { + EXPECT_EQ("QNH at EGKK is 1013, QFE is 1011.", messageHectopascals.MessageString()); + } + + TEST_F(PressureQueryMessageTest, ItHasAMessageInHg) + { + EXPECT_EQ("QNH at EGLL is 29.92, QFE is 29.84.", messageInhg.MessageString()); + } +} // namespace UKControllerPluginTest::Metar diff --git a/test/plugin/mock/MockMinStack.h b/test/plugin/mock/MockMinStack.h deleted file mode 100644 index 09fcf3437..000000000 --- a/test/plugin/mock/MockMinStack.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once -#include "minstack/MinStackManager.h" - -namespace UKControllerPluginTest { - namespace MinStack { - class MockMinStack : public UKControllerPlugin::MinStack::MinStackManager - { - public: - MockMinStack() : MinStackManager() {} - MOCK_METHOD1(IsConcernedAirfield, bool(std::string)); - MOCK_METHOD2(NewMetar, void(std::string, std::string)); - }; - } // namespace MinStack -} // namespace UKControllerPluginTest diff --git a/test/plugin/pch/pch.h b/test/plugin/pch/pch.h index 11f550294..4c7aec457 100644 --- a/test/plugin/pch/pch.h +++ b/test/plugin/pch/pch.h @@ -40,7 +40,6 @@ #include "../mock/MockHistoryTrailDialog.h" #include "../mock/MockHistoryTrailRepository.h" #include "../mock/MockIntegrationActionProcessor.h" -#include "../mock/MockMinStack.h" #include "../mock/MockOutboundIntegrationEventHandler.h" #include "../mock/MockPushEventConnection.h" #include "../mock/MockPushEventProcessor.h" diff --git a/test/testingutils/mock/MockApiInterface.h b/test/testingutils/mock/MockApiInterface.h index b07a09ea1..0e4e9c629 100644 --- a/test/testingutils/mock/MockApiInterface.h +++ b/test/testingutils/mock/MockApiInterface.h @@ -1,69 +1,70 @@ -#pragma once -#include "api/ApiInterface.h" -#include "srd/SrdSearchParameters.h" - -namespace UKControllerPluginTest::Api { - class MockApiInterface : public UKControllerPlugin::Api::ApiInterface - { - public: - MOCK_CONST_METHOD3( - CreateGeneralSquawkAssignment, - UKControllerPlugin::Squawk::ApiSquawkAllocation(std::string, std::string, std::string)); - MOCK_CONST_METHOD3( - CreateLocalSquawkAssignment, - UKControllerPlugin::Squawk::ApiSquawkAllocation(std::string, std::string, std::string)); - MOCK_CONST_METHOD0(CheckApiAuthorisation, bool(void)); - MOCK_CONST_METHOD1(DeleteSquawkAssignment, void(std::string)); - MOCK_CONST_METHOD0(GetDependencyList, nlohmann::json(void)); - MOCK_CONST_METHOD1(FetchRemoteFile, std::string(std::string)); - MOCK_CONST_METHOD1(GetAssignedSquawk, UKControllerPlugin::Squawk::ApiSquawkAllocation(std::string)); - MOCK_CONST_METHOD0(GetApiDomain, std::string(void)); - MOCK_CONST_METHOD0(GetApiKey, std::string(void)); - MOCK_METHOD( - nlohmann::json, - CreatePrenoteMessage, - (const std::string&, const std::string&, const std::string&, const std::string&, int, int, int), - (const, override)); - MOCK_METHOD(void, AcknowledgePrenoteMessage, (int, int), (const, override)); - MOCK_METHOD(void, DeletePrenoteMessage, (int), (const, override)); - MOCK_METHOD(nlohmann::json, CreateMissedApproach, (const std::string&), (const, override)); - MOCK_METHOD(void, AcknowledgeMissedApproach, (int, const std::string&), (const, override)); - MOCK_CONST_METHOD0(GetHoldDependency, nlohmann::json(void)); - MOCK_CONST_METHOD0(GetAssignedHolds, nlohmann::json(void)); - MOCK_CONST_METHOD2(AssignAircraftToHold, void(std::string, std::string)); - MOCK_CONST_METHOD1(UnassignAircraftHold, void(std::string)); - MOCK_CONST_METHOD0(GetMinStackLevels, nlohmann::json(void)); - MOCK_CONST_METHOD1(GetUri, nlohmann::json(std::string uri)); - MOCK_CONST_METHOD0(GetRegionalPressures, nlohmann::json(void)); - MOCK_CONST_METHOD1(SearchSrd, nlohmann::json(UKControllerPlugin::Srd::SrdSearchParameters)); - MOCK_CONST_METHOD0(GetAssignedStands, nlohmann::json(void)); - MOCK_CONST_METHOD2(AssignStandToAircraft, void(std::string, int)); - MOCK_CONST_METHOD1(DeleteStandAssignmentForAircraft, void(std::string)); - MOCK_CONST_METHOD4(SendEnrouteRelease, void(std::string, std::string, std::string, int)); - MOCK_CONST_METHOD5( - SendEnrouteReleaseWithReleasePoint, void(std::string, std::string, std::string, int, std::string)); - MOCK_CONST_METHOD0(GetAllNotifications, nlohmann::json(void)); - MOCK_CONST_METHOD0(GetUnreadNotifications, nlohmann::json(void)); - MOCK_CONST_METHOD1(ReadNotification, void(int)); - MOCK_CONST_METHOD0(SyncPluginEvents, nlohmann::json(void)); - MOCK_CONST_METHOD1(GetLatestPluginEvents, nlohmann::json(int)); - MOCK_CONST_METHOD1(UpdateCheck, int(std::string)); - MOCK_METHOD1(SetApiDomain, void(std::string)); - MOCK_METHOD1(SetApiKey, void(std::string)); - MOCK_CONST_METHOD0(GetUpdateDetails, nlohmann::json(void)); - MOCK_CONST_METHOD2(AcknowledgeDepartureReleaseRequest, void(int releaseId, int controllerPositionId)); - MOCK_CONST_METHOD2(RejectDepartureReleaseRequest, void(int releaseId, int controllerPositionId)); - MOCK_CONST_METHOD4( - ApproveDepartureReleaseRequest, - void( - int releaseId, - int controllerPositionId, - std::chrono::system_clock::time_point releasedAt, - int expiresInSeconds)); - MOCK_CONST_METHOD4( - RequestDepartureRelease, - nlohmann::json( - std::string callsign, int requestingControllerId, int targetControllerId, int expiresInSeconds)); - MOCK_CONST_METHOD1(CancelDepartureReleaseRequest, void(int releaseId)); - }; -} // namespace UKControllerPluginTest::Api +#pragma once +#include "api/ApiInterface.h" +#include "srd/SrdSearchParameters.h" + +namespace UKControllerPluginTest::Api { + class MockApiInterface : public UKControllerPlugin::Api::ApiInterface + { + public: + MOCK_CONST_METHOD3( + CreateGeneralSquawkAssignment, + UKControllerPlugin::Squawk::ApiSquawkAllocation(std::string, std::string, std::string)); + MOCK_CONST_METHOD3( + CreateLocalSquawkAssignment, + UKControllerPlugin::Squawk::ApiSquawkAllocation(std::string, std::string, std::string)); + MOCK_CONST_METHOD0(CheckApiAuthorisation, bool(void)); + MOCK_CONST_METHOD1(DeleteSquawkAssignment, void(std::string)); + MOCK_CONST_METHOD0(GetDependencyList, nlohmann::json(void)); + MOCK_CONST_METHOD1(FetchRemoteFile, std::string(std::string)); + MOCK_CONST_METHOD1(GetAssignedSquawk, UKControllerPlugin::Squawk::ApiSquawkAllocation(std::string)); + MOCK_CONST_METHOD0(GetApiDomain, std::string(void)); + MOCK_CONST_METHOD0(GetApiKey, std::string(void)); + MOCK_METHOD( + nlohmann::json, + CreatePrenoteMessage, + (const std::string&, const std::string&, const std::string&, const std::string&, int, int, int), + (const, override)); + MOCK_METHOD(void, AcknowledgePrenoteMessage, (int, int), (const, override)); + MOCK_METHOD(void, DeletePrenoteMessage, (int), (const, override)); + MOCK_METHOD(nlohmann::json, CreateMissedApproach, (const std::string&), (const, override)); + MOCK_METHOD(nlohmann::json, GetAllMetars, (), (const, override)); + MOCK_METHOD(void, AcknowledgeMissedApproach, (int, const std::string&), (const, override)); + MOCK_CONST_METHOD0(GetHoldDependency, nlohmann::json(void)); + MOCK_CONST_METHOD0(GetAssignedHolds, nlohmann::json(void)); + MOCK_CONST_METHOD2(AssignAircraftToHold, void(std::string, std::string)); + MOCK_CONST_METHOD1(UnassignAircraftHold, void(std::string)); + MOCK_CONST_METHOD0(GetMinStackLevels, nlohmann::json(void)); + MOCK_CONST_METHOD1(GetUri, nlohmann::json(std::string uri)); + MOCK_CONST_METHOD0(GetRegionalPressures, nlohmann::json(void)); + MOCK_CONST_METHOD1(SearchSrd, nlohmann::json(UKControllerPlugin::Srd::SrdSearchParameters)); + MOCK_CONST_METHOD0(GetAssignedStands, nlohmann::json(void)); + MOCK_CONST_METHOD2(AssignStandToAircraft, void(std::string, int)); + MOCK_CONST_METHOD1(DeleteStandAssignmentForAircraft, void(std::string)); + MOCK_CONST_METHOD4(SendEnrouteRelease, void(std::string, std::string, std::string, int)); + MOCK_CONST_METHOD5( + SendEnrouteReleaseWithReleasePoint, void(std::string, std::string, std::string, int, std::string)); + MOCK_CONST_METHOD0(GetAllNotifications, nlohmann::json(void)); + MOCK_CONST_METHOD0(GetUnreadNotifications, nlohmann::json(void)); + MOCK_CONST_METHOD1(ReadNotification, void(int)); + MOCK_CONST_METHOD0(SyncPluginEvents, nlohmann::json(void)); + MOCK_CONST_METHOD1(GetLatestPluginEvents, nlohmann::json(int)); + MOCK_CONST_METHOD1(UpdateCheck, int(std::string)); + MOCK_METHOD1(SetApiDomain, void(std::string)); + MOCK_METHOD1(SetApiKey, void(std::string)); + MOCK_CONST_METHOD0(GetUpdateDetails, nlohmann::json(void)); + MOCK_CONST_METHOD2(AcknowledgeDepartureReleaseRequest, void(int releaseId, int controllerPositionId)); + MOCK_CONST_METHOD2(RejectDepartureReleaseRequest, void(int releaseId, int controllerPositionId)); + MOCK_CONST_METHOD4( + ApproveDepartureReleaseRequest, + void( + int releaseId, + int controllerPositionId, + std::chrono::system_clock::time_point releasedAt, + int expiresInSeconds)); + MOCK_CONST_METHOD4( + RequestDepartureRelease, + nlohmann::json( + std::string callsign, int requestingControllerId, int targetControllerId, int expiresInSeconds)); + MOCK_CONST_METHOD1(CancelDepartureReleaseRequest, void(int releaseId)); + }; +} // namespace UKControllerPluginTest::Api diff --git a/test/utils/api/ApiHelperTest.cpp b/test/utils/api/ApiHelperTest.cpp index 94bfd6d4f..0c7592ee7 100644 --- a/test/utils/api/ApiHelperTest.cpp +++ b/test/utils/api/ApiHelperTest.cpp @@ -1,837 +1,850 @@ -#include "api/ApiException.h" -#include "api/ApiHelper.h" -#include "api/ApiNotAuthorisedException.h" -#include "api/ApiNotFoundException.h" -#include "curl/CurlInterface.h" -#include "helper/ApiRequestHelperFunctions.h" - -using ::testing::_; -using ::testing::NiceMock; -using ::testing::Return; -using ::testing::Test; -using UKControllerPlugin::Api::ApiException; -using UKControllerPlugin::Api::ApiHelper; -using UKControllerPlugin::Api::ApiNotAuthorisedException; -using UKControllerPlugin::Api::ApiNotFoundException; -using UKControllerPlugin::Api::ApiRequestBuilder; -using UKControllerPlugin::Api::ApiResponse; -using UKControllerPlugin::Curl::CurlInterface; -using UKControllerPlugin::Curl::CurlRequest; -using UKControllerPlugin::Curl::CurlResponse; -using UKControllerPlugin::Squawk::ApiSquawkAllocation; -using UKControllerPlugin::Srd::SrdSearchParameters; -using UKControllerPluginTest::Curl::MockCurlApi; - -namespace UKControllerPluginUtilsTest::Api { - - class ApiHelperTest : public Test - { - public: - ApiHelperTest() : helper(mockCurlApi, GetApiRequestBuilder()) - { - } - - ApiHelper helper; - NiceMock mockCurlApi; - }; - - TEST_F(ApiHelperTest, TestItReturnsApiAuthorisedIf200) - { - CurlResponse response(R"({"message": "teapots"})", false, 200); - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(GetApiCurlRequest("/authorise", CurlRequest::METHOD_GET))) - .Times(1) - .WillOnce(Return(response)); - - EXPECT_TRUE(this->helper.CheckApiAuthorisation()); - } - - TEST_F(ApiHelperTest, TestItReturnsNotApiAuthorisedIfNot200) - { - CurlResponse response(R"({"message": "teapots"})", false, 201); - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(GetApiCurlRequest("/authorise", CurlRequest::METHOD_GET))) - .Times(1) - .WillOnce(Return(response)); - - EXPECT_FALSE(this->helper.CheckApiAuthorisation()); - } - - TEST_F(ApiHelperTest, TestItThrowsNotFoundExceptionIf404) - { - CurlResponse response(R"({"message": "teapots"})", false, 404); - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(GetApiCurlRequest("/authorise", CurlRequest::METHOD_GET))) - .Times(1) - .WillOnce(Return(response)); - - EXPECT_THROW(static_cast(this->helper.CheckApiAuthorisation()), ApiNotFoundException); - } - - TEST_F(ApiHelperTest, TestItThrowsNotAuthorisedExceptionIf401) - { - CurlResponse response(R"({"message": "teapots"})", false, 401); - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(GetApiCurlRequest("/authorise", CurlRequest::METHOD_GET))) - .Times(1) - .WillOnce(Return(response)); - - EXPECT_THROW(static_cast(this->helper.CheckApiAuthorisation()), ApiNotAuthorisedException); - } - - TEST_F(ApiHelperTest, TestItThrowsNotAuthorisedExceptionIf403) - { - CurlResponse response(R"({"message": "teapots"})", false, 403); - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(GetApiCurlRequest("/authorise", CurlRequest::METHOD_GET))) - .Times(1) - .WillOnce(Return(response)); - - EXPECT_THROW(static_cast(this->helper.CheckApiAuthorisation()), ApiNotAuthorisedException); - } - - TEST_F(ApiHelperTest, TestItThrowsApiExceptionIfServiceUnavailable) - { - CurlResponse response(R"({"message": "teapots"})", false, 503); - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(GetApiCurlRequest("/authorise", CurlRequest::METHOD_GET))) - .Times(1) - .WillOnce(Return(response)); - - EXPECT_THROW(static_cast(this->helper.CheckApiAuthorisation()), ApiException); - } - - TEST_F(ApiHelperTest, TestItThrowsApiExceptionIfBadGateway) - { - CurlResponse response(R"({"message": "teapots"})", false, 502); - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(GetApiCurlRequest("/authorise", CurlRequest::METHOD_GET))) - .Times(1) - .WillOnce(Return(response)); - - EXPECT_THROW(static_cast(this->helper.CheckApiAuthorisation()), ApiException); - } - - TEST_F(ApiHelperTest, TestItThrowsApiExceptionIfServerError) - { - CurlResponse response(R"({"message": "teapots"})", false, 500); - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(GetApiCurlRequest("/authorise", CurlRequest::METHOD_GET))) - .Times(1) - .WillOnce(Return(response)); - - EXPECT_THROW(static_cast(this->helper.CheckApiAuthorisation()), ApiException); - } - - TEST_F(ApiHelperTest, ItThrowsAnExceptionIfBadRequest) - { - CurlResponse response(R"({"message": "teapots"})", false, 401); - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(GetApiCurlRequest("/authorise", CurlRequest::METHOD_GET))) - .Times(1) - .WillOnce(Return(response)); - - EXPECT_THROW(static_cast(this->helper.CheckApiAuthorisation()), ApiException); - } - - TEST_F(ApiHelperTest, ItThrowsAnExceptionIfUnknownResponseCode) - { - CurlResponse response(R"({"message": "teapots"})", false, 666); - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(GetApiCurlRequest("/authorise", CurlRequest::METHOD_GET))) - .Times(1) - .WillOnce(Return(response)); - - EXPECT_THROW(static_cast(this->helper.CheckApiAuthorisation()), ApiException); - } - - TEST_F(ApiHelperTest, FetchRemoteFileReturnsFileString) - { - nlohmann::json responseJson; - responseJson["test"] = "hi!"; - CurlResponse response(responseJson.dump(), false, 200); - - EXPECT_CALL( - this->mockCurlApi, MakeCurlRequest(CurlRequest("http://test.com/averynicefile", CurlRequest::METHOD_GET))) - .Times(1) - .WillOnce(Return(response)); - - EXPECT_TRUE(responseJson.dump() == this->helper.FetchRemoteFile("http://test.com/averynicefile")); - } - - TEST_F(ApiHelperTest, GetSquawkAssignmentHandlesNonJsonResponse) - { - CurlResponse response("here is some html that means something went wrong", false, 200); - - EXPECT_CALL( - this->mockCurlApi, MakeCurlRequest(GetApiCurlRequest("/squawk-assignment/BAW123", CurlRequest::METHOD_GET))) - .Times(1) - .WillOnce(Return(response)); - - EXPECT_THROW(static_cast(this->helper.GetAssignedSquawk("BAW123")), ApiException); - } - - TEST_F(ApiHelperTest, GetSquawkAssignmentReturnsSquawkAllocation) - { - CurlResponse response(R"({"squawk": "1234"})", false, 200); - - EXPECT_CALL( - this->mockCurlApi, MakeCurlRequest(GetApiCurlRequest("/squawk-assignment/BAW123", CurlRequest::METHOD_GET))) - .Times(1) - .WillOnce(Return(response)); - - ApiSquawkAllocation allocation = this->helper.GetAssignedSquawk("BAW123"); - EXPECT_TRUE("1234" == allocation.squawk); - EXPECT_TRUE("BAW123" == allocation.callsign); - } - - TEST_F(ApiHelperTest, GetSquawkAssignmentThrowsExceptionSquawkNotAllowed) - { - CurlResponse response(R"({"squawk": "7500"})", false, 200); - - EXPECT_CALL( - this->mockCurlApi, MakeCurlRequest(GetApiCurlRequest("/squawk-assignment/BAW123", CurlRequest::METHOD_GET))) - .Times(1) - .WillOnce(Return(response)); - - EXPECT_THROW(static_cast(this->helper.GetAssignedSquawk("BAW123")), ApiException); - } - - TEST_F(ApiHelperTest, GetSquawkAssignmentThrowsExceptionSquawkNotValid) - { - CurlResponse response(R"({"squawk": "abcd"})", false, 200); - - EXPECT_CALL( - this->mockCurlApi, MakeCurlRequest(GetApiCurlRequest("/squawk-assignment/BAW123", CurlRequest::METHOD_GET))) - .Times(1) - .WillOnce(Return(response)); - - EXPECT_THROW(static_cast(this->helper.GetAssignedSquawk("BAW123")), ApiException); - } - - TEST_F(ApiHelperTest, CreateGeneralSquawkAssignmentReturnsSquawk) - { - CurlResponse response(R"({"squawk": "1234"})", false, 200); - nlohmann::json requestBody; - requestBody["type"] = "general"; - requestBody["origin"] = "EGKK"; - requestBody["destination"] = "EGCC"; - - EXPECT_CALL( - this->mockCurlApi, - MakeCurlRequest(GetApiCurlRequest("/squawk-assignment/BAW123", CurlRequest::METHOD_PUT, requestBody))) - .Times(1) - .WillOnce(Return(response)); - - ApiSquawkAllocation allocation = this->helper.CreateGeneralSquawkAssignment("BAW123", "EGKK", "EGCC"); - EXPECT_TRUE("1234" == allocation.squawk); - EXPECT_TRUE("BAW123" == allocation.callsign); - } - - TEST_F(ApiHelperTest, CreateGeneralThrowsExceptionIfSquawkNotAllowed) - { - CurlResponse response(R"({"squawk": "7500"})", false, 200); - nlohmann::json requestBody; - requestBody["type"] = "general"; - requestBody["origin"] = "EGKK"; - requestBody["destination"] = "EGCC"; - - EXPECT_CALL( - this->mockCurlApi, - MakeCurlRequest(GetApiCurlRequest("/squawk-assignment/BAW123", CurlRequest::METHOD_PUT, requestBody))) - .Times(1) - .WillOnce(Return(response)); - - EXPECT_THROW( - static_cast(this->helper.CreateGeneralSquawkAssignment("BAW123", "EGKK", "EGCC")), ApiException); - } - - TEST_F(ApiHelperTest, CreateGeneralThrowsExceptionIfNoSquawkInResponse) - { - CurlResponse response("{}", false, 200); - nlohmann::json requestBody; - requestBody["type"] = "general"; - requestBody["origin"] = "EGKK"; - requestBody["destination"] = "EGCC"; - - EXPECT_CALL( - this->mockCurlApi, - MakeCurlRequest(GetApiCurlRequest("/squawk-assignment/BAW123", CurlRequest::METHOD_PUT, requestBody))) - .Times(1) - .WillOnce(Return(response)); - - EXPECT_THROW( - static_cast(this->helper.CreateGeneralSquawkAssignment("BAW123", "EGKK", "EGCC")), ApiException); - } - - TEST_F(ApiHelperTest, CreateGeneralThrowsExceptionIfSquawkInvalid) - { - CurlResponse response(R"({"squawk": "abcd"})", false, 200); - nlohmann::json requestBody; - requestBody["type"] = "general"; - requestBody["origin"] = "EGKK"; - requestBody["destination"] = "EGCC"; - - EXPECT_CALL( - this->mockCurlApi, - MakeCurlRequest(GetApiCurlRequest("/squawk-assignment/BAW123", CurlRequest::METHOD_PUT, requestBody))) - .Times(1) - .WillOnce(Return(response)); - - EXPECT_THROW( - static_cast(this->helper.CreateGeneralSquawkAssignment("BAW123", "EGKK", "EGCC")), ApiException); - } - - TEST_F(ApiHelperTest, CreateLocalSquawkAssignmentReturnsSquawk) - { - CurlResponse response(R"({"squawk": "1234"})", false, 200); - - nlohmann::json requestBody; - requestBody["type"] = "local"; - requestBody["rules"] = "V"; - requestBody["unit"] = "EGCC"; - - EXPECT_CALL( - this->mockCurlApi, - MakeCurlRequest(GetApiCurlRequest("/squawk-assignment/BAW123", CurlRequest::METHOD_PUT, requestBody))) - .Times(1) - .WillOnce(Return(response)); - - ApiSquawkAllocation allocation = this->helper.CreateLocalSquawkAssignment("BAW123", "EGCC", "V"); - EXPECT_TRUE("1234" == allocation.squawk); - EXPECT_TRUE("BAW123" == allocation.callsign); - } - - TEST_F(ApiHelperTest, CreateLocalSquawkThrowsExceptionIfSquawkNotAllowed) - { - CurlResponse response(R"({"squawk": "7700"})", false, 200); - - nlohmann::json requestBody; - requestBody["type"] = "local"; - requestBody["rules"] = "V"; - requestBody["unit"] = "EGCC"; - - EXPECT_CALL( - this->mockCurlApi, - MakeCurlRequest(GetApiCurlRequest("/squawk-assignment/BAW123", CurlRequest::METHOD_PUT, requestBody))) - .Times(1) - .WillOnce(Return(response)); - - EXPECT_THROW(static_cast(this->helper.CreateLocalSquawkAssignment("BAW123", "EGCC", "V")), ApiException); - } - - TEST_F(ApiHelperTest, CreateLocalSquawkThrowsExceptionIfSquawkInvalid) - { - CurlResponse response(R"({"squawk": "abcd"})", false, 200); - - nlohmann::json requestBody; - requestBody["type"] = "local"; - requestBody["rules"] = "V"; - requestBody["unit"] = "EGCC"; - - EXPECT_CALL( - this->mockCurlApi, - MakeCurlRequest(GetApiCurlRequest("/squawk-assignment/BAW123", CurlRequest::METHOD_PUT, requestBody))) - .Times(1) - .WillOnce(Return(response)); - - EXPECT_THROW(static_cast(this->helper.CreateLocalSquawkAssignment("BAW123", "EGCC", "V")), ApiException); - } - - TEST_F(ApiHelperTest, DeleteSquawkAssignmentIsCalledCorrectly) - { - CurlResponse response(R"({"squawk": "1234"})", false, 204); - - CurlRequest expectedRequest(GetApiCurlRequest("/squawk-assignment/BAW123", CurlRequest::METHOD_DELETE)); - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); - - EXPECT_NO_THROW(this->helper.DeleteSquawkAssignment("BAW123")); - } - - TEST_F(ApiHelperTest, GetHoldDependencyReturnsJsonData) - { - nlohmann::json data; - data["foo"] = "bar"; - data["big"] = "small"; - - CurlResponse response(data.dump(), false, 200); - CurlRequest expectedRequest(GetApiCurlRequest("/hold", CurlRequest::METHOD_GET)); - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); - - EXPECT_EQ(data, this->helper.GetHoldDependency()); - } - - TEST_F(ApiHelperTest, ItHasAUrlToSendTo) - { - EXPECT_TRUE(this->helper.GetApiDomain() == mockApiUrl); - } - - TEST_F(ApiHelperTest, ItHasAnApiKeyToUseForAuthentication) - { - EXPECT_TRUE(this->helper.GetApiKey() == mockApiKey); - } - - TEST_F(ApiHelperTest, ItCanUpdateTheUrl) - { - this->helper.SetApiDomain("https://nottheurl"); - EXPECT_TRUE(this->helper.GetApiDomain() == "https://nottheurl"); - } - - TEST_F(ApiHelperTest, ItCanUpdateTheKey) - { - this->helper.SetApiKey("notthekey"); - EXPECT_TRUE(this->helper.GetApiKey() == "notthekey"); - } - - TEST_F(ApiHelperTest, GetMinStackLevelsReturnsMinStackData) - { - nlohmann::json responseData; - responseData["bla"] = "bla"; - CurlResponse response(responseData.dump(), false, 200); - - CurlRequest expectedRequest(GetApiCurlRequest("/msl", CurlRequest::METHOD_GET)); - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); - - EXPECT_EQ(responseData, this->helper.GetMinStackLevels()); - } - - TEST_F(ApiHelperTest, GetMinStackLevelsReturnsRegionalPressureData) - { - nlohmann::json responseData; - responseData["bla"] = "bla"; - CurlResponse response(responseData.dump(), false, 200); - - CurlRequest expectedRequest(GetApiCurlRequest("/regional-pressure", CurlRequest::METHOD_GET)); - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); - - EXPECT_EQ(responseData, this->helper.GetRegionalPressures()); - } - - TEST_F(ApiHelperTest, GetUriReturnsUriData) - { - nlohmann::json responseData; - responseData["bla"] = "bla"; - CurlResponse response(responseData.dump(), false, 200); - - CurlRequest expectedRequest(GetApiGetUriCurlRequest("http://ukcp.test.com/someuri", CurlRequest::METHOD_GET)); - expectedRequest.SetMaxRequestTime(0L); - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); - - EXPECT_EQ(responseData, this->helper.GetUri("http://ukcp.test.com/someuri")); - } - - TEST_F(ApiHelperTest, GetUriThrowsExceptionIfNonUkcpRoute) - { - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(_)).Times(0); - - EXPECT_THROW(static_cast(this->helper.GetUri("http://ukcp.test.org/someuri")), ApiException); - } - - TEST_F(ApiHelperTest, SearchSrdReturnsData) - { - nlohmann::json responseData; - responseData["bla"] = "bla"; - CurlResponse response(responseData.dump(), false, 200); - - SrdSearchParameters params; - params.origin = "EGLL"; - params.destination = "EGGD"; - params.requestedLevel = 10000; - - CurlRequest expectedRequest(GetApiGetUriCurlRequest( - "http://ukcp.test.com/srd/route/search?origin=EGLL&destination=EGGD&requestedLevel=10000", - CurlRequest::METHOD_GET)); - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); - - EXPECT_EQ(responseData, this->helper.SearchSrd(params)); - } - - TEST_F(ApiHelperTest, GetAssignedHoldsReturnsData) - { - nlohmann::json responseData; - responseData["bla"] = "bla"; - CurlResponse response(responseData.dump(), false, 200); - - CurlRequest expectedRequest( - GetApiGetUriCurlRequest("http://ukcp.test.com/hold/assigned", CurlRequest::METHOD_GET)); - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); - - EXPECT_EQ(responseData, this->helper.GetAssignedHolds()); - } - - TEST_F(ApiHelperTest, AssignAircraftToHoldGeneratesRequest) - { - nlohmann::json responseData; - responseData["bla"] = "bla"; - CurlResponse response(responseData.dump(), false, 200); - - nlohmann::json requestBody; - requestBody["callsign"] = "BAW123"; - requestBody["navaid"] = "TIMBA"; - - CurlRequest expectedRequest(GetApiCurlRequest("/hold/assigned", CurlRequest::METHOD_PUT, requestBody)); - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); - - EXPECT_NO_THROW(this->helper.AssignAircraftToHold("BAW123", "TIMBA")); - } - - TEST_F(ApiHelperTest, UnassignAircraftHoldGeneratesRequest) - { - nlohmann::json responseData; - responseData["bla"] = "bla"; - CurlResponse response(responseData.dump(), false, 200); - - CurlRequest expectedRequest(GetApiCurlRequest("/hold/assigned/BAW123", CurlRequest::METHOD_DELETE)); - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); - - EXPECT_NO_THROW(this->helper.UnassignAircraftHold("BAW123")); - } - - TEST_F(ApiHelperTest, EnrouteReleaseGeneratesRequest) - { - nlohmann::json responseData; - responseData["bla"] = "bla"; - CurlResponse response(responseData.dump(), false, 200); - - CurlRequest expectedRequest = GetApiCurlRequest( - "/release/enroute", - CurlRequest::METHOD_POST, - { - {"callsign", "BAW123"}, - {"type", 1}, - {"initiating_controller", "LON_S_CTR"}, - {"target_controller", "LON_C_CTR"}, - }); - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); - - EXPECT_NO_THROW(this->helper.SendEnrouteRelease("BAW123", "LON_S_CTR", "LON_C_CTR", 1)); - } - - TEST_F(ApiHelperTest, EnrouteReleaseGeneratesRequestWithReleasePoint) - { - nlohmann::json responseData; - responseData["bla"] = "bla"; - CurlResponse response(responseData.dump(), false, 200); - - CurlRequest expectedRequest = GetApiCurlRequest( - "/release/enroute", - CurlRequest::METHOD_POST, - {{"callsign", "BAW123"}, - {"type", 1}, - {"initiating_controller", "LON_S_CTR"}, - {"target_controller", "LON_C_CTR"}, - {"release_point", "LAM"}}); - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); - - EXPECT_NO_THROW(this->helper.SendEnrouteReleaseWithReleasePoint("BAW123", "LON_S_CTR", "LON_C_CTR", 1, "LAM")); - } - - TEST_F(ApiHelperTest, GetAssignedStandsReturnsData) - { - nlohmann::json responseData; - responseData["bla"] = "bla"; - CurlResponse response(responseData.dump(), false, 200); - - CurlRequest expectedRequest( - GetApiGetUriCurlRequest("http://ukcp.test.com/stand/assignment", CurlRequest::METHOD_GET)); - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); - - EXPECT_EQ(responseData, this->helper.GetAssignedStands()); - } - - TEST_F(ApiHelperTest, AssignStandToAircraftGeneratesRequest) - { - nlohmann::json responseData; - responseData["bla"] = "bla"; - CurlResponse response(responseData.dump(), false, 200); - - nlohmann::json requestBody; - requestBody["callsign"] = "BAW123"; - requestBody["stand_id"] = 1; - - CurlRequest expectedRequest(GetApiCurlRequest("/stand/assignment", CurlRequest::METHOD_PUT, requestBody)); - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); - - EXPECT_NO_THROW(this->helper.AssignStandToAircraft("BAW123", 1)); - } - - TEST_F(ApiHelperTest, DeleteStandAssignmentForAircraftGeneratesRequest) - { - nlohmann::json responseData; - responseData["bla"] = "bla"; - CurlResponse response(responseData.dump(), false, 200); - - CurlRequest expectedRequest(GetApiCurlRequest("/stand/assignment/BAW123", CurlRequest::METHOD_DELETE)); - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); - - EXPECT_NO_THROW(this->helper.DeleteStandAssignmentForAircraft("BAW123")); - } - - TEST_F(ApiHelperTest, GetAllNotificationsMakesRequest) - { - nlohmann::json responseData; - responseData["bla"] = "bla"; - CurlResponse response(responseData.dump(), false, 200); - - CurlRequest expectedRequest(GetApiCurlRequest("/notifications", CurlRequest::METHOD_GET)); - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); - - EXPECT_EQ(responseData, this->helper.GetAllNotifications()); - } - - TEST_F(ApiHelperTest, GetUnreadNotificationsMakesRequest) - { - nlohmann::json responseData; - responseData["bla"] = "bla"; - CurlResponse response(responseData.dump(), false, 200); - - CurlRequest expectedRequest(GetApiCurlRequest("/notifications/unread", CurlRequest::METHOD_GET)); - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); - - EXPECT_EQ(responseData, this->helper.GetUnreadNotifications()); - } - - TEST_F(ApiHelperTest, ReadNotificationMakesRequest) - { - nlohmann::json responseData; - responseData["bla"] = "bla"; - CurlResponse response(responseData.dump(), false, 200); - - CurlRequest expectedRequest(GetApiCurlRequest("/notifications/read/1", CurlRequest::METHOD_PUT)); - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); - - EXPECT_NO_THROW(this->helper.ReadNotification(1)); - } - - TEST_F(ApiHelperTest, SyncPluginEventsMakesRequest) - { - nlohmann::json responseData; - responseData["bla"] = "bla"; - CurlResponse response(responseData.dump(), false, 200); - - CurlRequest expectedRequest(GetApiCurlRequest("/plugin-events/sync", CurlRequest::METHOD_GET)); - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); - - EXPECT_EQ(responseData, this->helper.SyncPluginEvents()); - } - - TEST_F(ApiHelperTest, GetLatestPluginEventsMakesRequest) - { - nlohmann::json responseData; - responseData["bla"] = "bla"; - CurlResponse response(responseData.dump(), false, 200); - - CurlRequest expectedRequest(GetApiCurlRequest("/plugin-events/recent?previous=5", CurlRequest::METHOD_GET)); - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); - - EXPECT_EQ(responseData, this->helper.GetLatestPluginEvents(5)); - } - - TEST_F(ApiHelperTest, GetUpdateDetailsReturnsData) - { - nlohmann::json responseData; - responseData["bla"] = "bla"; - CurlResponse response(responseData.dump(), false, 200); - - CurlRequest expectedRequest( - GetApiGetUriCurlRequest("http://ukcp.test.com/version/latest", CurlRequest::METHOD_GET)); - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); - - EXPECT_EQ(responseData, this->helper.GetUpdateDetails()); - } - - TEST_F(ApiHelperTest, RequestDepartureReleaseMakesRequest) - { - nlohmann::json responseData; - responseData["bla"] = "bla"; - CurlResponse response(responseData.dump(), false, 200); - - nlohmann::json expectedData; - expectedData["callsign"] = "BAW123"; - expectedData["requesting_controller_id"] = 1; - expectedData["target_controller_id"] = 3; - expectedData["expires_in_seconds"] = 54; - - CurlRequest expectedRequest( - GetApiCurlRequest("/departure/release/request", CurlRequest::METHOD_POST, expectedData)); - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); - - EXPECT_EQ(responseData, this->helper.RequestDepartureRelease("BAW123", 1, 3, 54)); - } - - TEST_F(ApiHelperTest, ApproveDepartureReleaseMakesRequest) - { - nlohmann::json responseData; - responseData["bla"] = "bla"; - CurlResponse response(responseData.dump(), false, 200); - - nlohmann::json expectedData; - expectedData["controller_position_id"] = 2; - expectedData["released_at"] = "2021-05-09 12:31:00"; - expectedData["expires_in_seconds"] = 120; - - std::chrono::system_clock::time_point timePoint; - std::istringstream inputStream("2021-05-09 12:31:00"); - inputStream >> date::parse("%Y-%m-%d %H:%M:%S", timePoint); - - CurlRequest expectedRequest( - GetApiCurlRequest("/departure/release/request/1/approve", CurlRequest::METHOD_PATCH, expectedData)); - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); - - this->helper.ApproveDepartureReleaseRequest(1, 2, timePoint, 120); - } - - TEST_F(ApiHelperTest, RejectDepartureReleaseMakesRequest) - { - nlohmann::json responseData; - responseData["bla"] = "bla"; - CurlResponse response(responseData.dump(), false, 200); - - nlohmann::json expectedData; - expectedData["controller_position_id"] = 2; - - CurlRequest expectedRequest( - GetApiCurlRequest("/departure/release/request/1/reject", CurlRequest::METHOD_PATCH, expectedData)); - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); - - this->helper.RejectDepartureReleaseRequest(1, 2); - } - - TEST_F(ApiHelperTest, AcknowledgeDepartureReleaseMakesRequest) - { - nlohmann::json responseData; - responseData["bla"] = "bla"; - CurlResponse response(responseData.dump(), false, 200); - - nlohmann::json expectedData; - expectedData["controller_position_id"] = 2; - - CurlRequest expectedRequest( - GetApiCurlRequest("/departure/release/request/1/acknowledge", CurlRequest::METHOD_PATCH, expectedData)); - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); - - this->helper.AcknowledgeDepartureReleaseRequest(1, 2); - } - - TEST_F(ApiHelperTest, CancelDepartureReleaseMakesRequest) - { - nlohmann::json responseData; - responseData["bla"] = "bla"; - CurlResponse response(responseData.dump(), false, 200); - - CurlRequest expectedRequest(GetApiCurlRequest("/departure/release/request/1", CurlRequest::METHOD_DELETE)); - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); - - this->helper.CancelDepartureReleaseRequest(1); - } - - TEST_F(ApiHelperTest, CreatePrenoteMessageRequestMakesRequest) - { - nlohmann::json responseData; - responseData["bla"] = "bla"; - CurlResponse response(responseData.dump(), false, 200); - - nlohmann::json expectedData; - expectedData["callsign"] = "BAW123"; - expectedData["departure_airfield"] = "EGGD"; - expectedData["departure_sid"] = "BADIM1X"; - expectedData["destination_airfield"] = "EGLC"; - expectedData["requesting_controller_id"] = 1; - expectedData["target_controller_id"] = 2; - expectedData["expires_in_seconds"] = 55; - - CurlRequest expectedRequest(GetApiCurlRequest("/prenotes/messages", CurlRequest::METHOD_POST, expectedData)); - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); - - EXPECT_EQ(responseData, this->helper.CreatePrenoteMessage("BAW123", "EGGD", "BADIM1X", "EGLC", 1, 2, 55)); - } - - TEST_F(ApiHelperTest, AcknowledgePrenoteMessageMakesRequest) - { - nlohmann::json responseData; - responseData["bla"] = "bla"; - CurlResponse response(responseData.dump(), false, 200); - - nlohmann::json expectedData; - expectedData["controller_position_id"] = 2; - - CurlRequest expectedRequest( - GetApiCurlRequest("/prenotes/messages/55/acknowledge", CurlRequest::METHOD_PATCH, expectedData)); - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); - - this->helper.AcknowledgePrenoteMessage(55, 2); - } - - TEST_F(ApiHelperTest, DeletePrenoteMessageMakesRequest) - { - nlohmann::json responseData; - responseData["bla"] = "bla"; - CurlResponse response(responseData.dump(), false, 200); - - CurlRequest expectedRequest(GetApiCurlRequest("/prenotes/messages/55", CurlRequest::METHOD_DELETE)); - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); - - this->helper.DeletePrenoteMessage(55); - } - - TEST_F(ApiHelperTest, CreateMissedApproachMakesRequest) - { - nlohmann::json responseData; - responseData["bla"] = "bla"; - CurlResponse response(responseData.dump(), false, 200); - - nlohmann::json expectedData; - expectedData["callsign"] = "BAW123"; - - CurlRequest expectedRequest(GetApiCurlRequest("/missed-approaches", CurlRequest::METHOD_POST, expectedData)); - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); - - EXPECT_EQ(responseData, this->helper.CreateMissedApproach("BAW123")); - } - - TEST_F(ApiHelperTest, AcknowledgeMissedApproachMakesRequest) - { - nlohmann::json responseData; - responseData["bla"] = "bla"; - CurlResponse response(responseData.dump(), false, 200); - - nlohmann::json expectedData; - expectedData["remarks"] = "Some remarks"; - - CurlRequest expectedRequest(GetApiCurlRequest("/missed-approaches/1", CurlRequest::METHOD_PATCH, expectedData)); - - EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); - - this->helper.AcknowledgeMissedApproach(1, "Some remarks"); - } -} // namespace UKControllerPluginUtilsTest::Api +#include "api/ApiException.h" +#include "api/ApiHelper.h" +#include "api/ApiNotAuthorisedException.h" +#include "api/ApiNotFoundException.h" +#include "curl/CurlInterface.h" +#include "helper/ApiRequestHelperFunctions.h" + +using ::testing::_; +using ::testing::NiceMock; +using ::testing::Return; +using ::testing::Test; +using UKControllerPlugin::Api::ApiException; +using UKControllerPlugin::Api::ApiHelper; +using UKControllerPlugin::Api::ApiNotAuthorisedException; +using UKControllerPlugin::Api::ApiNotFoundException; +using UKControllerPlugin::Api::ApiRequestBuilder; +using UKControllerPlugin::Api::ApiResponse; +using UKControllerPlugin::Curl::CurlInterface; +using UKControllerPlugin::Curl::CurlRequest; +using UKControllerPlugin::Curl::CurlResponse; +using UKControllerPlugin::Squawk::ApiSquawkAllocation; +using UKControllerPlugin::Srd::SrdSearchParameters; +using UKControllerPluginTest::Curl::MockCurlApi; + +namespace UKControllerPluginUtilsTest::Api { + + class ApiHelperTest : public Test + { + public: + ApiHelperTest() : helper(mockCurlApi, GetApiRequestBuilder()) + { + } + + ApiHelper helper; + NiceMock mockCurlApi; + }; + + TEST_F(ApiHelperTest, TestItReturnsApiAuthorisedIf200) + { + CurlResponse response(R"({"message": "teapots"})", false, 200); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(GetApiCurlRequest("/authorise", CurlRequest::METHOD_GET))) + .Times(1) + .WillOnce(Return(response)); + + EXPECT_TRUE(this->helper.CheckApiAuthorisation()); + } + + TEST_F(ApiHelperTest, TestItReturnsNotApiAuthorisedIfNot200) + { + CurlResponse response(R"({"message": "teapots"})", false, 201); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(GetApiCurlRequest("/authorise", CurlRequest::METHOD_GET))) + .Times(1) + .WillOnce(Return(response)); + + EXPECT_FALSE(this->helper.CheckApiAuthorisation()); + } + + TEST_F(ApiHelperTest, TestItThrowsNotFoundExceptionIf404) + { + CurlResponse response(R"({"message": "teapots"})", false, 404); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(GetApiCurlRequest("/authorise", CurlRequest::METHOD_GET))) + .Times(1) + .WillOnce(Return(response)); + + EXPECT_THROW(static_cast(this->helper.CheckApiAuthorisation()), ApiNotFoundException); + } + + TEST_F(ApiHelperTest, TestItThrowsNotAuthorisedExceptionIf401) + { + CurlResponse response(R"({"message": "teapots"})", false, 401); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(GetApiCurlRequest("/authorise", CurlRequest::METHOD_GET))) + .Times(1) + .WillOnce(Return(response)); + + EXPECT_THROW(static_cast(this->helper.CheckApiAuthorisation()), ApiNotAuthorisedException); + } + + TEST_F(ApiHelperTest, TestItThrowsNotAuthorisedExceptionIf403) + { + CurlResponse response(R"({"message": "teapots"})", false, 403); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(GetApiCurlRequest("/authorise", CurlRequest::METHOD_GET))) + .Times(1) + .WillOnce(Return(response)); + + EXPECT_THROW(static_cast(this->helper.CheckApiAuthorisation()), ApiNotAuthorisedException); + } + + TEST_F(ApiHelperTest, TestItThrowsApiExceptionIfServiceUnavailable) + { + CurlResponse response(R"({"message": "teapots"})", false, 503); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(GetApiCurlRequest("/authorise", CurlRequest::METHOD_GET))) + .Times(1) + .WillOnce(Return(response)); + + EXPECT_THROW(static_cast(this->helper.CheckApiAuthorisation()), ApiException); + } + + TEST_F(ApiHelperTest, TestItThrowsApiExceptionIfBadGateway) + { + CurlResponse response(R"({"message": "teapots"})", false, 502); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(GetApiCurlRequest("/authorise", CurlRequest::METHOD_GET))) + .Times(1) + .WillOnce(Return(response)); + + EXPECT_THROW(static_cast(this->helper.CheckApiAuthorisation()), ApiException); + } + + TEST_F(ApiHelperTest, TestItThrowsApiExceptionIfServerError) + { + CurlResponse response(R"({"message": "teapots"})", false, 500); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(GetApiCurlRequest("/authorise", CurlRequest::METHOD_GET))) + .Times(1) + .WillOnce(Return(response)); + + EXPECT_THROW(static_cast(this->helper.CheckApiAuthorisation()), ApiException); + } + + TEST_F(ApiHelperTest, ItThrowsAnExceptionIfBadRequest) + { + CurlResponse response(R"({"message": "teapots"})", false, 401); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(GetApiCurlRequest("/authorise", CurlRequest::METHOD_GET))) + .Times(1) + .WillOnce(Return(response)); + + EXPECT_THROW(static_cast(this->helper.CheckApiAuthorisation()), ApiException); + } + + TEST_F(ApiHelperTest, ItThrowsAnExceptionIfUnknownResponseCode) + { + CurlResponse response(R"({"message": "teapots"})", false, 666); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(GetApiCurlRequest("/authorise", CurlRequest::METHOD_GET))) + .Times(1) + .WillOnce(Return(response)); + + EXPECT_THROW(static_cast(this->helper.CheckApiAuthorisation()), ApiException); + } + + TEST_F(ApiHelperTest, FetchRemoteFileReturnsFileString) + { + nlohmann::json responseJson; + responseJson["test"] = "hi!"; + CurlResponse response(responseJson.dump(), false, 200); + + EXPECT_CALL( + this->mockCurlApi, MakeCurlRequest(CurlRequest("http://test.com/averynicefile", CurlRequest::METHOD_GET))) + .Times(1) + .WillOnce(Return(response)); + + EXPECT_TRUE(responseJson.dump() == this->helper.FetchRemoteFile("http://test.com/averynicefile")); + } + + TEST_F(ApiHelperTest, GetSquawkAssignmentHandlesNonJsonResponse) + { + CurlResponse response("here is some html that means something went wrong", false, 200); + + EXPECT_CALL( + this->mockCurlApi, MakeCurlRequest(GetApiCurlRequest("/squawk-assignment/BAW123", CurlRequest::METHOD_GET))) + .Times(1) + .WillOnce(Return(response)); + + EXPECT_THROW(static_cast(this->helper.GetAssignedSquawk("BAW123")), ApiException); + } + + TEST_F(ApiHelperTest, GetSquawkAssignmentReturnsSquawkAllocation) + { + CurlResponse response(R"({"squawk": "1234"})", false, 200); + + EXPECT_CALL( + this->mockCurlApi, MakeCurlRequest(GetApiCurlRequest("/squawk-assignment/BAW123", CurlRequest::METHOD_GET))) + .Times(1) + .WillOnce(Return(response)); + + ApiSquawkAllocation allocation = this->helper.GetAssignedSquawk("BAW123"); + EXPECT_TRUE("1234" == allocation.squawk); + EXPECT_TRUE("BAW123" == allocation.callsign); + } + + TEST_F(ApiHelperTest, GetSquawkAssignmentThrowsExceptionSquawkNotAllowed) + { + CurlResponse response(R"({"squawk": "7500"})", false, 200); + + EXPECT_CALL( + this->mockCurlApi, MakeCurlRequest(GetApiCurlRequest("/squawk-assignment/BAW123", CurlRequest::METHOD_GET))) + .Times(1) + .WillOnce(Return(response)); + + EXPECT_THROW(static_cast(this->helper.GetAssignedSquawk("BAW123")), ApiException); + } + + TEST_F(ApiHelperTest, GetSquawkAssignmentThrowsExceptionSquawkNotValid) + { + CurlResponse response(R"({"squawk": "abcd"})", false, 200); + + EXPECT_CALL( + this->mockCurlApi, MakeCurlRequest(GetApiCurlRequest("/squawk-assignment/BAW123", CurlRequest::METHOD_GET))) + .Times(1) + .WillOnce(Return(response)); + + EXPECT_THROW(static_cast(this->helper.GetAssignedSquawk("BAW123")), ApiException); + } + + TEST_F(ApiHelperTest, CreateGeneralSquawkAssignmentReturnsSquawk) + { + CurlResponse response(R"({"squawk": "1234"})", false, 200); + nlohmann::json requestBody; + requestBody["type"] = "general"; + requestBody["origin"] = "EGKK"; + requestBody["destination"] = "EGCC"; + + EXPECT_CALL( + this->mockCurlApi, + MakeCurlRequest(GetApiCurlRequest("/squawk-assignment/BAW123", CurlRequest::METHOD_PUT, requestBody))) + .Times(1) + .WillOnce(Return(response)); + + ApiSquawkAllocation allocation = this->helper.CreateGeneralSquawkAssignment("BAW123", "EGKK", "EGCC"); + EXPECT_TRUE("1234" == allocation.squawk); + EXPECT_TRUE("BAW123" == allocation.callsign); + } + + TEST_F(ApiHelperTest, CreateGeneralThrowsExceptionIfSquawkNotAllowed) + { + CurlResponse response(R"({"squawk": "7500"})", false, 200); + nlohmann::json requestBody; + requestBody["type"] = "general"; + requestBody["origin"] = "EGKK"; + requestBody["destination"] = "EGCC"; + + EXPECT_CALL( + this->mockCurlApi, + MakeCurlRequest(GetApiCurlRequest("/squawk-assignment/BAW123", CurlRequest::METHOD_PUT, requestBody))) + .Times(1) + .WillOnce(Return(response)); + + EXPECT_THROW( + static_cast(this->helper.CreateGeneralSquawkAssignment("BAW123", "EGKK", "EGCC")), ApiException); + } + + TEST_F(ApiHelperTest, CreateGeneralThrowsExceptionIfNoSquawkInResponse) + { + CurlResponse response("{}", false, 200); + nlohmann::json requestBody; + requestBody["type"] = "general"; + requestBody["origin"] = "EGKK"; + requestBody["destination"] = "EGCC"; + + EXPECT_CALL( + this->mockCurlApi, + MakeCurlRequest(GetApiCurlRequest("/squawk-assignment/BAW123", CurlRequest::METHOD_PUT, requestBody))) + .Times(1) + .WillOnce(Return(response)); + + EXPECT_THROW( + static_cast(this->helper.CreateGeneralSquawkAssignment("BAW123", "EGKK", "EGCC")), ApiException); + } + + TEST_F(ApiHelperTest, CreateGeneralThrowsExceptionIfSquawkInvalid) + { + CurlResponse response(R"({"squawk": "abcd"})", false, 200); + nlohmann::json requestBody; + requestBody["type"] = "general"; + requestBody["origin"] = "EGKK"; + requestBody["destination"] = "EGCC"; + + EXPECT_CALL( + this->mockCurlApi, + MakeCurlRequest(GetApiCurlRequest("/squawk-assignment/BAW123", CurlRequest::METHOD_PUT, requestBody))) + .Times(1) + .WillOnce(Return(response)); + + EXPECT_THROW( + static_cast(this->helper.CreateGeneralSquawkAssignment("BAW123", "EGKK", "EGCC")), ApiException); + } + + TEST_F(ApiHelperTest, CreateLocalSquawkAssignmentReturnsSquawk) + { + CurlResponse response(R"({"squawk": "1234"})", false, 200); + + nlohmann::json requestBody; + requestBody["type"] = "local"; + requestBody["rules"] = "V"; + requestBody["unit"] = "EGCC"; + + EXPECT_CALL( + this->mockCurlApi, + MakeCurlRequest(GetApiCurlRequest("/squawk-assignment/BAW123", CurlRequest::METHOD_PUT, requestBody))) + .Times(1) + .WillOnce(Return(response)); + + ApiSquawkAllocation allocation = this->helper.CreateLocalSquawkAssignment("BAW123", "EGCC", "V"); + EXPECT_TRUE("1234" == allocation.squawk); + EXPECT_TRUE("BAW123" == allocation.callsign); + } + + TEST_F(ApiHelperTest, CreateLocalSquawkThrowsExceptionIfSquawkNotAllowed) + { + CurlResponse response(R"({"squawk": "7700"})", false, 200); + + nlohmann::json requestBody; + requestBody["type"] = "local"; + requestBody["rules"] = "V"; + requestBody["unit"] = "EGCC"; + + EXPECT_CALL( + this->mockCurlApi, + MakeCurlRequest(GetApiCurlRequest("/squawk-assignment/BAW123", CurlRequest::METHOD_PUT, requestBody))) + .Times(1) + .WillOnce(Return(response)); + + EXPECT_THROW(static_cast(this->helper.CreateLocalSquawkAssignment("BAW123", "EGCC", "V")), ApiException); + } + + TEST_F(ApiHelperTest, CreateLocalSquawkThrowsExceptionIfSquawkInvalid) + { + CurlResponse response(R"({"squawk": "abcd"})", false, 200); + + nlohmann::json requestBody; + requestBody["type"] = "local"; + requestBody["rules"] = "V"; + requestBody["unit"] = "EGCC"; + + EXPECT_CALL( + this->mockCurlApi, + MakeCurlRequest(GetApiCurlRequest("/squawk-assignment/BAW123", CurlRequest::METHOD_PUT, requestBody))) + .Times(1) + .WillOnce(Return(response)); + + EXPECT_THROW(static_cast(this->helper.CreateLocalSquawkAssignment("BAW123", "EGCC", "V")), ApiException); + } + + TEST_F(ApiHelperTest, DeleteSquawkAssignmentIsCalledCorrectly) + { + CurlResponse response(R"({"squawk": "1234"})", false, 204); + + CurlRequest expectedRequest(GetApiCurlRequest("/squawk-assignment/BAW123", CurlRequest::METHOD_DELETE)); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); + + EXPECT_NO_THROW(this->helper.DeleteSquawkAssignment("BAW123")); + } + + TEST_F(ApiHelperTest, GetHoldDependencyReturnsJsonData) + { + nlohmann::json data; + data["foo"] = "bar"; + data["big"] = "small"; + + CurlResponse response(data.dump(), false, 200); + CurlRequest expectedRequest(GetApiCurlRequest("/hold", CurlRequest::METHOD_GET)); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); + + EXPECT_EQ(data, this->helper.GetHoldDependency()); + } + + TEST_F(ApiHelperTest, ItHasAUrlToSendTo) + { + EXPECT_TRUE(this->helper.GetApiDomain() == mockApiUrl); + } + + TEST_F(ApiHelperTest, ItHasAnApiKeyToUseForAuthentication) + { + EXPECT_TRUE(this->helper.GetApiKey() == mockApiKey); + } + + TEST_F(ApiHelperTest, ItCanUpdateTheUrl) + { + this->helper.SetApiDomain("https://nottheurl"); + EXPECT_TRUE(this->helper.GetApiDomain() == "https://nottheurl"); + } + + TEST_F(ApiHelperTest, ItCanUpdateTheKey) + { + this->helper.SetApiKey("notthekey"); + EXPECT_TRUE(this->helper.GetApiKey() == "notthekey"); + } + + TEST_F(ApiHelperTest, GetMinStackLevelsReturnsMinStackData) + { + nlohmann::json responseData; + responseData["bla"] = "bla"; + CurlResponse response(responseData.dump(), false, 200); + + CurlRequest expectedRequest(GetApiCurlRequest("/msl", CurlRequest::METHOD_GET)); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); + + EXPECT_EQ(responseData, this->helper.GetMinStackLevels()); + } + + TEST_F(ApiHelperTest, GetMinStackLevelsReturnsRegionalPressureData) + { + nlohmann::json responseData; + responseData["bla"] = "bla"; + CurlResponse response(responseData.dump(), false, 200); + + CurlRequest expectedRequest(GetApiCurlRequest("/regional-pressure", CurlRequest::METHOD_GET)); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); + + EXPECT_EQ(responseData, this->helper.GetRegionalPressures()); + } + + TEST_F(ApiHelperTest, GetUriReturnsUriData) + { + nlohmann::json responseData; + responseData["bla"] = "bla"; + CurlResponse response(responseData.dump(), false, 200); + + CurlRequest expectedRequest(GetApiGetUriCurlRequest("http://ukcp.test.com/someuri", CurlRequest::METHOD_GET)); + expectedRequest.SetMaxRequestTime(0L); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); + + EXPECT_EQ(responseData, this->helper.GetUri("http://ukcp.test.com/someuri")); + } + + TEST_F(ApiHelperTest, GetUriThrowsExceptionIfNonUkcpRoute) + { + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(_)).Times(0); + + EXPECT_THROW(static_cast(this->helper.GetUri("http://ukcp.test.org/someuri")), ApiException); + } + + TEST_F(ApiHelperTest, SearchSrdReturnsData) + { + nlohmann::json responseData; + responseData["bla"] = "bla"; + CurlResponse response(responseData.dump(), false, 200); + + SrdSearchParameters params; + params.origin = "EGLL"; + params.destination = "EGGD"; + params.requestedLevel = 10000; + + CurlRequest expectedRequest(GetApiGetUriCurlRequest( + "http://ukcp.test.com/srd/route/search?origin=EGLL&destination=EGGD&requestedLevel=10000", + CurlRequest::METHOD_GET)); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); + + EXPECT_EQ(responseData, this->helper.SearchSrd(params)); + } + + TEST_F(ApiHelperTest, GetAssignedHoldsReturnsData) + { + nlohmann::json responseData; + responseData["bla"] = "bla"; + CurlResponse response(responseData.dump(), false, 200); + + CurlRequest expectedRequest( + GetApiGetUriCurlRequest("http://ukcp.test.com/hold/assigned", CurlRequest::METHOD_GET)); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); + + EXPECT_EQ(responseData, this->helper.GetAssignedHolds()); + } + + TEST_F(ApiHelperTest, AssignAircraftToHoldGeneratesRequest) + { + nlohmann::json responseData; + responseData["bla"] = "bla"; + CurlResponse response(responseData.dump(), false, 200); + + nlohmann::json requestBody; + requestBody["callsign"] = "BAW123"; + requestBody["navaid"] = "TIMBA"; + + CurlRequest expectedRequest(GetApiCurlRequest("/hold/assigned", CurlRequest::METHOD_PUT, requestBody)); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); + + EXPECT_NO_THROW(this->helper.AssignAircraftToHold("BAW123", "TIMBA")); + } + + TEST_F(ApiHelperTest, UnassignAircraftHoldGeneratesRequest) + { + nlohmann::json responseData; + responseData["bla"] = "bla"; + CurlResponse response(responseData.dump(), false, 200); + + CurlRequest expectedRequest(GetApiCurlRequest("/hold/assigned/BAW123", CurlRequest::METHOD_DELETE)); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); + + EXPECT_NO_THROW(this->helper.UnassignAircraftHold("BAW123")); + } + + TEST_F(ApiHelperTest, EnrouteReleaseGeneratesRequest) + { + nlohmann::json responseData; + responseData["bla"] = "bla"; + CurlResponse response(responseData.dump(), false, 200); + + CurlRequest expectedRequest = GetApiCurlRequest( + "/release/enroute", + CurlRequest::METHOD_POST, + { + {"callsign", "BAW123"}, + {"type", 1}, + {"initiating_controller", "LON_S_CTR"}, + {"target_controller", "LON_C_CTR"}, + }); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); + + EXPECT_NO_THROW(this->helper.SendEnrouteRelease("BAW123", "LON_S_CTR", "LON_C_CTR", 1)); + } + + TEST_F(ApiHelperTest, EnrouteReleaseGeneratesRequestWithReleasePoint) + { + nlohmann::json responseData; + responseData["bla"] = "bla"; + CurlResponse response(responseData.dump(), false, 200); + + CurlRequest expectedRequest = GetApiCurlRequest( + "/release/enroute", + CurlRequest::METHOD_POST, + {{"callsign", "BAW123"}, + {"type", 1}, + {"initiating_controller", "LON_S_CTR"}, + {"target_controller", "LON_C_CTR"}, + {"release_point", "LAM"}}); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); + + EXPECT_NO_THROW(this->helper.SendEnrouteReleaseWithReleasePoint("BAW123", "LON_S_CTR", "LON_C_CTR", 1, "LAM")); + } + + TEST_F(ApiHelperTest, GetAssignedStandsReturnsData) + { + nlohmann::json responseData; + responseData["bla"] = "bla"; + CurlResponse response(responseData.dump(), false, 200); + + CurlRequest expectedRequest( + GetApiGetUriCurlRequest("http://ukcp.test.com/stand/assignment", CurlRequest::METHOD_GET)); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); + + EXPECT_EQ(responseData, this->helper.GetAssignedStands()); + } + + TEST_F(ApiHelperTest, AssignStandToAircraftGeneratesRequest) + { + nlohmann::json responseData; + responseData["bla"] = "bla"; + CurlResponse response(responseData.dump(), false, 200); + + nlohmann::json requestBody; + requestBody["callsign"] = "BAW123"; + requestBody["stand_id"] = 1; + + CurlRequest expectedRequest(GetApiCurlRequest("/stand/assignment", CurlRequest::METHOD_PUT, requestBody)); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); + + EXPECT_NO_THROW(this->helper.AssignStandToAircraft("BAW123", 1)); + } + + TEST_F(ApiHelperTest, DeleteStandAssignmentForAircraftGeneratesRequest) + { + nlohmann::json responseData; + responseData["bla"] = "bla"; + CurlResponse response(responseData.dump(), false, 200); + + CurlRequest expectedRequest(GetApiCurlRequest("/stand/assignment/BAW123", CurlRequest::METHOD_DELETE)); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); + + EXPECT_NO_THROW(this->helper.DeleteStandAssignmentForAircraft("BAW123")); + } + + TEST_F(ApiHelperTest, GetAllNotificationsMakesRequest) + { + nlohmann::json responseData; + responseData["bla"] = "bla"; + CurlResponse response(responseData.dump(), false, 200); + + CurlRequest expectedRequest(GetApiCurlRequest("/notifications", CurlRequest::METHOD_GET)); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); + + EXPECT_EQ(responseData, this->helper.GetAllNotifications()); + } + + TEST_F(ApiHelperTest, GetUnreadNotificationsMakesRequest) + { + nlohmann::json responseData; + responseData["bla"] = "bla"; + CurlResponse response(responseData.dump(), false, 200); + + CurlRequest expectedRequest(GetApiCurlRequest("/notifications/unread", CurlRequest::METHOD_GET)); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); + + EXPECT_EQ(responseData, this->helper.GetUnreadNotifications()); + } + + TEST_F(ApiHelperTest, ReadNotificationMakesRequest) + { + nlohmann::json responseData; + responseData["bla"] = "bla"; + CurlResponse response(responseData.dump(), false, 200); + + CurlRequest expectedRequest(GetApiCurlRequest("/notifications/read/1", CurlRequest::METHOD_PUT)); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); + + EXPECT_NO_THROW(this->helper.ReadNotification(1)); + } + + TEST_F(ApiHelperTest, SyncPluginEventsMakesRequest) + { + nlohmann::json responseData; + responseData["bla"] = "bla"; + CurlResponse response(responseData.dump(), false, 200); + + CurlRequest expectedRequest(GetApiCurlRequest("/plugin-events/sync", CurlRequest::METHOD_GET)); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); + + EXPECT_EQ(responseData, this->helper.SyncPluginEvents()); + } + + TEST_F(ApiHelperTest, GetLatestPluginEventsMakesRequest) + { + nlohmann::json responseData; + responseData["bla"] = "bla"; + CurlResponse response(responseData.dump(), false, 200); + + CurlRequest expectedRequest(GetApiCurlRequest("/plugin-events/recent?previous=5", CurlRequest::METHOD_GET)); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); + + EXPECT_EQ(responseData, this->helper.GetLatestPluginEvents(5)); + } + + TEST_F(ApiHelperTest, GetUpdateDetailsReturnsData) + { + nlohmann::json responseData; + responseData["bla"] = "bla"; + CurlResponse response(responseData.dump(), false, 200); + + CurlRequest expectedRequest( + GetApiGetUriCurlRequest("http://ukcp.test.com/version/latest", CurlRequest::METHOD_GET)); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); + + EXPECT_EQ(responseData, this->helper.GetUpdateDetails()); + } + + TEST_F(ApiHelperTest, RequestDepartureReleaseMakesRequest) + { + nlohmann::json responseData; + responseData["bla"] = "bla"; + CurlResponse response(responseData.dump(), false, 200); + + nlohmann::json expectedData; + expectedData["callsign"] = "BAW123"; + expectedData["requesting_controller_id"] = 1; + expectedData["target_controller_id"] = 3; + expectedData["expires_in_seconds"] = 54; + + CurlRequest expectedRequest( + GetApiCurlRequest("/departure/release/request", CurlRequest::METHOD_POST, expectedData)); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); + + EXPECT_EQ(responseData, this->helper.RequestDepartureRelease("BAW123", 1, 3, 54)); + } + + TEST_F(ApiHelperTest, ApproveDepartureReleaseMakesRequest) + { + nlohmann::json responseData; + responseData["bla"] = "bla"; + CurlResponse response(responseData.dump(), false, 200); + + nlohmann::json expectedData; + expectedData["controller_position_id"] = 2; + expectedData["released_at"] = "2021-05-09 12:31:00"; + expectedData["expires_in_seconds"] = 120; + + std::chrono::system_clock::time_point timePoint; + std::istringstream inputStream("2021-05-09 12:31:00"); + inputStream >> date::parse("%Y-%m-%d %H:%M:%S", timePoint); + + CurlRequest expectedRequest( + GetApiCurlRequest("/departure/release/request/1/approve", CurlRequest::METHOD_PATCH, expectedData)); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); + + this->helper.ApproveDepartureReleaseRequest(1, 2, timePoint, 120); + } + + TEST_F(ApiHelperTest, RejectDepartureReleaseMakesRequest) + { + nlohmann::json responseData; + responseData["bla"] = "bla"; + CurlResponse response(responseData.dump(), false, 200); + + nlohmann::json expectedData; + expectedData["controller_position_id"] = 2; + + CurlRequest expectedRequest( + GetApiCurlRequest("/departure/release/request/1/reject", CurlRequest::METHOD_PATCH, expectedData)); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); + + this->helper.RejectDepartureReleaseRequest(1, 2); + } + + TEST_F(ApiHelperTest, AcknowledgeDepartureReleaseMakesRequest) + { + nlohmann::json responseData; + responseData["bla"] = "bla"; + CurlResponse response(responseData.dump(), false, 200); + + nlohmann::json expectedData; + expectedData["controller_position_id"] = 2; + + CurlRequest expectedRequest( + GetApiCurlRequest("/departure/release/request/1/acknowledge", CurlRequest::METHOD_PATCH, expectedData)); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); + + this->helper.AcknowledgeDepartureReleaseRequest(1, 2); + } + + TEST_F(ApiHelperTest, CancelDepartureReleaseMakesRequest) + { + nlohmann::json responseData; + responseData["bla"] = "bla"; + CurlResponse response(responseData.dump(), false, 200); + + CurlRequest expectedRequest(GetApiCurlRequest("/departure/release/request/1", CurlRequest::METHOD_DELETE)); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); + + this->helper.CancelDepartureReleaseRequest(1); + } + + TEST_F(ApiHelperTest, CreatePrenoteMessageRequestMakesRequest) + { + nlohmann::json responseData; + responseData["bla"] = "bla"; + CurlResponse response(responseData.dump(), false, 200); + + nlohmann::json expectedData; + expectedData["callsign"] = "BAW123"; + expectedData["departure_airfield"] = "EGGD"; + expectedData["departure_sid"] = "BADIM1X"; + expectedData["destination_airfield"] = "EGLC"; + expectedData["requesting_controller_id"] = 1; + expectedData["target_controller_id"] = 2; + expectedData["expires_in_seconds"] = 55; + + CurlRequest expectedRequest(GetApiCurlRequest("/prenotes/messages", CurlRequest::METHOD_POST, expectedData)); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); + + EXPECT_EQ(responseData, this->helper.CreatePrenoteMessage("BAW123", "EGGD", "BADIM1X", "EGLC", 1, 2, 55)); + } + + TEST_F(ApiHelperTest, AcknowledgePrenoteMessageMakesRequest) + { + nlohmann::json responseData; + responseData["bla"] = "bla"; + CurlResponse response(responseData.dump(), false, 200); + + nlohmann::json expectedData; + expectedData["controller_position_id"] = 2; + + CurlRequest expectedRequest( + GetApiCurlRequest("/prenotes/messages/55/acknowledge", CurlRequest::METHOD_PATCH, expectedData)); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); + + this->helper.AcknowledgePrenoteMessage(55, 2); + } + + TEST_F(ApiHelperTest, DeletePrenoteMessageMakesRequest) + { + nlohmann::json responseData; + responseData["bla"] = "bla"; + CurlResponse response(responseData.dump(), false, 200); + + CurlRequest expectedRequest(GetApiCurlRequest("/prenotes/messages/55", CurlRequest::METHOD_DELETE)); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); + + this->helper.DeletePrenoteMessage(55); + } + + TEST_F(ApiHelperTest, CreateMissedApproachMakesRequest) + { + nlohmann::json responseData; + responseData["bla"] = "bla"; + CurlResponse response(responseData.dump(), false, 200); + + nlohmann::json expectedData; + expectedData["callsign"] = "BAW123"; + + CurlRequest expectedRequest(GetApiCurlRequest("/missed-approaches", CurlRequest::METHOD_POST, expectedData)); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); + + EXPECT_EQ(responseData, this->helper.CreateMissedApproach("BAW123")); + } + + TEST_F(ApiHelperTest, GetAllMetarsMakesRequest) + { + nlohmann::json responseData; + responseData["bla"] = "bla"; + CurlResponse response(responseData.dump(), false, 200); + + CurlRequest expectedRequest(GetApiCurlRequest("/metar", CurlRequest::METHOD_GET)); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); + + EXPECT_EQ(responseData, this->helper.GetAllMetars()); + } + + TEST_F(ApiHelperTest, AcknowledgeMissedApproachMakesRequest) + { + nlohmann::json responseData; + responseData["bla"] = "bla"; + CurlResponse response(responseData.dump(), false, 200); + + nlohmann::json expectedData; + expectedData["remarks"] = "Some remarks"; + + CurlRequest expectedRequest(GetApiCurlRequest("/missed-approaches/1", CurlRequest::METHOD_PATCH, expectedData)); + + EXPECT_CALL(this->mockCurlApi, MakeCurlRequest(expectedRequest)).Times(1).WillOnce(Return(response)); + + this->helper.AcknowledgeMissedApproach(1, "Some remarks"); + } +} // namespace UKControllerPluginUtilsTest::Api diff --git a/test/utils/api/ApiRequestBuilderTest.cpp b/test/utils/api/ApiRequestBuilderTest.cpp index d720b9cb3..49bd1dbca 100644 --- a/test/utils/api/ApiRequestBuilderTest.cpp +++ b/test/utils/api/ApiRequestBuilderTest.cpp @@ -1,575 +1,584 @@ -#include "api/ApiRequestBuilder.h" -#include "srd/SrdSearchParameters.h" - -using ::testing::Test; - -using UKControllerPlugin::Api::ApiRequestBuilder; -using UKControllerPlugin::Curl::CurlRequest; -using UKControllerPlugin::Srd::SrdSearchParameters; - -namespace UKControllerPluginUtilsTest::Api { - - class ApiRequestBuilderTest : public Test - { - public: - ApiRequestBuilderTest() : builder("http://testurl.com", "apikey") - { - } - ApiRequestBuilder builder; - }; - - TEST_F(ApiRequestBuilderTest, ItHasAnApiDomain) - { - EXPECT_TRUE("http://testurl.com" == this->builder.GetApiDomain()); - } - - TEST_F(ApiRequestBuilderTest, ItHasAnApiKey) - { - EXPECT_TRUE("apikey" == this->builder.GetApiKey()); - } - - TEST_F(ApiRequestBuilderTest, ApiDomainCanBeUpdated) - { - this->builder.SetApiDomain("http://nottesturl.com"); - EXPECT_TRUE("http://nottesturl.com" == this->builder.GetApiDomain()); - } - - TEST_F(ApiRequestBuilderTest, ApiKeyCanBeUpdated) - { - this->builder.SetApiKey("notapikey"); - EXPECT_TRUE("notapikey" == this->builder.GetApiKey()); - } - - TEST_F(ApiRequestBuilderTest, ItBuildsAuthCheckRequests) - { - CurlRequest expectedRequest("http://testurl.com/authorise", CurlRequest::METHOD_GET); - expectedRequest.AddHeader("Authorization", "Bearer apikey"); - expectedRequest.AddHeader("Accept", "application/json"); - expectedRequest.AddHeader("Content-Type", "application/json"); - EXPECT_TRUE(expectedRequest == this->builder.BuildAuthCheckRequest()); - } - - TEST_F(ApiRequestBuilderTest, ItBuildsDependencyListRequests) - { - CurlRequest expectedRequest("http://testurl.com/dependency", CurlRequest::METHOD_GET); - expectedRequest.AddHeader("Authorization", "Bearer apikey"); - expectedRequest.AddHeader("Accept", "application/json"); - expectedRequest.AddHeader("Content-Type", "application/json"); - EXPECT_TRUE(expectedRequest == this->builder.BuildDependencyListRequest()); - } - - TEST_F(ApiRequestBuilderTest, ItBuildsRemoteFileDownloadRequests) - { - CurlRequest expectedRequest("http://testurl.com/files/test1.json", CurlRequest::METHOD_GET); - EXPECT_TRUE(expectedRequest == this->builder.BuildRemoteFileRequest("http://testurl.com/files/test1.json")); - } - - TEST_F(ApiRequestBuilderTest, ItBuildsSquawkAssignmentDeletionRequests) - { - CurlRequest expectedRequest("http://testurl.com/squawk-assignment/BAW123", CurlRequest::METHOD_DELETE); - expectedRequest.AddHeader("Authorization", "Bearer apikey"); - expectedRequest.AddHeader("Accept", "application/json"); - expectedRequest.AddHeader("Content-Type", "application/json"); - EXPECT_TRUE(expectedRequest == this->builder.BuildSquawkAssignmentDeletionRequest("BAW123")); - } - - TEST_F(ApiRequestBuilderTest, ItBuildsSquawkAssignmentCheckRequests) - { - CurlRequest expectedRequest("http://testurl.com/squawk-assignment/BAW123", CurlRequest::METHOD_GET); - expectedRequest.AddHeader("Authorization", "Bearer apikey"); - expectedRequest.AddHeader("Accept", "application/json"); - expectedRequest.AddHeader("Content-Type", "application/json"); - EXPECT_TRUE(expectedRequest == this->builder.BuildSquawkAssignmentCheckRequest("BAW123")); - } - - TEST_F(ApiRequestBuilderTest, ItBuildsGeneralSquawkAssignmentRequests) - { - CurlRequest expectedRequest("http://testurl.com/squawk-assignment/BAW123", CurlRequest::METHOD_PUT); - expectedRequest.AddHeader("Authorization", "Bearer apikey"); - expectedRequest.AddHeader("Accept", "application/json"); - expectedRequest.AddHeader("Content-Type", "application/json"); - - nlohmann::json expectedBodyJson; - expectedBodyJson["type"] = "general"; - expectedBodyJson["origin"] = "EGKK"; - expectedBodyJson["destination"] = "EGLL"; - expectedRequest.SetBody(expectedBodyJson.dump()); - - EXPECT_TRUE(expectedRequest == this->builder.BuildGeneralSquawkAssignmentRequest("BAW123", "EGKK", "EGLL")); - } - - TEST_F(ApiRequestBuilderTest, ItBuildsLocalSquawkAssignmentRequests) - { - CurlRequest expectedRequest("http://testurl.com/squawk-assignment/BAW123", CurlRequest::METHOD_PUT); - expectedRequest.AddHeader("Authorization", "Bearer apikey"); - expectedRequest.AddHeader("Accept", "application/json"); - expectedRequest.AddHeader("Content-Type", "application/json"); - - nlohmann::json expectedBodyJson; - expectedBodyJson["type"] = "local"; - expectedBodyJson["unit"] = "EGKK"; - expectedBodyJson["rules"] = "V"; - expectedRequest.SetBody(expectedBodyJson.dump()); - - EXPECT_TRUE(expectedRequest == this->builder.BuildLocalSquawkAssignmentRequest("BAW123", "EGKK", "V")); - } - - TEST_F(ApiRequestBuilderTest, ItBuildsHoldDependencyDataRequests) - { - CurlRequest expectedRequest("http://testurl.com/hold", CurlRequest::METHOD_GET); - expectedRequest.AddHeader("Authorization", "Bearer apikey"); - expectedRequest.AddHeader("Accept", "application/json"); - expectedRequest.AddHeader("Content-Type", "application/json"); - EXPECT_TRUE(expectedRequest == this->builder.BuildHoldDependencyRequest()); - } - - TEST_F(ApiRequestBuilderTest, ItBuildsAMinStackRequest) - { - CurlRequest expectedRequest("http://testurl.com/msl", CurlRequest::METHOD_GET); - expectedRequest.AddHeader("Authorization", "Bearer apikey"); - expectedRequest.AddHeader("Accept", "application/json"); - expectedRequest.AddHeader("Content-Type", "application/json"); - - EXPECT_TRUE(expectedRequest == this->builder.BuildMinStackLevelRequest()); - } - - TEST_F(ApiRequestBuilderTest, ItBuildsARegionalPressureRequest) - { - CurlRequest expectedRequest("http://testurl.com/regional-pressure", CurlRequest::METHOD_GET); - expectedRequest.AddHeader("Authorization", "Bearer apikey"); - expectedRequest.AddHeader("Accept", "application/json"); - expectedRequest.AddHeader("Content-Type", "application/json"); - - EXPECT_TRUE(expectedRequest == this->builder.BuildRegionalPressureRequest()); - } - - TEST_F(ApiRequestBuilderTest, ItBuildsAGetUriRequest) - { - CurlRequest expectedRequest("someuri", CurlRequest::METHOD_GET); - expectedRequest.AddHeader("Authorization", "Bearer apikey"); - expectedRequest.AddHeader("Accept", "application/json"); - expectedRequest.AddHeader("Content-Type", "application/json"); - expectedRequest.SetMaxRequestTime(0L); - - EXPECT_TRUE(expectedRequest == this->builder.BuildGetUriRequest("someuri")); - } - - TEST_F(ApiRequestBuilderTest, ItBuildsASrdSearchRequest) - { - SrdSearchParameters params; - params.origin = "EGKK"; - params.destination = "EGLL"; - - CurlRequest expectedRequest( - "http://testurl.com/srd/route/search?origin=EGKK&destination=EGLL", CurlRequest::METHOD_GET); - expectedRequest.AddHeader("Authorization", "Bearer apikey"); - expectedRequest.AddHeader("Accept", "application/json"); - expectedRequest.AddHeader("Content-Type", "application/json"); - - EXPECT_TRUE(expectedRequest == this->builder.BuildSrdQueryRequest(params)); - } - - TEST_F(ApiRequestBuilderTest, ItBuildsASrdSearchRequestWithRequestedLevel) - { - SrdSearchParameters params; - params.origin = "EGKK"; - params.destination = "EGLL"; - params.requestedLevel = 15000; - - CurlRequest expectedRequest( - "http://testurl.com/srd/route/search?origin=EGKK&destination=EGLL&requestedLevel=15000", - CurlRequest::METHOD_GET); - expectedRequest.AddHeader("Authorization", "Bearer apikey"); - expectedRequest.AddHeader("Accept", "application/json"); - expectedRequest.AddHeader("Content-Type", "application/json"); - - EXPECT_TRUE(expectedRequest == this->builder.BuildSrdQueryRequest(params)); - } - - TEST_F(ApiRequestBuilderTest, ItBuildsGetAssignedHoldsRequest) - { - CurlRequest expectedRequest("http://testurl.com/hold/assigned", CurlRequest::METHOD_GET); - expectedRequest.AddHeader("Authorization", "Bearer apikey"); - expectedRequest.AddHeader("Accept", "application/json"); - expectedRequest.AddHeader("Content-Type", "application/json"); - - EXPECT_TRUE(expectedRequest == this->builder.BuildAllAssignedHoldsRequest()); - } - - TEST_F(ApiRequestBuilderTest, ItBuildsSetAssignedHoldRequest) - { - CurlRequest expectedRequest("http://testurl.com/hold/assigned", CurlRequest::METHOD_PUT); - - nlohmann::json expectedData; - expectedData["callsign"] = "BAW123"; - expectedData["navaid"] = "TIMBA"; - expectedRequest.SetBody(expectedData.dump()); - - expectedRequest.AddHeader("Authorization", "Bearer apikey"); - expectedRequest.AddHeader("Accept", "application/json"); - expectedRequest.AddHeader("Content-Type", "application/json"); - - EXPECT_TRUE(expectedRequest == this->builder.BuildSetAssignedHoldRequest("BAW123", "TIMBA")); - } - - TEST_F(ApiRequestBuilderTest, ItBuildsDeleteAssignedHoldRequest) - { - CurlRequest expectedRequest("http://testurl.com/hold/assigned/BAW123", CurlRequest::METHOD_DELETE); - - expectedRequest.AddHeader("Authorization", "Bearer apikey"); - expectedRequest.AddHeader("Accept", "application/json"); - expectedRequest.AddHeader("Content-Type", "application/json"); - - EXPECT_TRUE(expectedRequest == this->builder.BuildDeleteAssignedHoldRequest("BAW123")); - } - - TEST_F(ApiRequestBuilderTest, ItBuildsBuildEnrouteReleaseRequest) - { - CurlRequest expectedRequest("http://testurl.com/release/enroute", CurlRequest::METHOD_POST); - - nlohmann::json expectedData{ - {"callsign", "BAW123"}, - {"type", 1}, - {"initiating_controller", "LON_S_CTR"}, - {"target_controller", "LON_C_CTR"}, - }; - expectedRequest.SetBody(expectedData.dump()); - - expectedRequest.AddHeader("Authorization", "Bearer apikey"); - expectedRequest.AddHeader("Accept", "application/json"); - expectedRequest.AddHeader("Content-Type", "application/json"); - - EXPECT_TRUE(expectedRequest == this->builder.BuildEnrouteReleaseRequest("BAW123", "LON_S_CTR", "LON_C_CTR", 1)); - } - - TEST_F(ApiRequestBuilderTest, ItBuildsBuildEnrouteReleaseRequestWithReleasePoint) - { - CurlRequest expectedRequest("http://testurl.com/release/enroute", CurlRequest::METHOD_POST); - - nlohmann::json expectedData{ - {"callsign", "BAW123"}, - {"type", 1}, - {"initiating_controller", "LON_S_CTR"}, - {"target_controller", "LON_C_CTR"}, - {"release_point", "LAM"}, - }; - expectedRequest.SetBody(expectedData.dump()); - - expectedRequest.AddHeader("Authorization", "Bearer apikey"); - expectedRequest.AddHeader("Accept", "application/json"); - expectedRequest.AddHeader("Content-Type", "application/json"); - - EXPECT_TRUE( - expectedRequest == - this->builder.BuildEnrouteReleaseRequestWithReleasePoint("BAW123", "LON_S_CTR", "LON_C_CTR", 1, "LAM")); - } - - TEST_F(ApiRequestBuilderTest, ItBuildsGetAssignedStandsRequest) - { - CurlRequest expectedRequest("http://testurl.com/stand/assignment", CurlRequest::METHOD_GET); - expectedRequest.AddHeader("Authorization", "Bearer apikey"); - expectedRequest.AddHeader("Accept", "application/json"); - expectedRequest.AddHeader("Content-Type", "application/json"); - - EXPECT_TRUE(expectedRequest == this->builder.BuildGetStandAssignmentsRequest()); - } - - TEST_F(ApiRequestBuilderTest, ItBuildsSetAssignedStandForAircraftRequest) - { - CurlRequest expectedRequest("http://testurl.com/stand/assignment", CurlRequest::METHOD_PUT); - - nlohmann::json expectedData; - expectedData["callsign"] = "BAW123"; - expectedData["stand_id"] = 1; - expectedRequest.SetBody(expectedData.dump()); - - expectedRequest.AddHeader("Authorization", "Bearer apikey"); - expectedRequest.AddHeader("Accept", "application/json"); - expectedRequest.AddHeader("Content-Type", "application/json"); - - EXPECT_TRUE(expectedRequest == this->builder.BuildAssignStandToAircraftRequest("BAW123", 1)); - } - - TEST_F(ApiRequestBuilderTest, ItBuildsDeleteAssignedStandForAircraftRequest) - { - CurlRequest expectedRequest("http://testurl.com/stand/assignment/BAW123", CurlRequest::METHOD_DELETE); - - expectedRequest.AddHeader("Authorization", "Bearer apikey"); - expectedRequest.AddHeader("Accept", "application/json"); - expectedRequest.AddHeader("Content-Type", "application/json"); - - EXPECT_TRUE(expectedRequest == this->builder.BuildDeleteStandAssignmentForAircraftRequest("BAW123")); - } - - TEST_F(ApiRequestBuilderTest, ItBuildsGetAllNotificationsRequest) - { - CurlRequest expectedRequest("http://testurl.com/notifications", CurlRequest::METHOD_GET); - - expectedRequest.AddHeader("Authorization", "Bearer apikey"); - expectedRequest.AddHeader("Accept", "application/json"); - expectedRequest.AddHeader("Content-Type", "application/json"); - - EXPECT_TRUE(expectedRequest == this->builder.BuildGetAllNotificationsRequest()); - } - - TEST_F(ApiRequestBuilderTest, ItBuildsGetUnreadNotificationsRequest) - { - CurlRequest expectedRequest("http://testurl.com/notifications/unread", CurlRequest::METHOD_GET); - - expectedRequest.AddHeader("Authorization", "Bearer apikey"); - expectedRequest.AddHeader("Accept", "application/json"); - expectedRequest.AddHeader("Content-Type", "application/json"); - - EXPECT_TRUE(expectedRequest == this->builder.BuildGetUnreadNotificationsRequest()); - } - - TEST_F(ApiRequestBuilderTest, ItBuildsReadNotificationRequest) - { - CurlRequest expectedRequest("http://testurl.com/notifications/read/1", CurlRequest::METHOD_PUT); - - expectedRequest.AddHeader("Authorization", "Bearer apikey"); - expectedRequest.AddHeader("Accept", "application/json"); - expectedRequest.AddHeader("Content-Type", "application/json"); - - EXPECT_TRUE(expectedRequest == this->builder.BuildReadNotificationRequest(1)); - } - - TEST_F(ApiRequestBuilderTest, ItBuildsLatestVersionDetailsRequest) - { - CurlRequest expectedRequest("http://testurl.com/version/latest", CurlRequest::METHOD_GET); - - expectedRequest.AddHeader("Authorization", "Bearer apikey"); - expectedRequest.AddHeader("Accept", "application/json"); - expectedRequest.AddHeader("Content-Type", "application/json"); - - EXPECT_TRUE(expectedRequest == this->builder.BuildLatestGithubVersionRequest()); - } - - TEST_F(ApiRequestBuilderTest, ItBuildsPluginEventsSyncRequest) - { - CurlRequest expectedRequest("http://testurl.com/plugin-events/sync", CurlRequest::METHOD_GET); - - expectedRequest.AddHeader("Authorization", "Bearer apikey"); - expectedRequest.AddHeader("Accept", "application/json"); - expectedRequest.AddHeader("Content-Type", "application/json"); - - EXPECT_TRUE(expectedRequest == this->builder.BuildPluginEventSyncRequest()); - } - - TEST_F(ApiRequestBuilderTest, ItBuildsGetLastestPluginEventsTest) - { - CurlRequest expectedRequest("http://testurl.com/plugin-events/recent?previous=5", CurlRequest::METHOD_GET); - - expectedRequest.AddHeader("Authorization", "Bearer apikey"); - expectedRequest.AddHeader("Accept", "application/json"); - expectedRequest.AddHeader("Content-Type", "application/json"); - - EXPECT_TRUE(expectedRequest == this->builder.BuildGetLatestPluginEventsRequest(5)); - } - - TEST_F(ApiRequestBuilderTest, ItBuildsDepartureReleaseRequest) - { - CurlRequest expectedRequest("http://testurl.com/departure/release/request", CurlRequest::METHOD_POST); - - expectedRequest.AddHeader("Authorization", "Bearer apikey"); - expectedRequest.AddHeader("Accept", "application/json"); - expectedRequest.AddHeader("Content-Type", "application/json"); - - nlohmann::json expectedData; - expectedData["callsign"] = "BAW123"; - expectedData["requesting_controller_id"] = 1; - expectedData["target_controller_id"] = 3; - expectedData["expires_in_seconds"] = 54; - expectedRequest.SetBody(expectedData.dump()); - - EXPECT_TRUE(expectedRequest == this->builder.BuildDepartureReleaseRequest("BAW123", 1, 3, 54)); - } - - TEST_F(ApiRequestBuilderTest, ItBuildsApproveDepartureReleaseRequest) - { - CurlRequest expectedRequest( - "http://testurl.com/departure/release/request/1/approve", CurlRequest::METHOD_PATCH); - - expectedRequest.AddHeader("Authorization", "Bearer apikey"); - expectedRequest.AddHeader("Accept", "application/json"); - expectedRequest.AddHeader("Content-Type", "application/json"); - - nlohmann::json expectedData; - expectedData["controller_position_id"] = 2; - expectedData["released_at"] = "2021-05-09 12:31:00"; - expectedData["expires_in_seconds"] = 120; - expectedRequest.SetBody(expectedData.dump()); - - std::chrono::system_clock::time_point timePoint; - std::istringstream inputStream("2021-05-09 12:31:00"); - inputStream >> date::parse("%Y-%m-%d %H:%M:%S", timePoint); - - EXPECT_TRUE(expectedRequest == this->builder.BuildApproveDepartureReleaseRequest(1, 2, timePoint, 120)); - } - - TEST_F(ApiRequestBuilderTest, ItBuildsApproveDepartureReleaseRequestWithNoExpiry) - { - CurlRequest expectedRequest( - "http://testurl.com/departure/release/request/1/approve", CurlRequest::METHOD_PATCH); - - expectedRequest.AddHeader("Authorization", "Bearer apikey"); - expectedRequest.AddHeader("Accept", "application/json"); - expectedRequest.AddHeader("Content-Type", "application/json"); - - nlohmann::json expectedData; - expectedData["controller_position_id"] = 2; - expectedData["released_at"] = "2021-05-09 12:31:00"; - expectedData["expires_in_seconds"] = nlohmann::json::value_t::null; - expectedRequest.SetBody(expectedData.dump()); - - std::chrono::system_clock::time_point timePoint; - std::istringstream inputStream("2021-05-09 12:31:00"); - inputStream >> date::parse("%Y-%m-%d %H:%M:%S", timePoint); - - EXPECT_TRUE(expectedRequest == this->builder.BuildApproveDepartureReleaseRequest(1, 2, timePoint, -1)); - } - - TEST_F(ApiRequestBuilderTest, ItBuildsRejectDepartureReleaseRequest) - { - CurlRequest expectedRequest("http://testurl.com/departure/release/request/1/reject", CurlRequest::METHOD_PATCH); - - expectedRequest.AddHeader("Authorization", "Bearer apikey"); - expectedRequest.AddHeader("Accept", "application/json"); - expectedRequest.AddHeader("Content-Type", "application/json"); - - nlohmann::json expectedData; - expectedData["controller_position_id"] = 2; - expectedRequest.SetBody(expectedData.dump()); - - EXPECT_TRUE(expectedRequest == this->builder.BuildRejectDepartureReleaseRequest(1, 2)); - } - - TEST_F(ApiRequestBuilderTest, ItBuildsAcknowledgeDepartureReleaseRequest) - { - CurlRequest expectedRequest( - "http://testurl.com/departure/release/request/1/acknowledge", CurlRequest::METHOD_PATCH); - - expectedRequest.AddHeader("Authorization", "Bearer apikey"); - expectedRequest.AddHeader("Accept", "application/json"); - expectedRequest.AddHeader("Content-Type", "application/json"); - - nlohmann::json expectedData; - expectedData["controller_position_id"] = 2; - expectedRequest.SetBody(expectedData.dump()); - - EXPECT_TRUE(expectedRequest == this->builder.BuildAcknowledgeDepartureReleaseRequest(1, 2)); - } - - TEST_F(ApiRequestBuilderTest, ItBuildsCancelDepartureReleaseRequest) - { - CurlRequest expectedRequest("http://testurl.com/departure/release/request/1", CurlRequest::METHOD_DELETE); - - expectedRequest.AddHeader("Authorization", "Bearer apikey"); - expectedRequest.AddHeader("Accept", "application/json"); - expectedRequest.AddHeader("Content-Type", "application/json"); - - EXPECT_TRUE(expectedRequest == this->builder.BuildCancelReleaseRequest(1)); - } - - TEST_F(ApiRequestBuilderTest, ItBuildsCreatePrenoteMessageWithAllValues) - { - CurlRequest expectedRequest("http://testurl.com/prenotes/messages", CurlRequest::METHOD_POST); - - expectedRequest.AddHeader("Authorization", "Bearer apikey"); - expectedRequest.AddHeader("Accept", "application/json"); - expectedRequest.AddHeader("Content-Type", "application/json"); - - nlohmann::json expectedData = { - {"callsign", "BAW123"}, - {"departure_airfield", "EGLC"}, - {"departure_sid", "CPT2T"}, - {"destination_airfield", "EGGD"}, - {"requesting_controller_id", 1}, - {"target_controller_id", 2}, - {"expires_in_seconds", 50}, - }; - expectedRequest.SetBody(expectedData.dump()); - - EXPECT_TRUE( - expectedRequest == - this->builder.BuildCreatePrenoteMessageRequest("BAW123", "EGLC", "CPT2T", "EGGD", 1, 2, 50)); - } - - TEST_F(ApiRequestBuilderTest, ItBuildsCreatePrenoteMessageWithMissingValues) - { - CurlRequest expectedRequest("http://testurl.com/prenotes/messages", CurlRequest::METHOD_POST); - - expectedRequest.AddHeader("Authorization", "Bearer apikey"); - expectedRequest.AddHeader("Accept", "application/json"); - expectedRequest.AddHeader("Content-Type", "application/json"); - - nlohmann::json expectedData = { - {"callsign", "BAW123"}, - {"departure_airfield", "EGLC"}, - {"departure_sid", nlohmann::json::value_t::null}, - {"destination_airfield", nlohmann::json::value_t::null}, - {"requesting_controller_id", 1}, - {"target_controller_id", 2}, - {"expires_in_seconds", 50}, - }; - expectedRequest.SetBody(expectedData.dump()); - - EXPECT_TRUE( - expectedRequest == this->builder.BuildCreatePrenoteMessageRequest("BAW123", "EGLC", "", "", 1, 2, 50)); - } - - TEST_F(ApiRequestBuilderTest, ItBuildsAcknowledgePrenoteMessage) - { - CurlRequest expectedRequest("http://testurl.com/prenotes/messages/55/acknowledge", CurlRequest::METHOD_PATCH); - - expectedRequest.AddHeader("Authorization", "Bearer apikey"); - expectedRequest.AddHeader("Accept", "application/json"); - expectedRequest.AddHeader("Content-Type", "application/json"); - - nlohmann::json expectedData = { - {"controller_position_id", 1}, - }; - expectedRequest.SetBody(expectedData.dump()); - - EXPECT_TRUE(expectedRequest == this->builder.BuildAcknowledgePrenoteMessageRequest(55, 1)); - } - - TEST_F(ApiRequestBuilderTest, ItBuildsDeletePrenoteMessage) - { - CurlRequest expectedRequest("http://testurl.com/prenotes/messages/55", CurlRequest::METHOD_DELETE); - - expectedRequest.AddHeader("Authorization", "Bearer apikey"); - expectedRequest.AddHeader("Accept", "application/json"); - expectedRequest.AddHeader("Content-Type", "application/json"); - - EXPECT_TRUE(expectedRequest == this->builder.BuildDeletePrenoteMessageRequest(55)); - } - - TEST_F(ApiRequestBuilderTest, ItBuildsMissedApproachMessage) - { - CurlRequest expectedRequest("http://testurl.com/missed-approaches", CurlRequest::METHOD_POST); - - expectedRequest.AddHeader("Authorization", "Bearer apikey"); - expectedRequest.AddHeader("Accept", "application/json"); - expectedRequest.AddHeader("Content-Type", "application/json"); - - nlohmann::json expectedData = {{"callsign", "BAW123"}}; - expectedRequest.SetBody(expectedData.dump()); - - EXPECT_TRUE(expectedRequest == this->builder.BuildMissedApproachMessage("BAW123")); - } - - TEST_F(ApiRequestBuilderTest, ItBuildsMissedApproachAcknowledge) - { - CurlRequest expectedRequest("http://testurl.com/missed-approaches/1", CurlRequest::METHOD_PATCH); - - expectedRequest.AddHeader("Authorization", "Bearer apikey"); - expectedRequest.AddHeader("Accept", "application/json"); - expectedRequest.AddHeader("Content-Type", "application/json"); - - nlohmann::json expectedData = {{"remarks", "Some remarks"}}; - expectedRequest.SetBody(expectedData.dump()); - - EXPECT_TRUE(expectedRequest == this->builder.BuildMissedApproachAcknowledgeMessage(1, "Some remarks")); - } -} // namespace UKControllerPluginUtilsTest::Api +#include "api/ApiRequestBuilder.h" +#include "srd/SrdSearchParameters.h" + +using ::testing::Test; + +using UKControllerPlugin::Api::ApiRequestBuilder; +using UKControllerPlugin::Curl::CurlRequest; +using UKControllerPlugin::Srd::SrdSearchParameters; + +namespace UKControllerPluginUtilsTest::Api { + + class ApiRequestBuilderTest : public Test + { + public: + ApiRequestBuilderTest() : builder("http://testurl.com", "apikey") + { + } + ApiRequestBuilder builder; + }; + + TEST_F(ApiRequestBuilderTest, ItHasAnApiDomain) + { + EXPECT_TRUE("http://testurl.com" == this->builder.GetApiDomain()); + } + + TEST_F(ApiRequestBuilderTest, ItHasAnApiKey) + { + EXPECT_TRUE("apikey" == this->builder.GetApiKey()); + } + + TEST_F(ApiRequestBuilderTest, ApiDomainCanBeUpdated) + { + this->builder.SetApiDomain("http://nottesturl.com"); + EXPECT_TRUE("http://nottesturl.com" == this->builder.GetApiDomain()); + } + + TEST_F(ApiRequestBuilderTest, ApiKeyCanBeUpdated) + { + this->builder.SetApiKey("notapikey"); + EXPECT_TRUE("notapikey" == this->builder.GetApiKey()); + } + + TEST_F(ApiRequestBuilderTest, ItBuildsAuthCheckRequests) + { + CurlRequest expectedRequest("http://testurl.com/authorise", CurlRequest::METHOD_GET); + expectedRequest.AddHeader("Authorization", "Bearer apikey"); + expectedRequest.AddHeader("Accept", "application/json"); + expectedRequest.AddHeader("Content-Type", "application/json"); + EXPECT_TRUE(expectedRequest == this->builder.BuildAuthCheckRequest()); + } + + TEST_F(ApiRequestBuilderTest, ItBuildsDependencyListRequests) + { + CurlRequest expectedRequest("http://testurl.com/dependency", CurlRequest::METHOD_GET); + expectedRequest.AddHeader("Authorization", "Bearer apikey"); + expectedRequest.AddHeader("Accept", "application/json"); + expectedRequest.AddHeader("Content-Type", "application/json"); + EXPECT_TRUE(expectedRequest == this->builder.BuildDependencyListRequest()); + } + + TEST_F(ApiRequestBuilderTest, ItBuildsRemoteFileDownloadRequests) + { + CurlRequest expectedRequest("http://testurl.com/files/test1.json", CurlRequest::METHOD_GET); + EXPECT_TRUE(expectedRequest == this->builder.BuildRemoteFileRequest("http://testurl.com/files/test1.json")); + } + + TEST_F(ApiRequestBuilderTest, ItBuildsSquawkAssignmentDeletionRequests) + { + CurlRequest expectedRequest("http://testurl.com/squawk-assignment/BAW123", CurlRequest::METHOD_DELETE); + expectedRequest.AddHeader("Authorization", "Bearer apikey"); + expectedRequest.AddHeader("Accept", "application/json"); + expectedRequest.AddHeader("Content-Type", "application/json"); + EXPECT_TRUE(expectedRequest == this->builder.BuildSquawkAssignmentDeletionRequest("BAW123")); + } + + TEST_F(ApiRequestBuilderTest, ItBuildsSquawkAssignmentCheckRequests) + { + CurlRequest expectedRequest("http://testurl.com/squawk-assignment/BAW123", CurlRequest::METHOD_GET); + expectedRequest.AddHeader("Authorization", "Bearer apikey"); + expectedRequest.AddHeader("Accept", "application/json"); + expectedRequest.AddHeader("Content-Type", "application/json"); + EXPECT_TRUE(expectedRequest == this->builder.BuildSquawkAssignmentCheckRequest("BAW123")); + } + + TEST_F(ApiRequestBuilderTest, ItBuildsGeneralSquawkAssignmentRequests) + { + CurlRequest expectedRequest("http://testurl.com/squawk-assignment/BAW123", CurlRequest::METHOD_PUT); + expectedRequest.AddHeader("Authorization", "Bearer apikey"); + expectedRequest.AddHeader("Accept", "application/json"); + expectedRequest.AddHeader("Content-Type", "application/json"); + + nlohmann::json expectedBodyJson; + expectedBodyJson["type"] = "general"; + expectedBodyJson["origin"] = "EGKK"; + expectedBodyJson["destination"] = "EGLL"; + expectedRequest.SetBody(expectedBodyJson.dump()); + + EXPECT_TRUE(expectedRequest == this->builder.BuildGeneralSquawkAssignmentRequest("BAW123", "EGKK", "EGLL")); + } + + TEST_F(ApiRequestBuilderTest, ItBuildsLocalSquawkAssignmentRequests) + { + CurlRequest expectedRequest("http://testurl.com/squawk-assignment/BAW123", CurlRequest::METHOD_PUT); + expectedRequest.AddHeader("Authorization", "Bearer apikey"); + expectedRequest.AddHeader("Accept", "application/json"); + expectedRequest.AddHeader("Content-Type", "application/json"); + + nlohmann::json expectedBodyJson; + expectedBodyJson["type"] = "local"; + expectedBodyJson["unit"] = "EGKK"; + expectedBodyJson["rules"] = "V"; + expectedRequest.SetBody(expectedBodyJson.dump()); + + EXPECT_TRUE(expectedRequest == this->builder.BuildLocalSquawkAssignmentRequest("BAW123", "EGKK", "V")); + } + + TEST_F(ApiRequestBuilderTest, ItBuildsHoldDependencyDataRequests) + { + CurlRequest expectedRequest("http://testurl.com/hold", CurlRequest::METHOD_GET); + expectedRequest.AddHeader("Authorization", "Bearer apikey"); + expectedRequest.AddHeader("Accept", "application/json"); + expectedRequest.AddHeader("Content-Type", "application/json"); + EXPECT_TRUE(expectedRequest == this->builder.BuildHoldDependencyRequest()); + } + + TEST_F(ApiRequestBuilderTest, ItBuildsAMinStackRequest) + { + CurlRequest expectedRequest("http://testurl.com/msl", CurlRequest::METHOD_GET); + expectedRequest.AddHeader("Authorization", "Bearer apikey"); + expectedRequest.AddHeader("Accept", "application/json"); + expectedRequest.AddHeader("Content-Type", "application/json"); + + EXPECT_TRUE(expectedRequest == this->builder.BuildMinStackLevelRequest()); + } + + TEST_F(ApiRequestBuilderTest, ItBuildsARegionalPressureRequest) + { + CurlRequest expectedRequest("http://testurl.com/regional-pressure", CurlRequest::METHOD_GET); + expectedRequest.AddHeader("Authorization", "Bearer apikey"); + expectedRequest.AddHeader("Accept", "application/json"); + expectedRequest.AddHeader("Content-Type", "application/json"); + + EXPECT_TRUE(expectedRequest == this->builder.BuildRegionalPressureRequest()); + } + + TEST_F(ApiRequestBuilderTest, ItBuildsAGetUriRequest) + { + CurlRequest expectedRequest("someuri", CurlRequest::METHOD_GET); + expectedRequest.AddHeader("Authorization", "Bearer apikey"); + expectedRequest.AddHeader("Accept", "application/json"); + expectedRequest.AddHeader("Content-Type", "application/json"); + expectedRequest.SetMaxRequestTime(0L); + + EXPECT_TRUE(expectedRequest == this->builder.BuildGetUriRequest("someuri")); + } + + TEST_F(ApiRequestBuilderTest, ItBuildsASrdSearchRequest) + { + SrdSearchParameters params; + params.origin = "EGKK"; + params.destination = "EGLL"; + + CurlRequest expectedRequest( + "http://testurl.com/srd/route/search?origin=EGKK&destination=EGLL", CurlRequest::METHOD_GET); + expectedRequest.AddHeader("Authorization", "Bearer apikey"); + expectedRequest.AddHeader("Accept", "application/json"); + expectedRequest.AddHeader("Content-Type", "application/json"); + + EXPECT_TRUE(expectedRequest == this->builder.BuildSrdQueryRequest(params)); + } + + TEST_F(ApiRequestBuilderTest, ItBuildsASrdSearchRequestWithRequestedLevel) + { + SrdSearchParameters params; + params.origin = "EGKK"; + params.destination = "EGLL"; + params.requestedLevel = 15000; + + CurlRequest expectedRequest( + "http://testurl.com/srd/route/search?origin=EGKK&destination=EGLL&requestedLevel=15000", + CurlRequest::METHOD_GET); + expectedRequest.AddHeader("Authorization", "Bearer apikey"); + expectedRequest.AddHeader("Accept", "application/json"); + expectedRequest.AddHeader("Content-Type", "application/json"); + + EXPECT_TRUE(expectedRequest == this->builder.BuildSrdQueryRequest(params)); + } + + TEST_F(ApiRequestBuilderTest, ItBuildsGetAssignedHoldsRequest) + { + CurlRequest expectedRequest("http://testurl.com/hold/assigned", CurlRequest::METHOD_GET); + expectedRequest.AddHeader("Authorization", "Bearer apikey"); + expectedRequest.AddHeader("Accept", "application/json"); + expectedRequest.AddHeader("Content-Type", "application/json"); + + EXPECT_TRUE(expectedRequest == this->builder.BuildAllAssignedHoldsRequest()); + } + + TEST_F(ApiRequestBuilderTest, ItBuildsSetAssignedHoldRequest) + { + CurlRequest expectedRequest("http://testurl.com/hold/assigned", CurlRequest::METHOD_PUT); + + nlohmann::json expectedData; + expectedData["callsign"] = "BAW123"; + expectedData["navaid"] = "TIMBA"; + expectedRequest.SetBody(expectedData.dump()); + + expectedRequest.AddHeader("Authorization", "Bearer apikey"); + expectedRequest.AddHeader("Accept", "application/json"); + expectedRequest.AddHeader("Content-Type", "application/json"); + + EXPECT_TRUE(expectedRequest == this->builder.BuildSetAssignedHoldRequest("BAW123", "TIMBA")); + } + + TEST_F(ApiRequestBuilderTest, ItBuildsDeleteAssignedHoldRequest) + { + CurlRequest expectedRequest("http://testurl.com/hold/assigned/BAW123", CurlRequest::METHOD_DELETE); + + expectedRequest.AddHeader("Authorization", "Bearer apikey"); + expectedRequest.AddHeader("Accept", "application/json"); + expectedRequest.AddHeader("Content-Type", "application/json"); + + EXPECT_TRUE(expectedRequest == this->builder.BuildDeleteAssignedHoldRequest("BAW123")); + } + + TEST_F(ApiRequestBuilderTest, ItBuildsBuildEnrouteReleaseRequest) + { + CurlRequest expectedRequest("http://testurl.com/release/enroute", CurlRequest::METHOD_POST); + + nlohmann::json expectedData{ + {"callsign", "BAW123"}, + {"type", 1}, + {"initiating_controller", "LON_S_CTR"}, + {"target_controller", "LON_C_CTR"}, + }; + expectedRequest.SetBody(expectedData.dump()); + + expectedRequest.AddHeader("Authorization", "Bearer apikey"); + expectedRequest.AddHeader("Accept", "application/json"); + expectedRequest.AddHeader("Content-Type", "application/json"); + + EXPECT_TRUE(expectedRequest == this->builder.BuildEnrouteReleaseRequest("BAW123", "LON_S_CTR", "LON_C_CTR", 1)); + } + + TEST_F(ApiRequestBuilderTest, ItBuildsBuildEnrouteReleaseRequestWithReleasePoint) + { + CurlRequest expectedRequest("http://testurl.com/release/enroute", CurlRequest::METHOD_POST); + + nlohmann::json expectedData{ + {"callsign", "BAW123"}, + {"type", 1}, + {"initiating_controller", "LON_S_CTR"}, + {"target_controller", "LON_C_CTR"}, + {"release_point", "LAM"}, + }; + expectedRequest.SetBody(expectedData.dump()); + + expectedRequest.AddHeader("Authorization", "Bearer apikey"); + expectedRequest.AddHeader("Accept", "application/json"); + expectedRequest.AddHeader("Content-Type", "application/json"); + + EXPECT_TRUE( + expectedRequest == + this->builder.BuildEnrouteReleaseRequestWithReleasePoint("BAW123", "LON_S_CTR", "LON_C_CTR", 1, "LAM")); + } + + TEST_F(ApiRequestBuilderTest, ItBuildsGetAssignedStandsRequest) + { + CurlRequest expectedRequest("http://testurl.com/stand/assignment", CurlRequest::METHOD_GET); + expectedRequest.AddHeader("Authorization", "Bearer apikey"); + expectedRequest.AddHeader("Accept", "application/json"); + expectedRequest.AddHeader("Content-Type", "application/json"); + + EXPECT_TRUE(expectedRequest == this->builder.BuildGetStandAssignmentsRequest()); + } + + TEST_F(ApiRequestBuilderTest, ItBuildsSetAssignedStandForAircraftRequest) + { + CurlRequest expectedRequest("http://testurl.com/stand/assignment", CurlRequest::METHOD_PUT); + + nlohmann::json expectedData; + expectedData["callsign"] = "BAW123"; + expectedData["stand_id"] = 1; + expectedRequest.SetBody(expectedData.dump()); + + expectedRequest.AddHeader("Authorization", "Bearer apikey"); + expectedRequest.AddHeader("Accept", "application/json"); + expectedRequest.AddHeader("Content-Type", "application/json"); + + EXPECT_TRUE(expectedRequest == this->builder.BuildAssignStandToAircraftRequest("BAW123", 1)); + } + + TEST_F(ApiRequestBuilderTest, ItBuildsDeleteAssignedStandForAircraftRequest) + { + CurlRequest expectedRequest("http://testurl.com/stand/assignment/BAW123", CurlRequest::METHOD_DELETE); + + expectedRequest.AddHeader("Authorization", "Bearer apikey"); + expectedRequest.AddHeader("Accept", "application/json"); + expectedRequest.AddHeader("Content-Type", "application/json"); + + EXPECT_TRUE(expectedRequest == this->builder.BuildDeleteStandAssignmentForAircraftRequest("BAW123")); + } + + TEST_F(ApiRequestBuilderTest, ItBuildsGetAllNotificationsRequest) + { + CurlRequest expectedRequest("http://testurl.com/notifications", CurlRequest::METHOD_GET); + + expectedRequest.AddHeader("Authorization", "Bearer apikey"); + expectedRequest.AddHeader("Accept", "application/json"); + expectedRequest.AddHeader("Content-Type", "application/json"); + + EXPECT_TRUE(expectedRequest == this->builder.BuildGetAllNotificationsRequest()); + } + + TEST_F(ApiRequestBuilderTest, ItBuildsGetUnreadNotificationsRequest) + { + CurlRequest expectedRequest("http://testurl.com/notifications/unread", CurlRequest::METHOD_GET); + + expectedRequest.AddHeader("Authorization", "Bearer apikey"); + expectedRequest.AddHeader("Accept", "application/json"); + expectedRequest.AddHeader("Content-Type", "application/json"); + + EXPECT_TRUE(expectedRequest == this->builder.BuildGetUnreadNotificationsRequest()); + } + + TEST_F(ApiRequestBuilderTest, ItBuildsReadNotificationRequest) + { + CurlRequest expectedRequest("http://testurl.com/notifications/read/1", CurlRequest::METHOD_PUT); + + expectedRequest.AddHeader("Authorization", "Bearer apikey"); + expectedRequest.AddHeader("Accept", "application/json"); + expectedRequest.AddHeader("Content-Type", "application/json"); + + EXPECT_TRUE(expectedRequest == this->builder.BuildReadNotificationRequest(1)); + } + + TEST_F(ApiRequestBuilderTest, ItBuildsLatestVersionDetailsRequest) + { + CurlRequest expectedRequest("http://testurl.com/version/latest", CurlRequest::METHOD_GET); + + expectedRequest.AddHeader("Authorization", "Bearer apikey"); + expectedRequest.AddHeader("Accept", "application/json"); + expectedRequest.AddHeader("Content-Type", "application/json"); + + EXPECT_TRUE(expectedRequest == this->builder.BuildLatestGithubVersionRequest()); + } + + TEST_F(ApiRequestBuilderTest, ItBuildsPluginEventsSyncRequest) + { + CurlRequest expectedRequest("http://testurl.com/plugin-events/sync", CurlRequest::METHOD_GET); + + expectedRequest.AddHeader("Authorization", "Bearer apikey"); + expectedRequest.AddHeader("Accept", "application/json"); + expectedRequest.AddHeader("Content-Type", "application/json"); + + EXPECT_TRUE(expectedRequest == this->builder.BuildPluginEventSyncRequest()); + } + + TEST_F(ApiRequestBuilderTest, ItBuildsGetLastestPluginEventsTest) + { + CurlRequest expectedRequest("http://testurl.com/plugin-events/recent?previous=5", CurlRequest::METHOD_GET); + + expectedRequest.AddHeader("Authorization", "Bearer apikey"); + expectedRequest.AddHeader("Accept", "application/json"); + expectedRequest.AddHeader("Content-Type", "application/json"); + + EXPECT_TRUE(expectedRequest == this->builder.BuildGetLatestPluginEventsRequest(5)); + } + + TEST_F(ApiRequestBuilderTest, ItBuildsDepartureReleaseRequest) + { + CurlRequest expectedRequest("http://testurl.com/departure/release/request", CurlRequest::METHOD_POST); + + expectedRequest.AddHeader("Authorization", "Bearer apikey"); + expectedRequest.AddHeader("Accept", "application/json"); + expectedRequest.AddHeader("Content-Type", "application/json"); + + nlohmann::json expectedData; + expectedData["callsign"] = "BAW123"; + expectedData["requesting_controller_id"] = 1; + expectedData["target_controller_id"] = 3; + expectedData["expires_in_seconds"] = 54; + expectedRequest.SetBody(expectedData.dump()); + + EXPECT_TRUE(expectedRequest == this->builder.BuildDepartureReleaseRequest("BAW123", 1, 3, 54)); + } + + TEST_F(ApiRequestBuilderTest, ItBuildsApproveDepartureReleaseRequest) + { + CurlRequest expectedRequest( + "http://testurl.com/departure/release/request/1/approve", CurlRequest::METHOD_PATCH); + + expectedRequest.AddHeader("Authorization", "Bearer apikey"); + expectedRequest.AddHeader("Accept", "application/json"); + expectedRequest.AddHeader("Content-Type", "application/json"); + + nlohmann::json expectedData; + expectedData["controller_position_id"] = 2; + expectedData["released_at"] = "2021-05-09 12:31:00"; + expectedData["expires_in_seconds"] = 120; + expectedRequest.SetBody(expectedData.dump()); + + std::chrono::system_clock::time_point timePoint; + std::istringstream inputStream("2021-05-09 12:31:00"); + inputStream >> date::parse("%Y-%m-%d %H:%M:%S", timePoint); + + EXPECT_TRUE(expectedRequest == this->builder.BuildApproveDepartureReleaseRequest(1, 2, timePoint, 120)); + } + + TEST_F(ApiRequestBuilderTest, ItBuildsApproveDepartureReleaseRequestWithNoExpiry) + { + CurlRequest expectedRequest( + "http://testurl.com/departure/release/request/1/approve", CurlRequest::METHOD_PATCH); + + expectedRequest.AddHeader("Authorization", "Bearer apikey"); + expectedRequest.AddHeader("Accept", "application/json"); + expectedRequest.AddHeader("Content-Type", "application/json"); + + nlohmann::json expectedData; + expectedData["controller_position_id"] = 2; + expectedData["released_at"] = "2021-05-09 12:31:00"; + expectedData["expires_in_seconds"] = nlohmann::json::value_t::null; + expectedRequest.SetBody(expectedData.dump()); + + std::chrono::system_clock::time_point timePoint; + std::istringstream inputStream("2021-05-09 12:31:00"); + inputStream >> date::parse("%Y-%m-%d %H:%M:%S", timePoint); + + EXPECT_TRUE(expectedRequest == this->builder.BuildApproveDepartureReleaseRequest(1, 2, timePoint, -1)); + } + + TEST_F(ApiRequestBuilderTest, ItBuildsRejectDepartureReleaseRequest) + { + CurlRequest expectedRequest("http://testurl.com/departure/release/request/1/reject", CurlRequest::METHOD_PATCH); + + expectedRequest.AddHeader("Authorization", "Bearer apikey"); + expectedRequest.AddHeader("Accept", "application/json"); + expectedRequest.AddHeader("Content-Type", "application/json"); + + nlohmann::json expectedData; + expectedData["controller_position_id"] = 2; + expectedRequest.SetBody(expectedData.dump()); + + EXPECT_TRUE(expectedRequest == this->builder.BuildRejectDepartureReleaseRequest(1, 2)); + } + + TEST_F(ApiRequestBuilderTest, ItBuildsAcknowledgeDepartureReleaseRequest) + { + CurlRequest expectedRequest( + "http://testurl.com/departure/release/request/1/acknowledge", CurlRequest::METHOD_PATCH); + + expectedRequest.AddHeader("Authorization", "Bearer apikey"); + expectedRequest.AddHeader("Accept", "application/json"); + expectedRequest.AddHeader("Content-Type", "application/json"); + + nlohmann::json expectedData; + expectedData["controller_position_id"] = 2; + expectedRequest.SetBody(expectedData.dump()); + + EXPECT_TRUE(expectedRequest == this->builder.BuildAcknowledgeDepartureReleaseRequest(1, 2)); + } + + TEST_F(ApiRequestBuilderTest, ItBuildsCancelDepartureReleaseRequest) + { + CurlRequest expectedRequest("http://testurl.com/departure/release/request/1", CurlRequest::METHOD_DELETE); + + expectedRequest.AddHeader("Authorization", "Bearer apikey"); + expectedRequest.AddHeader("Accept", "application/json"); + expectedRequest.AddHeader("Content-Type", "application/json"); + + EXPECT_TRUE(expectedRequest == this->builder.BuildCancelReleaseRequest(1)); + } + + TEST_F(ApiRequestBuilderTest, ItBuildsCreatePrenoteMessageWithAllValues) + { + CurlRequest expectedRequest("http://testurl.com/prenotes/messages", CurlRequest::METHOD_POST); + + expectedRequest.AddHeader("Authorization", "Bearer apikey"); + expectedRequest.AddHeader("Accept", "application/json"); + expectedRequest.AddHeader("Content-Type", "application/json"); + + nlohmann::json expectedData = { + {"callsign", "BAW123"}, + {"departure_airfield", "EGLC"}, + {"departure_sid", "CPT2T"}, + {"destination_airfield", "EGGD"}, + {"requesting_controller_id", 1}, + {"target_controller_id", 2}, + {"expires_in_seconds", 50}, + }; + expectedRequest.SetBody(expectedData.dump()); + + EXPECT_TRUE( + expectedRequest == + this->builder.BuildCreatePrenoteMessageRequest("BAW123", "EGLC", "CPT2T", "EGGD", 1, 2, 50)); + } + + TEST_F(ApiRequestBuilderTest, ItBuildsCreatePrenoteMessageWithMissingValues) + { + CurlRequest expectedRequest("http://testurl.com/prenotes/messages", CurlRequest::METHOD_POST); + + expectedRequest.AddHeader("Authorization", "Bearer apikey"); + expectedRequest.AddHeader("Accept", "application/json"); + expectedRequest.AddHeader("Content-Type", "application/json"); + + nlohmann::json expectedData = { + {"callsign", "BAW123"}, + {"departure_airfield", "EGLC"}, + {"departure_sid", nlohmann::json::value_t::null}, + {"destination_airfield", nlohmann::json::value_t::null}, + {"requesting_controller_id", 1}, + {"target_controller_id", 2}, + {"expires_in_seconds", 50}, + }; + expectedRequest.SetBody(expectedData.dump()); + + EXPECT_TRUE( + expectedRequest == this->builder.BuildCreatePrenoteMessageRequest("BAW123", "EGLC", "", "", 1, 2, 50)); + } + + TEST_F(ApiRequestBuilderTest, ItBuildsAcknowledgePrenoteMessage) + { + CurlRequest expectedRequest("http://testurl.com/prenotes/messages/55/acknowledge", CurlRequest::METHOD_PATCH); + + expectedRequest.AddHeader("Authorization", "Bearer apikey"); + expectedRequest.AddHeader("Accept", "application/json"); + expectedRequest.AddHeader("Content-Type", "application/json"); + + nlohmann::json expectedData = { + {"controller_position_id", 1}, + }; + expectedRequest.SetBody(expectedData.dump()); + + EXPECT_TRUE(expectedRequest == this->builder.BuildAcknowledgePrenoteMessageRequest(55, 1)); + } + + TEST_F(ApiRequestBuilderTest, ItBuildsDeletePrenoteMessage) + { + CurlRequest expectedRequest("http://testurl.com/prenotes/messages/55", CurlRequest::METHOD_DELETE); + + expectedRequest.AddHeader("Authorization", "Bearer apikey"); + expectedRequest.AddHeader("Accept", "application/json"); + expectedRequest.AddHeader("Content-Type", "application/json"); + + EXPECT_TRUE(expectedRequest == this->builder.BuildDeletePrenoteMessageRequest(55)); + } + + TEST_F(ApiRequestBuilderTest, ItBuildsMissedApproachMessage) + { + CurlRequest expectedRequest("http://testurl.com/missed-approaches", CurlRequest::METHOD_POST); + + expectedRequest.AddHeader("Authorization", "Bearer apikey"); + expectedRequest.AddHeader("Accept", "application/json"); + expectedRequest.AddHeader("Content-Type", "application/json"); + + nlohmann::json expectedData = {{"callsign", "BAW123"}}; + expectedRequest.SetBody(expectedData.dump()); + + EXPECT_TRUE(expectedRequest == this->builder.BuildMissedApproachMessage("BAW123")); + } + + TEST_F(ApiRequestBuilderTest, ItBuildsGetAllMetarsMessage) + { + CurlRequest expectedRequest("http://testurl.com/metar", CurlRequest::METHOD_GET); + expectedRequest.AddHeader("Authorization", "Bearer apikey"); + expectedRequest.AddHeader("Accept", "application/json"); + expectedRequest.AddHeader("Content-Type", "application/json"); + + EXPECT_TRUE(expectedRequest == this->builder.BuildGetAllMetarsRequest()); + } + + TEST_F(ApiRequestBuilderTest, ItBuildsMissedApproachAcknowledge) + { + CurlRequest expectedRequest("http://testurl.com/missed-approaches/1", CurlRequest::METHOD_PATCH); + expectedRequest.AddHeader("Authorization", "Bearer apikey"); + expectedRequest.AddHeader("Accept", "application/json"); + expectedRequest.AddHeader("Content-Type", "application/json"); + + nlohmann::json expectedData = {{"remarks", "Some remarks"}}; + expectedRequest.SetBody(expectedData.dump()); + + EXPECT_TRUE(expectedRequest == this->builder.BuildMissedApproachAcknowledgeMessage(1, "Some remarks")); + } +} // namespace UKControllerPluginUtilsTest::Api