Gems/AWSCore/Code/Source/ScriptCanvas/AWSScriptBehaviorS3.cpp (199 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/FileIO.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzFramework/StringFunc/StringFunc.h>
#include <aws/core/auth/AWSCredentials.h>
#include <aws/s3/S3Client.h>
#include <aws/s3/model/GetObjectRequest.h>
#include <aws/s3/model/HeadObjectRequest.h>
#include <fstream>
#include <Framework/AWSApiRequestJob.h>
#include <ResourceMapping/AWSResourceMappingBus.h>
#include <ScriptCanvas/AWSScriptBehaviorS3.h>
namespace AWSCore
{
void AWSScriptBehaviorS3::Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<AWSScriptBehaviorS3>()
->Version(0);
}
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->Class<AWSScriptBehaviorS3>(AWSScriptBehaviorS3Name)
->Attribute(AZ::Script::Attributes::Category, "AWSCore")
->Method("GetObject", &AWSScriptBehaviorS3::GetObject,
{{{"Bucket Resource KeyName", "The resource key name of the bucket in resource mapping config file."},
{"Object KeyName", "The object key."},
{"Outfile Name", "Filename where the content will be saved."}}})
->Method("GetObjectRaw", &AWSScriptBehaviorS3::GetObjectRaw,
{{{"Bucket Name", "The name of the bucket containing the object."},
{"Object KeyName", "The object key."},
{"Region Name", "The region of the bucket located in."},
{"Outfile Name", "Filename where the content will be saved."}}})
->Method("HeadObject", &AWSScriptBehaviorS3::HeadObject,
{{{"Bucket Resource KeyName", "The resource key name of the bucket in resource mapping config file."},
{"Object KeyName", "The object key."}}})
->Method("HeadObjectRaw", &AWSScriptBehaviorS3::HeadObjectRaw,
{{{"Bucket Name", "The name of the bucket containing the object."},
{"Object KeyName", "The object key."},
{"Region Name", "The region of the bucket located in."}}});
behaviorContext->EBus<AWSScriptBehaviorS3NotificationBus>("AWSS3BehaviorNotificationBus")
->Attribute(AZ::Script::Attributes::Category, "AWSCore")
->Handler<AWSScriptBehaviorS3NotificationBusHandler>();
}
}
void AWSScriptBehaviorS3::GetObject(
const AZStd::string& bucketResourceKey, const AZStd::string& objectKey, const AZStd::string& outFile)
{
AZStd::string requestBucket = "";
AWSResourceMappingRequestBus::BroadcastResult(requestBucket, &AWSResourceMappingRequests::GetResourceNameId, bucketResourceKey);
AZStd::string requestRegion = "";
AWSResourceMappingRequestBus::BroadcastResult(requestRegion, &AWSResourceMappingRequests::GetResourceRegion, bucketResourceKey);
GetObjectRaw(requestBucket, objectKey, requestRegion, outFile);
}
void AWSScriptBehaviorS3::GetObjectRaw(
const AZStd::string& bucket, const AZStd::string& objectKey, const AZStd::string& region, const AZStd::string& outFile)
{
AZStd::string normalizedOutFile = outFile;
if (!ValidateGetObjectRequest(
&AWSScriptBehaviorS3NotificationBus::Events::OnGetObjectError, bucket, objectKey, region, normalizedOutFile))
{
return;
}
using S3GetObjectRequestJob = AWS_API_REQUEST_JOB(S3, GetObject);
S3GetObjectRequestJob::Config config(S3GetObjectRequestJob::GetDefaultConfig());
config.region = region.c_str();
auto job = S3GetObjectRequestJob::Create(
[objectKey](S3GetObjectRequestJob*) // OnSuccess handler
{
AWSScriptBehaviorS3NotificationBus::Broadcast(
&AWSScriptBehaviorS3NotificationBus::Events::OnGetObjectSuccess,
AZStd::string::format("Object %s is downloaded.", objectKey.c_str()));
},
[](S3GetObjectRequestJob* job) // OnError handler
{
Aws::String errorMessage = job->error.GetMessage();
AWSScriptBehaviorS3NotificationBus::Broadcast(
&AWSScriptBehaviorS3NotificationBus::Events::OnGetObjectError, errorMessage.c_str());
},
&config);
job->request.SetBucket(Aws::String(bucket.c_str()));
job->request.SetKey(Aws::String(objectKey.c_str()));
Aws::String outFileName(normalizedOutFile.c_str());
job->request.SetResponseStreamFactory([outFileName]() {
return Aws::New<Aws::FStream>(
AWSScriptBehaviorS3Name, outFileName.c_str(),
std::ios_base::out | std::ios_base::in | std::ios_base::binary | std::ios_base::trunc);
});
job->Start();
}
void AWSScriptBehaviorS3::HeadObject(const AZStd::string& bucketResourceKey, const AZStd::string& objectKey)
{
AZStd::string requestBucket = "";
AWSResourceMappingRequestBus::BroadcastResult(requestBucket, &AWSResourceMappingRequests::GetResourceNameId, bucketResourceKey);
AZStd::string requestRegion = "";
AWSResourceMappingRequestBus::BroadcastResult(requestRegion, &AWSResourceMappingRequests::GetResourceRegion, bucketResourceKey);
HeadObjectRaw(requestBucket, objectKey, requestRegion);
}
void AWSScriptBehaviorS3::HeadObjectRaw(
const AZStd::string& bucket, const AZStd::string& objectKey, const AZStd::string& region)
{
if (!ValidateHeadObjectRequest(&AWSScriptBehaviorS3NotificationBus::Events::OnHeadObjectError, bucket, objectKey, region))
{
return;
}
using S3HeadObjectRequestJob = AWS_API_REQUEST_JOB(S3, HeadObject);
S3HeadObjectRequestJob::Config config(S3HeadObjectRequestJob::GetDefaultConfig());
config.region = region.c_str();
auto job = S3HeadObjectRequestJob::Create(
[objectKey](S3HeadObjectRequestJob*) // OnSuccess handler
{
AWSScriptBehaviorS3NotificationBus::Broadcast(
&AWSScriptBehaviorS3NotificationBus::Events::OnHeadObjectSuccess,
AZStd::string::format("Object %s is found.", objectKey.c_str()));
},
[](S3HeadObjectRequestJob* job) // OnError handler
{
Aws::String errorMessage = job->error.GetMessage();
AWSScriptBehaviorS3NotificationBus::Broadcast(
&AWSScriptBehaviorS3NotificationBus::Events::OnHeadObjectError, errorMessage.c_str());
},
&config);
job->request.SetBucket(Aws::String(bucket.c_str()));
job->request.SetKey(Aws::String(objectKey.c_str()));
job->Start();
}
bool AWSScriptBehaviorS3::ValidateGetObjectRequest(S3NotificationFunctionType notificationFunc,
const AZStd::string& bucket, const AZStd::string& objectKey, const AZStd::string& region, AZStd::string& outFile)
{
if (ValidateHeadObjectRequest(notificationFunc, bucket, objectKey, region))
{
AzFramework::StringFunc::Path::Normalize(outFile);
if (outFile.empty())
{
AZ_Warning(AWSScriptBehaviorS3Name, false, OutputFileIsEmptyErrorMessage);
AWSScriptBehaviorS3NotificationBus::Broadcast(notificationFunc, OutputFileIsEmptyErrorMessage);
return false;
}
char resolvedPath[AZ_MAX_PATH_LEN] = { 0 };
if (!AZ::IO::FileIOBase::GetInstance()->ResolvePath(outFile.c_str(), resolvedPath, AZ_MAX_PATH_LEN))
{
AZ_Warning(AWSScriptBehaviorS3Name, false, OutputFileFailedToResolveErrorMessage);
AWSScriptBehaviorS3NotificationBus::Broadcast(notificationFunc, OutputFileFailedToResolveErrorMessage);
return false;
}
outFile = resolvedPath;
if (AZ::IO::FileIOBase::GetInstance()->IsDirectory(outFile.c_str()))
{
AZ_Warning(AWSScriptBehaviorS3Name, false, OutputFileIsDirectoryErrorMessage);
AWSScriptBehaviorS3NotificationBus::Broadcast(notificationFunc, OutputFileIsDirectoryErrorMessage);
return false;
}
auto lastSeparator = outFile.find_last_of(AZ_CORRECT_FILESYSTEM_SEPARATOR);
if (lastSeparator != AZStd::string::npos)
{
auto parentPath = outFile.substr(0, lastSeparator);
if (!AZ::IO::FileIOBase::GetInstance()->Exists(parentPath.c_str()))
{
AZ_Warning(AWSScriptBehaviorS3Name, false, OutputFileDirectoryNotExistErrorMessage);
AWSScriptBehaviorS3NotificationBus::Broadcast(notificationFunc, OutputFileDirectoryNotExistErrorMessage);
return false;
}
}
if (AZ::IO::FileIOBase::GetInstance()->IsReadOnly(outFile.c_str()))
{
AZ_Warning(AWSScriptBehaviorS3Name, false, OutputFileIsReadOnlyErrorMessage);
AWSScriptBehaviorS3NotificationBus::Broadcast(notificationFunc, OutputFileIsReadOnlyErrorMessage);
return false;
}
return true;
}
return false;
}
bool AWSScriptBehaviorS3::ValidateHeadObjectRequest(S3NotificationFunctionType notificationFunc,
const AZStd::string& bucket, const AZStd::string& objectKey, const AZStd::string& region)
{
if (bucket.empty())
{
AZ_Warning(AWSScriptBehaviorS3Name, false, BucketNameIsEmptyErrorMessage);
AWSScriptBehaviorS3NotificationBus::Broadcast(notificationFunc, BucketNameIsEmptyErrorMessage);
return false;
}
if (objectKey.empty())
{
AZ_Warning(AWSScriptBehaviorS3Name, false, ObjectKeyNameIsEmptyErrorMessage);
AWSScriptBehaviorS3NotificationBus::Broadcast(notificationFunc, ObjectKeyNameIsEmptyErrorMessage);
return false;
}
if (region.empty())
{
AZ_Warning(AWSScriptBehaviorS3Name, false, RegionNameIsEmptyErrorMessage);
AWSScriptBehaviorS3NotificationBus::Broadcast(notificationFunc, RegionNameIsEmptyErrorMessage);
return false;
}
return true;
}
}