Gems/AWSCore/Code/Source/ResourceMapping/AWSResourceMappingManager.cpp (238 lines of code) (raw):
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/IO/Path/Path.h>
#include <AzCore/JSON/schema.h>
#include <AzCore/JSON/prettywriter.h>
#include <AzCore/Serialization/Json/JsonUtils.h>
#include <AzCore/Settings/SettingsRegistry.h>
#include <AzCore/Settings/SettingsRegistryImpl.h>
#include <AzCore/Utils/Utils.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <AWSCoreInternalBus.h>
#include <Configuration/AWSCoreConfiguration.h>
#include <ResourceMapping/AWSResourceMappingConstants.h>
#include <ResourceMapping/AWSResourceMappingManager.h>
#include <ResourceMapping/AWSResourceMappingUtils.h>
namespace AWSCore
{
namespace Platform
{
AZ::IO::Path GetJsonSchemaPath();
}
AWSResourceMappingManager::AWSResourceMappingManager()
: m_status(Status::NotLoaded)
, m_defaultAccountId("")
, m_defaultRegion("")
{
}
void AWSResourceMappingManager::ActivateManager()
{
ReloadConfigFile();
AWSResourceMappingRequestBus::Handler::BusConnect();
}
void AWSResourceMappingManager::DeactivateManager()
{
AWSResourceMappingRequestBus::Handler::BusDisconnect();
ResetResourceMappingsData();
}
AZStd::string AWSResourceMappingManager::GetResourceAttributeErrorMessageByStatus(const AZStd::string& resourceKeyName) const
{
switch (m_status)
{
case Status::NotLoaded:
return AZStd::string::format(ResourceMappingFileNotLoadedErrorMessage, AWSCoreConfiguration::AWSCoreConfigurationFileName);
case Status::Ready:
return AZStd::string::format(ResourceMappingKeyNotFoundErrorMessage, resourceKeyName.c_str());
case Status::Error:
return ResourceMappingFileLoadFailureErrorMessage;
default:
return ManagerUnexpectedStatusErrorMessage;
}
}
AZStd::string AWSResourceMappingManager::GetDefaultAccountId() const
{
if (m_defaultAccountId.empty())
{
AZ_Warning(AWSResourceMappingManagerName, false,
GetResourceAttributeErrorMessageByStatus(ResourceMappingAccountIdKeyName).c_str());
}
return m_defaultAccountId;
}
AZStd::string AWSResourceMappingManager::GetDefaultRegion() const
{
if (m_defaultRegion.empty())
{
AZ_Warning(AWSResourceMappingManagerName, false,
GetResourceAttributeErrorMessageByStatus(ResourceMappingRegionKeyName).c_str());
}
return m_defaultRegion;
}
bool AWSResourceMappingManager::HasResource(const AZStd::string& resourceKeyName) const
{
return m_resourceMappings.contains(resourceKeyName);
}
AZStd::string AWSResourceMappingManager::GetResourceAccountId(const AZStd::string& resourceKeyName) const
{
return GetResourceAttribute([this](auto& attributes) {
if (!attributes.resourceAccountId.empty())
{
return attributes.resourceAccountId;
}
return m_defaultAccountId;
}, resourceKeyName);
}
AZStd::string AWSResourceMappingManager::GetResourceNameId(const AZStd::string& resourceKeyName) const
{
return GetResourceAttribute([](auto& attributes) {
return attributes.resourceNameId;
}, resourceKeyName);
}
AZStd::string AWSResourceMappingManager::GetResourceRegion(const AZStd::string& resourceKeyName) const
{
return GetResourceAttribute([this](auto& attributes) {
if (!attributes.resourceRegion.empty())
{
return attributes.resourceRegion;
}
return m_defaultRegion;
}, resourceKeyName);
}
AZStd::string AWSResourceMappingManager::GetResourceType(const AZStd::string& resourceKeyName) const
{
return GetResourceAttribute([](auto& attributes) {
return attributes.resourceType;
}, resourceKeyName);
}
AZStd::string AWSResourceMappingManager::GetServiceUrlByServiceName(const AZStd::string& serviceName) const
{
return GetServiceUrlByRESTApiIdAndStage(
AZStd::string::format("%s%s", serviceName.c_str(), AWSFeatureGemRESTApiIdKeyNameSuffix),
AZStd::string::format("%s%s", serviceName.c_str(), AWSFeatureGemRESTApiStageKeyNameSuffix));
}
AZStd::string AWSResourceMappingManager::GetServiceUrlByRESTApiIdAndStage(
const AZStd::string& restApiIdKeyName, const AZStd::string& restApiStageKeyName) const
{
AZStd::string serviceRESTApiId = GetResourceNameId(restApiIdKeyName);
AZStd::string serviceRESTApiStage = GetResourceNameId(restApiStageKeyName);
AZStd::string serviceRegion = GetResourceRegion(restApiIdKeyName);
if (serviceRegion != GetResourceRegion(restApiStageKeyName))
{
AZ_Warning(AWSResourceMappingManagerName, false, ResourceMappingRESTApiIdAndStageInconsistentErrorMessage,
restApiIdKeyName.c_str(), restApiStageKeyName.c_str());
return "";
}
AZStd::string serviceRESTApiUrl = AWSResourceMappingUtils::FormatRESTApiUrl(serviceRESTApiId, serviceRegion, serviceRESTApiStage);
AZ_Warning(AWSResourceMappingManagerName, !serviceRESTApiUrl.empty(), ResourceMappingRESTApiInvalidServiceUrlErrorMessage,
serviceRESTApiId.c_str(), serviceRegion.c_str(), serviceRESTApiStage.c_str());
return serviceRESTApiUrl;
}
AZStd::string AWSResourceMappingManager::GetResourceAttribute(
AZStd::function<AZStd::string(const AWSResourceMappingAttributes&)> getAttributeFunction, const AZStd::string& resourceKeyName) const
{
auto iter = m_resourceMappings.find(resourceKeyName);
if (iter != m_resourceMappings.end())
{
return getAttributeFunction(iter->second);
}
AZ_Warning(AWSResourceMappingManagerName, false, GetResourceAttributeErrorMessageByStatus(resourceKeyName).c_str());
return "";
}
AWSResourceMappingManager::Status AWSResourceMappingManager::GetStatus() const
{
return m_status;
}
void AWSResourceMappingManager::ParseJsonDocument(const rapidjson::Document& jsonDocument)
{
m_defaultAccountId = jsonDocument.FindMember(ResourceMappingAccountIdKeyName)->value.GetString();
m_defaultRegion = jsonDocument.FindMember(ResourceMappingRegionKeyName)->value.GetString();
auto resourceMappings = jsonDocument.FindMember(ResourceMappingResourcesKeyName)->value.GetObject();
for (auto mappingIter = resourceMappings.MemberBegin(); mappingIter != resourceMappings.MemberEnd(); ++mappingIter)
{
auto mappingValue = mappingIter->value.GetObject();
if (mappingValue.MemberCount() != 0)
{
AWSResourceMappingAttributes attributes = ParseJsonObjectIntoResourceMappingAttributes(mappingValue);
auto mappingKey = mappingIter->name.GetString();
m_resourceMappings.emplace(AZStd::make_pair(mappingKey, attributes));
}
}
}
AWSResourceMappingManager::AWSResourceMappingAttributes AWSResourceMappingManager::ParseJsonObjectIntoResourceMappingAttributes(
const JsonObject& jsonObject)
{
AWSResourceMappingAttributes attributes;
if (jsonObject.HasMember(ResourceMappingAccountIdKeyName))
{
attributes.resourceAccountId = jsonObject.FindMember(ResourceMappingAccountIdKeyName)->value.GetString();
}
attributes.resourceNameId = jsonObject.FindMember(ResourceMappingNameIdKeyName)->value.GetString();
if (jsonObject.HasMember(ResourceMappingRegionKeyName))
{
attributes.resourceRegion = jsonObject.FindMember(ResourceMappingRegionKeyName)->value.GetString();
}
attributes.resourceType = jsonObject.FindMember(ResourceMappingTypeKeyName)->value.GetString();
return attributes;
}
void AWSResourceMappingManager::ReloadConfigFile(bool reloadConfigFileName)
{
ResetResourceMappingsData();
if (reloadConfigFileName)
{
AWSCoreInternalRequestBus::Broadcast(&AWSCoreInternalRequests::ReloadConfiguration);
}
AZStd::string configJsonPath = "";
AWSCoreInternalRequestBus::BroadcastResult(configJsonPath, &AWSCoreInternalRequests::GetResourceMappingConfigFilePath);
if (configJsonPath.empty())
{
AZ_Warning(AWSResourceMappingManagerName, false, ResourceMappingFileInvalidPathErrorMessage);
return;
}
AzFramework::StringFunc::Path::Normalize(configJsonPath);
auto readJsonOutcome = AZ::JsonSerializationUtils::ReadJsonFile(configJsonPath);
if (readJsonOutcome.IsSuccess())
{
auto jsonDocument = readJsonOutcome.TakeValue();
if (!ValidateJsonDocumentAgainstSchema(jsonDocument))
{
// Failed to satisfy the validation against json schema
m_status = Status::Error;
return;
}
ParseJsonDocument(jsonDocument);
}
else
{
m_status = Status::Error;
AZ_Warning(AWSResourceMappingManagerName, false,
ResourceMappingFileInvalidJsonFormatErrorMessage, readJsonOutcome.GetError().c_str());
return;
}
// Resource mapping config file gets loaded successfully
m_status = Status::Ready;
}
void AWSResourceMappingManager::ResetResourceMappingsData()
{
m_status = Status::NotLoaded;
m_defaultAccountId = "";
m_defaultRegion = "";
m_resourceMappings.clear();
}
bool AWSResourceMappingManager::ValidateJsonDocumentAgainstSchema(const rapidjson::Document& jsonDocument)
{
AZ::IO::Path executablePath = AZ::IO::PathView(AZ::Utils::GetExecutableDirectory());
AZ::IO::Path jsonSchemaPath = Platform::GetJsonSchemaPath();
AZ::Outcome<rapidjson::Document, AZStd::string> readJsonOutcome = AZ::JsonSerializationUtils::ReadJsonFile(jsonSchemaPath.c_str());
if (!readJsonOutcome.IsSuccess() || readJsonOutcome.TakeValue().ObjectEmpty())
{
AZ_Error(AWSResourceMappingManagerName, false, ResourceMappingFileInvalidSchemaErrorMessage);
return false;
}
auto jsonSchema = rapidjson::SchemaDocument(readJsonOutcome.TakeValue());
rapidjson::SchemaValidator validator(jsonSchema);
if (!jsonDocument.Accept(validator))
{
rapidjson::StringBuffer error;
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(error);
validator.GetError().Accept(writer);
AZ_Warning(AWSResourceMappingManagerName, false, ResourceMappingFileInvalidContentErrorMessage, error.GetString());
return false;
}
return true;
}
} // namespace AWSCore