3
0

AWSScriptBehaviorS3.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <AzCore/IO/FileIO.h>
  9. #include <AzCore/IO/SystemFile.h>
  10. #include <AzCore/RTTI/BehaviorContext.h>
  11. #include <AzCore/Serialization/EditContext.h>
  12. #include <AzFramework/StringFunc/StringFunc.h>
  13. #include <aws/core/auth/AWSCredentials.h>
  14. #include <aws/s3/S3Client.h>
  15. #include <aws/s3/model/GetObjectRequest.h>
  16. #include <aws/s3/model/HeadObjectRequest.h>
  17. #include <fstream>
  18. #include <Framework/AWSApiRequestJob.h>
  19. #include <ResourceMapping/AWSResourceMappingBus.h>
  20. #include <ScriptCanvas/AWSScriptBehaviorS3.h>
  21. namespace AWSCore
  22. {
  23. void AWSScriptBehaviorS3::Reflect(AZ::ReflectContext* context)
  24. {
  25. if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  26. {
  27. serializeContext->Class<AWSScriptBehaviorS3>()
  28. ->Version(0);
  29. }
  30. if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  31. {
  32. behaviorContext->Class<AWSScriptBehaviorS3>(AWSScriptBehaviorS3Name)
  33. ->Attribute(AZ::Script::Attributes::Category, "AWSCore")
  34. ->Method("GetObject", &AWSScriptBehaviorS3::GetObject,
  35. {{{"Bucket Resource KeyName", "The resource key name of the bucket in resource mapping config file."},
  36. {"Object KeyName", "The object key."},
  37. {"Outfile Name", "Filename where the content will be saved."}}})
  38. ->Method("GetObjectRaw", &AWSScriptBehaviorS3::GetObjectRaw,
  39. {{{"Bucket Name", "The name of the bucket containing the object."},
  40. {"Object KeyName", "The object key."},
  41. {"Region Name", "The region of the bucket located in."},
  42. {"Outfile Name", "Filename where the content will be saved."}}})
  43. ->Method("HeadObject", &AWSScriptBehaviorS3::HeadObject,
  44. {{{"Bucket Resource KeyName", "The resource key name of the bucket in resource mapping config file."},
  45. {"Object KeyName", "The object key."}}})
  46. ->Method("HeadObjectRaw", &AWSScriptBehaviorS3::HeadObjectRaw,
  47. {{{"Bucket Name", "The name of the bucket containing the object."},
  48. {"Object KeyName", "The object key."},
  49. {"Region Name", "The region of the bucket located in."}}});
  50. behaviorContext->EBus<AWSScriptBehaviorS3NotificationBus>("AWSS3BehaviorNotificationBus")
  51. ->Attribute(AZ::Script::Attributes::Category, "AWSCore")
  52. ->Handler<AWSScriptBehaviorS3NotificationBusHandler>();
  53. }
  54. }
  55. void AWSScriptBehaviorS3::GetObject(
  56. const AZStd::string& bucketResourceKey, const AZStd::string& objectKey, const AZStd::string& outFile)
  57. {
  58. AZStd::string requestBucket = "";
  59. AWSResourceMappingRequestBus::BroadcastResult(requestBucket, &AWSResourceMappingRequests::GetResourceNameId, bucketResourceKey);
  60. AZStd::string requestRegion = "";
  61. AWSResourceMappingRequestBus::BroadcastResult(requestRegion, &AWSResourceMappingRequests::GetResourceRegion, bucketResourceKey);
  62. GetObjectRaw(requestBucket, objectKey, requestRegion, outFile);
  63. }
  64. void AWSScriptBehaviorS3::GetObjectRaw(
  65. const AZStd::string& bucket, const AZStd::string& objectKey, const AZStd::string& region, const AZStd::string& outFile)
  66. {
  67. AZStd::string normalizedOutFile = outFile;
  68. if (!ValidateGetObjectRequest(
  69. &AWSScriptBehaviorS3NotificationBus::Events::OnGetObjectError, bucket, objectKey, region, normalizedOutFile))
  70. {
  71. return;
  72. }
  73. using S3GetObjectRequestJob = AWS_API_REQUEST_JOB(S3, GetObject);
  74. S3GetObjectRequestJob::Config config(S3GetObjectRequestJob::GetDefaultConfig());
  75. config.region = region.c_str();
  76. auto job = S3GetObjectRequestJob::Create(
  77. [objectKey](S3GetObjectRequestJob*) // OnSuccess handler
  78. {
  79. AWSScriptBehaviorS3NotificationBus::Broadcast(
  80. &AWSScriptBehaviorS3NotificationBus::Events::OnGetObjectSuccess,
  81. AZStd::string::format("Object %s is downloaded.", objectKey.c_str()));
  82. },
  83. [](S3GetObjectRequestJob* job) // OnError handler
  84. {
  85. Aws::String errorMessage = job->error.GetMessage();
  86. AWSScriptBehaviorS3NotificationBus::Broadcast(
  87. &AWSScriptBehaviorS3NotificationBus::Events::OnGetObjectError, errorMessage.c_str());
  88. },
  89. &config);
  90. job->request.SetBucket(Aws::String(bucket.c_str()));
  91. job->request.SetKey(Aws::String(objectKey.c_str()));
  92. Aws::String outFileName(normalizedOutFile.c_str());
  93. job->request.SetResponseStreamFactory([outFileName]() {
  94. return Aws::New<Aws::FStream>(
  95. AWSScriptBehaviorS3Name, outFileName.c_str(),
  96. std::ios_base::out | std::ios_base::in | std::ios_base::binary | std::ios_base::trunc);
  97. });
  98. job->Start();
  99. }
  100. void AWSScriptBehaviorS3::HeadObject(const AZStd::string& bucketResourceKey, const AZStd::string& objectKey)
  101. {
  102. AZStd::string requestBucket = "";
  103. AWSResourceMappingRequestBus::BroadcastResult(requestBucket, &AWSResourceMappingRequests::GetResourceNameId, bucketResourceKey);
  104. AZStd::string requestRegion = "";
  105. AWSResourceMappingRequestBus::BroadcastResult(requestRegion, &AWSResourceMappingRequests::GetResourceRegion, bucketResourceKey);
  106. HeadObjectRaw(requestBucket, objectKey, requestRegion);
  107. }
  108. void AWSScriptBehaviorS3::HeadObjectRaw(
  109. const AZStd::string& bucket, const AZStd::string& objectKey, const AZStd::string& region)
  110. {
  111. if (!ValidateHeadObjectRequest(&AWSScriptBehaviorS3NotificationBus::Events::OnHeadObjectError, bucket, objectKey, region))
  112. {
  113. return;
  114. }
  115. using S3HeadObjectRequestJob = AWS_API_REQUEST_JOB(S3, HeadObject);
  116. S3HeadObjectRequestJob::Config config(S3HeadObjectRequestJob::GetDefaultConfig());
  117. config.region = region.c_str();
  118. auto job = S3HeadObjectRequestJob::Create(
  119. [objectKey](S3HeadObjectRequestJob*) // OnSuccess handler
  120. {
  121. AWSScriptBehaviorS3NotificationBus::Broadcast(
  122. &AWSScriptBehaviorS3NotificationBus::Events::OnHeadObjectSuccess,
  123. AZStd::string::format("Object %s is found.", objectKey.c_str()));
  124. },
  125. [](S3HeadObjectRequestJob* job) // OnError handler
  126. {
  127. Aws::String errorMessage = job->error.GetMessage();
  128. AWSScriptBehaviorS3NotificationBus::Broadcast(
  129. &AWSScriptBehaviorS3NotificationBus::Events::OnHeadObjectError, errorMessage.c_str());
  130. },
  131. &config);
  132. job->request.SetBucket(Aws::String(bucket.c_str()));
  133. job->request.SetKey(Aws::String(objectKey.c_str()));
  134. job->Start();
  135. }
  136. bool AWSScriptBehaviorS3::ValidateGetObjectRequest(S3NotificationFunctionType notificationFunc,
  137. const AZStd::string& bucket, const AZStd::string& objectKey, const AZStd::string& region, AZStd::string& outFile)
  138. {
  139. if (ValidateHeadObjectRequest(notificationFunc, bucket, objectKey, region))
  140. {
  141. AzFramework::StringFunc::Path::Normalize(outFile);
  142. if (outFile.empty())
  143. {
  144. AZ_Warning(AWSScriptBehaviorS3Name, false, OutputFileIsEmptyErrorMessage);
  145. AWSScriptBehaviorS3NotificationBus::Broadcast(notificationFunc, OutputFileIsEmptyErrorMessage);
  146. return false;
  147. }
  148. char resolvedPath[AZ_MAX_PATH_LEN] = { 0 };
  149. if (!AZ::IO::FileIOBase::GetInstance()->ResolvePath(outFile.c_str(), resolvedPath, AZ_MAX_PATH_LEN))
  150. {
  151. AZ_Warning(AWSScriptBehaviorS3Name, false, OutputFileFailedToResolveErrorMessage);
  152. AWSScriptBehaviorS3NotificationBus::Broadcast(notificationFunc, OutputFileFailedToResolveErrorMessage);
  153. return false;
  154. }
  155. outFile = resolvedPath;
  156. if (AZ::IO::FileIOBase::GetInstance()->IsDirectory(outFile.c_str()))
  157. {
  158. AZ_Warning(AWSScriptBehaviorS3Name, false, OutputFileIsDirectoryErrorMessage);
  159. AWSScriptBehaviorS3NotificationBus::Broadcast(notificationFunc, OutputFileIsDirectoryErrorMessage);
  160. return false;
  161. }
  162. auto lastSeparator = outFile.find_last_of(AZ_CORRECT_FILESYSTEM_SEPARATOR);
  163. if (lastSeparator != AZStd::string::npos)
  164. {
  165. auto parentPath = outFile.substr(0, lastSeparator);
  166. if (!AZ::IO::FileIOBase::GetInstance()->Exists(parentPath.c_str()))
  167. {
  168. AZ_Warning(AWSScriptBehaviorS3Name, false, OutputFileDirectoryNotExistErrorMessage);
  169. AWSScriptBehaviorS3NotificationBus::Broadcast(notificationFunc, OutputFileDirectoryNotExistErrorMessage);
  170. return false;
  171. }
  172. }
  173. if (AZ::IO::FileIOBase::GetInstance()->IsReadOnly(outFile.c_str()))
  174. {
  175. AZ_Warning(AWSScriptBehaviorS3Name, false, OutputFileIsReadOnlyErrorMessage);
  176. AWSScriptBehaviorS3NotificationBus::Broadcast(notificationFunc, OutputFileIsReadOnlyErrorMessage);
  177. return false;
  178. }
  179. return true;
  180. }
  181. return false;
  182. }
  183. bool AWSScriptBehaviorS3::ValidateHeadObjectRequest(S3NotificationFunctionType notificationFunc,
  184. const AZStd::string& bucket, const AZStd::string& objectKey, const AZStd::string& region)
  185. {
  186. if (bucket.empty())
  187. {
  188. AZ_Warning(AWSScriptBehaviorS3Name, false, BucketNameIsEmptyErrorMessage);
  189. AWSScriptBehaviorS3NotificationBus::Broadcast(notificationFunc, BucketNameIsEmptyErrorMessage);
  190. return false;
  191. }
  192. if (objectKey.empty())
  193. {
  194. AZ_Warning(AWSScriptBehaviorS3Name, false, ObjectKeyNameIsEmptyErrorMessage);
  195. AWSScriptBehaviorS3NotificationBus::Broadcast(notificationFunc, ObjectKeyNameIsEmptyErrorMessage);
  196. return false;
  197. }
  198. if (region.empty())
  199. {
  200. AZ_Warning(AWSScriptBehaviorS3Name, false, RegionNameIsEmptyErrorMessage);
  201. AWSScriptBehaviorS3NotificationBus::Broadcast(notificationFunc, RegionNameIsEmptyErrorMessage);
  202. return false;
  203. }
  204. return true;
  205. }
  206. }