files.com 1.1.813 → 1.1.815

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (465) hide show
  1. checksums.yaml +4 -4
  2. data/_VERSION +1 -1
  3. data/doc/Files/APIConnectionError.html +1 -1
  4. data/doc/Files/APIError.html +1 -1
  5. data/doc/Files/AccountAlreadyExistsError.html +1 -1
  6. data/doc/Files/AccountLineItem.html +1 -1
  7. data/doc/Files/AccountOverdueError.html +1 -1
  8. data/doc/Files/Action.html +1 -1
  9. data/doc/Files/ActionLog.html +1 -1
  10. data/doc/Files/ActionNotificationExport.html +1 -1
  11. data/doc/Files/ActionNotificationExportResult.html +1 -1
  12. data/doc/Files/AdditionalAuthenticationRequiredError.html +1 -1
  13. data/doc/Files/AgentNode.html +1 -1
  14. data/doc/Files/AgentNodeConnection.html +1 -1
  15. data/doc/Files/AgentNodeInstance.html +1 -1
  16. data/doc/Files/AgentPushUpdate.html +1 -1
  17. data/doc/Files/AgentPushUpdateBlockedError.html +1 -1
  18. data/doc/Files/AgentUnavailableError.html +1 -1
  19. data/doc/Files/AgentUpgradeRequiredError.html +1 -1
  20. data/doc/Files/AiAssistantPersonality.html +1 -1
  21. data/doc/Files/AiTask.html +1 -1
  22. data/doc/Files/AiTaskCannotBeRunManuallyError.html +1 -1
  23. data/doc/Files/AlreadyCompletedError.html +1 -1
  24. data/doc/Files/Api.html +20 -13
  25. data/doc/Files/ApiClient/RequestLogContext.html +29 -29
  26. data/doc/Files/ApiClient/TransferSettings.html +721 -0
  27. data/doc/Files/ApiClient.html +756 -204
  28. data/doc/Files/ApiKey.html +1 -1
  29. data/doc/Files/ApiKeyIsDisabledError.html +1 -1
  30. data/doc/Files/ApiKeyIsPathRestrictedError.html +1 -1
  31. data/doc/Files/ApiKeyNotFoundError.html +1 -1
  32. data/doc/Files/ApiKeyOnlyForDesktopAppError.html +1 -1
  33. data/doc/Files/ApiKeyOnlyForFileOperationsError.html +1 -1
  34. data/doc/Files/ApiKeyOnlyForMobileAppError.html +1 -1
  35. data/doc/Files/ApiKeyOnlyForOfficeIntegrationError.html +1 -1
  36. data/doc/Files/ApiKeySessionsNotSupportedError.html +1 -1
  37. data/doc/Files/ApiRequestLog.html +1 -1
  38. data/doc/Files/App.html +1 -1
  39. data/doc/Files/As2IncomingMessage.html +1 -1
  40. data/doc/Files/As2OutgoingMessage.html +1 -1
  41. data/doc/Files/As2Partner.html +1 -1
  42. data/doc/Files/As2Station.html +1 -1
  43. data/doc/Files/AttachmentTooLargeError.html +1 -1
  44. data/doc/Files/AuthenticationError.html +1 -1
  45. data/doc/Files/AuthenticationRequiredError.html +1 -1
  46. data/doc/Files/Auto.html +1 -1
  47. data/doc/Files/Automation.html +1 -1
  48. data/doc/Files/AutomationAuthoringSchema.html +1 -1
  49. data/doc/Files/AutomationCannotBeRunManuallyError.html +1 -1
  50. data/doc/Files/AutomationExecutionNode.html +1 -1
  51. data/doc/Files/AutomationLog.html +1 -1
  52. data/doc/Files/AutomationRun.html +1 -1
  53. data/doc/Files/AutomationsUnavailableError.html +1 -1
  54. data/doc/Files/BadRequestError.html +1 -1
  55. data/doc/Files/BandwidthSnapshot.html +1 -1
  56. data/doc/Files/Behavior.html +1 -1
  57. data/doc/Files/BehaviorNotAllowedOnRemoteServerError.html +1 -1
  58. data/doc/Files/BillingInformationHiddenError.html +1 -1
  59. data/doc/Files/BillingPermissionRequiredError.html +1 -1
  60. data/doc/Files/BufferedUploadDisabledForThisDestinationError.html +1 -1
  61. data/doc/Files/Bundle.html +1 -1
  62. data/doc/Files/BundleAction.html +1 -1
  63. data/doc/Files/BundleDownload.html +1 -1
  64. data/doc/Files/BundleMaximumUsesReachedError.html +1 -1
  65. data/doc/Files/BundleNotification.html +1 -1
  66. data/doc/Files/BundleOnlyAllowsPreviewsError.html +1 -1
  67. data/doc/Files/BundleOperationRequiresSubfolderError.html +1 -1
  68. data/doc/Files/BundlePath.html +1 -1
  69. data/doc/Files/BundlePathNotFoundError.html +1 -1
  70. data/doc/Files/BundlePermissionRequiredError.html +1 -1
  71. data/doc/Files/BundleRecipient.html +1 -1
  72. data/doc/Files/BundleRegistration.html +1 -1
  73. data/doc/Files/BundleRegistrationCodeFailedError.html +1 -1
  74. data/doc/Files/BundleRegistrationNotFoundError.html +1 -1
  75. data/doc/Files/CannotAdministerHigherLevelUserError.html +1 -1
  76. data/doc/Files/CannotDownloadDirectoryError.html +1 -1
  77. data/doc/Files/CannotLoginWhileUsingKeyError.html +1 -1
  78. data/doc/Files/CantActForOtherUserError.html +1 -1
  79. data/doc/Files/CantMoveWithMultipleLocationsError.html +1 -1
  80. data/doc/Files/ChatMessage.html +1 -1
  81. data/doc/Files/ChatSession.html +1 -1
  82. data/doc/Files/ChildSiteManagementPolicy.html +1 -1
  83. data/doc/Files/Clickwrap.html +1 -1
  84. data/doc/Files/CodeNotFoundError.html +1 -1
  85. data/doc/Files/ConfigurationLockedPathError.html +1 -1
  86. data/doc/Files/ContactAdminForPasswordChangeHelpError.html +1 -1
  87. data/doc/Files/CouldNotCreateParentError.html +1 -1
  88. data/doc/Files/CustomDomain.html +1 -1
  89. data/doc/Files/DatetimeParseError.html +1 -1
  90. data/doc/Files/DesktopConfigurationProfile.html +1 -1
  91. data/doc/Files/DestinationExistsError.html +1 -1
  92. data/doc/Files/DestinationFolderLimitedError.html +1 -1
  93. data/doc/Files/DestinationParentConflictError.html +1 -1
  94. data/doc/Files/DestinationParentDoesNotExistError.html +1 -1
  95. data/doc/Files/DestinationSameError.html +1 -1
  96. data/doc/Files/DestinationSiteMismatchError.html +1 -1
  97. data/doc/Files/DirectConnectionInfo.html +1 -1
  98. data/doc/Files/DnsRecord.html +1 -1
  99. data/doc/Files/DoesNotSupportSortingError.html +1 -1
  100. data/doc/Files/DuplicateShareRecipientError.html +1 -1
  101. data/doc/Files/EmailIncomingMessage.html +1 -1
  102. data/doc/Files/EmailLog.html +1 -1
  103. data/doc/Files/Error.html +2 -2
  104. data/doc/Files/Errors.html +1 -1
  105. data/doc/Files/EventChannel.html +1 -1
  106. data/doc/Files/EventDeliveryAttempt.html +1 -1
  107. data/doc/Files/EventRecord.html +1 -1
  108. data/doc/Files/EventSubscription.html +1 -1
  109. data/doc/Files/EventTarget.html +1 -1
  110. data/doc/Files/ExavaultApiRequestLog.html +1 -1
  111. data/doc/Files/ExceededRuntimeLimitError.html +1 -1
  112. data/doc/Files/Expectation.html +1 -1
  113. data/doc/Files/ExpectationAlreadyHasOpenWindowError.html +1 -1
  114. data/doc/Files/ExpectationEvaluation.html +1 -1
  115. data/doc/Files/ExpectationIncident.html +1 -1
  116. data/doc/Files/ExpectationNotManualTriggerError.html +1 -1
  117. data/doc/Files/ExpiredPrivateKeyError.html +1 -1
  118. data/doc/Files/ExpiredPublicKeyError.html +1 -1
  119. data/doc/Files/ExportFailureError.html +1 -1
  120. data/doc/Files/ExportNotReadyError.html +1 -1
  121. data/doc/Files/ExternalEvent.html +1 -1
  122. data/doc/Files/FailedToChangePasswordError.html +1 -1
  123. data/doc/Files/File.html +2019 -1515
  124. data/doc/Files/FileAction.html +1 -1
  125. data/doc/Files/FileActionError.html +1 -1
  126. data/doc/Files/FileComment.html +1 -1
  127. data/doc/Files/FileCommentReaction.html +1 -1
  128. data/doc/Files/FileLockedError.html +1 -1
  129. data/doc/Files/FileMigration.html +1 -1
  130. data/doc/Files/FileMigrationLog.html +1 -1
  131. data/doc/Files/FileNotFoundError.html +1 -1
  132. data/doc/Files/FileNotUploadedError.html +1 -1
  133. data/doc/Files/FilePendingProcessingError.html +1 -1
  134. data/doc/Files/FileProcessingErrorError.html +1 -1
  135. data/doc/Files/FileTooBigToDecryptError.html +1 -1
  136. data/doc/Files/FileTooBigToEncryptError.html +1 -1
  137. data/doc/Files/FileUploadNotFoundError.html +1 -1
  138. data/doc/Files/FileUploadPart.html +1 -1
  139. data/doc/Files/FileUploadedToWrongRegionError.html +1 -1
  140. data/doc/Files/FileUtils.html +1 -1
  141. data/doc/Files/FilenameTooLongError.html +1 -1
  142. data/doc/Files/FilesAgentFailedAuthorizationError.html +1 -1
  143. data/doc/Files/FilesNativeStorageOverloadedError.html +1 -1
  144. data/doc/Files/Folder.html +1 -1
  145. data/doc/Files/FolderAdminOrBillingPermissionRequiredError.html +1 -1
  146. data/doc/Files/FolderAdminPermissionRequiredError.html +1 -1
  147. data/doc/Files/FolderLockedError.html +1 -1
  148. data/doc/Files/FolderMustNotBeAFileError.html +1 -1
  149. data/doc/Files/FolderNotEmptyError.html +1 -1
  150. data/doc/Files/FoldersNotAllowedError.html +1 -1
  151. data/doc/Files/FormField.html +1 -1
  152. data/doc/Files/FormFieldSet.html +1 -1
  153. data/doc/Files/FtpActionLog.html +1 -1
  154. data/doc/Files/FullPermissionRequiredError.html +1 -1
  155. data/doc/Files/GpgKey.html +1 -1
  156. data/doc/Files/Group.html +1 -1
  157. data/doc/Files/GroupNotFoundError.html +1 -1
  158. data/doc/Files/GroupUser.html +1 -1
  159. data/doc/Files/History.html +1 -1
  160. data/doc/Files/HistoryExport.html +1 -1
  161. data/doc/Files/HistoryExportResult.html +1 -1
  162. data/doc/Files/HistoryPermissionRequiredError.html +1 -1
  163. data/doc/Files/HistoryUnavailableError.html +1 -1
  164. data/doc/Files/HolidayCalendar.html +1 -1
  165. data/doc/Files/HolidayRegion.html +1 -1
  166. data/doc/Files/Image.html +1 -1
  167. data/doc/Files/InAppAiAssistantUnavailableError.html +1 -1
  168. data/doc/Files/InboundS3Log.html +1 -1
  169. data/doc/Files/InboxNotFoundError.html +1 -1
  170. data/doc/Files/InboxRecipient.html +1 -1
  171. data/doc/Files/InboxRegistration.html +1 -1
  172. data/doc/Files/InboxRegistrationCodeFailedError.html +1 -1
  173. data/doc/Files/InboxUpload.html +1 -1
  174. data/doc/Files/InsufficientPermissionForParamsError.html +1 -1
  175. data/doc/Files/InsufficientPermissionForSiteError.html +1 -1
  176. data/doc/Files/IntegrationCentricProfile.html +1 -1
  177. data/doc/Files/InternalGeneralErrorError.html +1 -1
  178. data/doc/Files/InvalidBodyError.html +1 -1
  179. data/doc/Files/InvalidBundleCodeError.html +1 -1
  180. data/doc/Files/InvalidCredentialsError.html +1 -1
  181. data/doc/Files/InvalidCursorError.html +1 -1
  182. data/doc/Files/InvalidCursorTypeForSortError.html +1 -1
  183. data/doc/Files/InvalidEtagsError.html +1 -1
  184. data/doc/Files/InvalidFileTypeError.html +1 -1
  185. data/doc/Files/InvalidFilenameError.html +1 -1
  186. data/doc/Files/InvalidFilterAliasCombinationError.html +1 -1
  187. data/doc/Files/InvalidFilterFieldError.html +1 -1
  188. data/doc/Files/InvalidFilterParamError.html +1 -1
  189. data/doc/Files/InvalidFilterParamFormatError.html +1 -1
  190. data/doc/Files/InvalidFilterParamValueError.html +1 -1
  191. data/doc/Files/InvalidInputEncodingError.html +1 -1
  192. data/doc/Files/InvalidInterfaceError.html +1 -1
  193. data/doc/Files/InvalidOauthError.html +1 -1
  194. data/doc/Files/InvalidOauthProviderError.html +1 -1
  195. data/doc/Files/InvalidOrExpiredCodeError.html +1 -1
  196. data/doc/Files/InvalidParameterError.html +5 -1
  197. data/doc/Files/InvalidPathError.html +1 -1
  198. data/doc/Files/InvalidPriorityColorError.html +1 -1
  199. data/doc/Files/InvalidRangeError.html +1 -1
  200. data/doc/Files/InvalidReturnToUrlError.html +1 -1
  201. data/doc/Files/InvalidSearchQueryError.html +1 -1
  202. data/doc/Files/InvalidSessionError.html +1 -1
  203. data/doc/Files/InvalidSiteError.html +1 -1
  204. data/doc/Files/InvalidSortFieldError.html +1 -1
  205. data/doc/Files/InvalidSortFilterCombinationError.html +1 -1
  206. data/doc/Files/InvalidUploadOffsetError.html +1 -1
  207. data/doc/Files/InvalidUploadPartGapError.html +1 -1
  208. data/doc/Files/InvalidUploadPartSizeError.html +1 -1
  209. data/doc/Files/InvalidUsernameOrPasswordError.html +1 -1
  210. data/doc/Files/InvalidWorkspaceIdHeaderError.html +1 -1
  211. data/doc/Files/InvalidZipFileError.html +1 -1
  212. data/doc/Files/Invoice.html +1 -1
  213. data/doc/Files/InvoiceLineItem.html +1 -1
  214. data/doc/Files/IpAddress.html +1 -1
  215. data/doc/Files/KeyLifecycleRule.html +1 -1
  216. data/doc/Files/List.html +1 -1
  217. data/doc/Files/Lock.html +1 -1
  218. data/doc/Files/LockOperationBusyError.html +1 -1
  219. data/doc/Files/LockedOutError.html +1 -1
  220. data/doc/Files/LockoutRegionMismatchError.html +1 -1
  221. data/doc/Files/MetadataCategory.html +1 -1
  222. data/doc/Files/MetadataNotSupportedOnRemotesError.html +1 -1
  223. data/doc/Files/MethodNotAllowedError.html +1 -1
  224. data/doc/Files/MigrationInProgressError.html +1 -1
  225. data/doc/Files/MissingParameterError.html +1 -1
  226. data/doc/Files/ModelSaveErrorError.html +1 -1
  227. data/doc/Files/MoverAccessDeniedError.html +1 -1
  228. data/doc/Files/MoverPackageRequiredError.html +1 -1
  229. data/doc/Files/MultipleProcessingErrorsError.html +1 -1
  230. data/doc/Files/MultipleSortParamsNotAllowedError.html +1 -1
  231. data/doc/Files/MustAuthenticateWithApiKeyError.html +1 -1
  232. data/doc/Files/NeedAdminPermissionForInboxError.html +1 -1
  233. data/doc/Files/NestedNotFoundError.html +1 -1
  234. data/doc/Files/NoAccountForSiteError.html +1 -1
  235. data/doc/Files/NoValidInputParamsError.html +1 -1
  236. data/doc/Files/NonAdminsMustQueryByFolderOrPathError.html +1 -1
  237. data/doc/Files/NotAllowedToCreateBundleError.html +1 -1
  238. data/doc/Files/NotAuthenticatedError.html +1 -1
  239. data/doc/Files/NotAuthorizedError.html +1 -1
  240. data/doc/Files/NotEnqueuableSyncError.html +1 -1
  241. data/doc/Files/NotFoundError.html +1 -1
  242. data/doc/Files/NotImplementedError.html +1 -1
  243. data/doc/Files/Notification.html +1 -1
  244. data/doc/Files/OffsetUploadNotAllowedWithMalwareScanningError.html +1 -1
  245. data/doc/Files/OneTimePasswordIncorrectError.html +1 -1
  246. data/doc/Files/OutboundConnectionLog.html +1 -1
  247. data/doc/Files/PartNumberTooLargeError.html +1 -1
  248. data/doc/Files/Partner.html +1 -1
  249. data/doc/Files/PartnerChannel.html +1 -1
  250. data/doc/Files/PartnerChannelTemplate.html +1 -1
  251. data/doc/Files/PartnerConnection.html +1 -1
  252. data/doc/Files/PartnerSite.html +1 -1
  253. data/doc/Files/PartnerSiteRequest.html +1 -1
  254. data/doc/Files/PasswordChangeNotRequiredError.html +1 -1
  255. data/doc/Files/PasswordChangeRequiredError.html +1 -1
  256. data/doc/Files/PathCannotHaveTrailingWhitespaceError.html +1 -1
  257. data/doc/Files/PathTooLongError.html +1 -1
  258. data/doc/Files/PathUtil.html +1 -1
  259. data/doc/Files/Payment.html +1 -1
  260. data/doc/Files/PaymentLineItem.html +1 -1
  261. data/doc/Files/PaymentMethodErrorError.html +1 -1
  262. data/doc/Files/PendingWorkEvent.html +1 -1
  263. data/doc/Files/Permission.html +1 -1
  264. data/doc/Files/PlanNotFoundError.html +1 -1
  265. data/doc/Files/Preview.html +1 -1
  266. data/doc/Files/PreviewOnlyPermissionCannotDownloadError.html +1 -1
  267. data/doc/Files/ProcessingFailureError.html +1 -1
  268. data/doc/Files/PublicHostingRequestLog.html +1 -1
  269. data/doc/Files/PublicIpAddress.html +1 -1
  270. data/doc/Files/PublicKey.html +1 -1
  271. data/doc/Files/RateLimitedError.html +1 -1
  272. data/doc/Files/ReadOnlySessionError.html +1 -1
  273. data/doc/Files/ReadPermissionRequiredError.html +1 -1
  274. data/doc/Files/ReauthenticationFailedError.html +1 -1
  275. data/doc/Files/ReauthenticationFailedFinalError.html +1 -1
  276. data/doc/Files/ReauthenticationNeededActionError.html +1 -1
  277. data/doc/Files/ReauthenticationNeededFieldsError.html +1 -1
  278. data/doc/Files/ReauthenticationRateLimitedError.html +1 -1
  279. data/doc/Files/RecaptchaFailedError.html +1 -1
  280. data/doc/Files/RecipientAlreadySharedError.html +1 -1
  281. data/doc/Files/RemoteBandwidthSnapshot.html +1 -1
  282. data/doc/Files/RemoteDesktopDebugLoggingDisabledError.html +1 -1
  283. data/doc/Files/RemoteEntryReadOnlyError.html +1 -1
  284. data/doc/Files/RemoteMountBackend.html +1 -1
  285. data/doc/Files/RemoteServer.html +1 -1
  286. data/doc/Files/RemoteServerConfigurationFile.html +1 -1
  287. data/doc/Files/RemoteServerCredential.html +1 -1
  288. data/doc/Files/RemoteServerErrorError.html +1 -1
  289. data/doc/Files/Request.html +1 -1
  290. data/doc/Files/RequestBodyTooLargeError.html +1 -1
  291. data/doc/Files/RequestParamsContainInvalidCharacterError.html +1 -1
  292. data/doc/Files/RequestParamsInvalidError.html +1 -1
  293. data/doc/Files/RequestParamsRequiredError.html +1 -1
  294. data/doc/Files/ResourceBelongsToParentSiteError.html +1 -1
  295. data/doc/Files/ResourceLockedError.html +1 -1
  296. data/doc/Files/Response.html +1 -1
  297. data/doc/Files/Restore.html +1 -1
  298. data/doc/Files/RootFolderBehaviorSiteAdminRequiredError.html +1 -1
  299. data/doc/Files/RootFolderBehaviorSkipSiteAdminRequiredError.html +1 -1
  300. data/doc/Files/Schedule.html +1 -1
  301. data/doc/Files/ScheduledExport.html +1 -1
  302. data/doc/Files/ScimLog.html +1 -1
  303. data/doc/Files/SearchAllOnChildPathError.html +1 -1
  304. data/doc/Files/SearchUnavailableError.html +1 -1
  305. data/doc/Files/Secret.html +1 -1
  306. data/doc/Files/SelfManagedRequiredError.html +1 -1
  307. data/doc/Files/ServiceUnavailableError.html +1 -1
  308. data/doc/Files/Session.html +1 -1
  309. data/doc/Files/SettingsChange.html +1 -1
  310. data/doc/Files/SftpActionLog.html +1 -1
  311. data/doc/Files/SftpHostKey.html +1 -1
  312. data/doc/Files/ShareGroup.html +1 -1
  313. data/doc/Files/ShareGroupMember.html +1 -1
  314. data/doc/Files/SiemHttpDestination.html +1 -1
  315. data/doc/Files/SiemHttpDestinationEvent.html +1 -1
  316. data/doc/Files/Site.html +911 -731
  317. data/doc/Files/SiteAdminOrPartnerAdminPermissionRequiredError.html +1 -1
  318. data/doc/Files/SiteAdminOrWorkspaceAdminOrFolderAdminPermissionRequiredError.html +1 -1
  319. data/doc/Files/SiteAdminOrWorkspaceAdminOrPartnerAdminOrFolderAdminPermissionRequiredError.html +1 -1
  320. data/doc/Files/SiteAdminOrWorkspaceAdminOrPartnerAdminPermissionRequiredError.html +1 -1
  321. data/doc/Files/SiteAdminOrWorkspaceAdminPermissionRequiredError.html +1 -1
  322. data/doc/Files/SiteAdminRequiredError.html +1 -1
  323. data/doc/Files/SiteConfigurationError.html +1 -1
  324. data/doc/Files/SiteDisabledError.html +1 -1
  325. data/doc/Files/SiteFilesAreImmutableError.html +1 -1
  326. data/doc/Files/SiteNotFoundError.html +1 -1
  327. data/doc/Files/SiteSubdomainRedirect.html +1 -1
  328. data/doc/Files/SiteWasRemovedError.html +1 -1
  329. data/doc/Files/SizableIO.html +1 -1
  330. data/doc/Files/Snapshot.html +1 -1
  331. data/doc/Files/SsoEvent.html +1 -1
  332. data/doc/Files/SsoStrategy.html +1 -1
  333. data/doc/Files/Status.html +1 -1
  334. data/doc/Files/Style.html +1 -1
  335. data/doc/Files/SubfolderLockedError.html +1 -1
  336. data/doc/Files/Sync.html +1 -1
  337. data/doc/Files/SyncInProgressError.html +1 -1
  338. data/doc/Files/SyncLog.html +1 -1
  339. data/doc/Files/SyncRun.html +1 -1
  340. data/doc/Files/SyncRunLiveTransfer.html +1 -1
  341. data/doc/Files/SystemProfiler.html +1 -1
  342. data/doc/Files/TooManyConcurrentLoginsError.html +1 -1
  343. data/doc/Files/TooManyConcurrentRequestsError.html +1 -1
  344. data/doc/Files/TooManyLoginAttemptsError.html +1 -1
  345. data/doc/Files/TooManyRequestsError.html +1 -1
  346. data/doc/Files/TooManySharesError.html +1 -1
  347. data/doc/Files/Transfer/BufferLimitError.html +175 -0
  348. data/doc/Files/Transfer/ByteBudget/Reservation.html +301 -0
  349. data/doc/Files/Transfer/ByteBudget.html +748 -0
  350. data/doc/Files/Transfer/CancelledError.html +235 -0
  351. data/doc/Files/Transfer/Checkpoint.html +1371 -0
  352. data/doc/Files/Transfer/CheckpointError.html +173 -0
  353. data/doc/Files/Transfer/ClientTransport/ClientResponse.html +661 -0
  354. data/doc/Files/Transfer/ClientTransport/Reader.html +530 -0
  355. data/doc/Files/Transfer/ClientTransport.html +675 -0
  356. data/doc/Files/Transfer/Clock.html +189 -0
  357. data/doc/Files/Transfer/Control.html +667 -0
  358. data/doc/Files/Transfer/Controller/Attempt.html +465 -0
  359. data/doc/Files/Transfer/Controller.html +1166 -0
  360. data/doc/Files/Transfer/Coordinator/Timing.html +568 -0
  361. data/doc/Files/Transfer/Coordinator.html +534 -0
  362. data/doc/Files/Transfer/DownloadCoordinator/FileSink.html +976 -0
  363. data/doc/Files/Transfer/DownloadCoordinator/IoSink.html +1168 -0
  364. data/doc/Files/Transfer/DownloadCoordinator/Piece.html +875 -0
  365. data/doc/Files/Transfer/DownloadCoordinator.html +728 -0
  366. data/doc/Files/Transfer/FileSlots.html +480 -0
  367. data/doc/Files/Transfer/FrontendResponseError.html +235 -0
  368. data/doc/Files/Transfer/Holds.html +505 -0
  369. data/doc/Files/Transfer/HttpTransport.html +579 -0
  370. data/doc/Files/Transfer/Job.html +2338 -0
  371. data/doc/Files/Transfer/Options.html +924 -0
  372. data/doc/Files/Transfer/PausedError.html +319 -0
  373. data/doc/Files/Transfer/Policy.html +1642 -0
  374. data/doc/Files/Transfer/RequestPermits.html +160 -0
  375. data/doc/Files/Transfer/SourceChangedError.html +235 -0
  376. data/doc/Files/Transfer/StorageResponse.html +546 -0
  377. data/doc/Files/Transfer/StorageRetry.html +1098 -0
  378. data/doc/Files/Transfer/UploadCoordinator/Grant.html +1048 -0
  379. data/doc/Files/Transfer/UploadCoordinator/Part.html +1048 -0
  380. data/doc/Files/Transfer/UploadCoordinator/Region.html +295 -0
  381. data/doc/Files/Transfer/UploadCoordinator/Segments.html +299 -0
  382. data/doc/Files/Transfer/UploadCoordinator/Source.html +725 -0
  383. data/doc/Files/Transfer/UploadCoordinator/StreamSource.html +664 -0
  384. data/doc/Files/Transfer/UploadCoordinator.html +546 -0
  385. data/doc/Files/Transfer/UploadPlan.html +522 -0
  386. data/doc/Files/Transfer/VersionChangedError.html +235 -0
  387. data/doc/Files/Transfer.html +134 -0
  388. data/doc/Files/TrialExpiredError.html +1 -1
  389. data/doc/Files/TrialLockedError.html +1 -1
  390. data/doc/Files/TwoFactorAuthenticationCodeAlreadySentError.html +1 -1
  391. data/doc/Files/TwoFactorAuthenticationCountryBlacklistedError.html +1 -1
  392. data/doc/Files/TwoFactorAuthenticationErrorError.html +1 -1
  393. data/doc/Files/TwoFactorAuthenticationGeneralErrorError.html +1 -1
  394. data/doc/Files/TwoFactorAuthenticationMethodUnsupportedErrorError.html +1 -1
  395. data/doc/Files/TwoFactorAuthenticationRequiredError.html +1 -1
  396. data/doc/Files/TwoFactorAuthenticationSetupExpiredError.html +1 -1
  397. data/doc/Files/TwoFactorAuthenticationUnsubscribedRecipientError.html +1 -1
  398. data/doc/Files/URI.html +1 -1
  399. data/doc/Files/UnrecognizedSortIndexError.html +1 -1
  400. data/doc/Files/UnsupportedCurrencyError.html +1 -1
  401. data/doc/Files/UnsupportedHttpResponseFormatError.html +1 -1
  402. data/doc/Files/UnsupportedMediaTypeError.html +1 -1
  403. data/doc/Files/UpdatesNotAllowedForRemotesError.html +1 -1
  404. data/doc/Files/UploadsUnavailableError.html +1 -1
  405. data/doc/Files/UsageByTopLevelDir.html +1 -1
  406. data/doc/Files/UsageDailySnapshot.html +1 -1
  407. data/doc/Files/UsageSnapshot.html +1 -1
  408. data/doc/Files/User.html +597 -386
  409. data/doc/Files/UserAdditionalEmailRecipient.html +1 -1
  410. data/doc/Files/UserCipherUse.html +1 -1
  411. data/doc/Files/UserIdInvalidError.html +1 -1
  412. data/doc/Files/UserIdOnUserEndpointError.html +1 -1
  413. data/doc/Files/UserIdWithoutSiteAdminError.html +1 -1
  414. data/doc/Files/UserLifecycleRule.html +1 -1
  415. data/doc/Files/UserNotFoundError.html +1 -1
  416. data/doc/Files/UserRequest.html +1 -1
  417. data/doc/Files/UserRequestsEnabledRequiredError.html +1 -1
  418. data/doc/Files/UserRequiredError.html +1 -1
  419. data/doc/Files/UserSecurityEvent.html +1 -1
  420. data/doc/Files/UserSftpClientUse.html +1 -1
  421. data/doc/Files/Util/LogSettings.html +1 -1
  422. data/doc/Files/Util.html +1 -1
  423. data/doc/Files/WebDavActionLog.html +1 -1
  424. data/doc/Files/WebhookTest.html +1 -1
  425. data/doc/Files/Workspace.html +1 -1
  426. data/doc/Files/WriteAndBundlePermissionRequiredError.html +1 -1
  427. data/doc/Files/WritePermissionRequiredError.html +1 -1
  428. data/doc/Files/ZipListEntry.html +1 -1
  429. data/doc/Files.html +116 -116
  430. data/doc/_index.html +314 -4
  431. data/doc/class_list.html +1 -1
  432. data/doc/file.README.html +1 -1
  433. data/doc/index.html +1 -1
  434. data/doc/method_list.html +5368 -3048
  435. data/doc/top-level-namespace.html +1 -1
  436. data/docs/file.md +346 -0
  437. data/docs/site.md +10 -0
  438. data/docs/user.md +8 -0
  439. data/lib/files.com/api.rb +6 -1
  440. data/lib/files.com/api_client.rb +220 -32
  441. data/lib/files.com/models/file.rb +124 -17
  442. data/lib/files.com/models/site.rb +16 -0
  443. data/lib/files.com/models/user.rb +19 -0
  444. data/lib/files.com/transfer/budgets.rb +123 -0
  445. data/lib/files.com/transfer/checkpoint.rb +113 -0
  446. data/lib/files.com/transfer/clock.rb +14 -0
  447. data/lib/files.com/transfer/controller.rb +549 -0
  448. data/lib/files.com/transfer/download_coordinator.rb +906 -0
  449. data/lib/files.com/transfer/errors.rb +100 -0
  450. data/lib/files.com/transfer/http_transport.rb +337 -0
  451. data/lib/files.com/transfer/job.rb +778 -0
  452. data/lib/files.com/transfer/options.rb +126 -0
  453. data/lib/files.com/transfer/policy.rb +91 -0
  454. data/lib/files.com/transfer/storage_retry.rb +169 -0
  455. data/lib/files.com/transfer/upload_coordinator.rb +657 -0
  456. data/lib/files.com/transfer/upload_plan.rb +157 -0
  457. data/lib/files.com.rb +13 -0
  458. data/spec/lib/api_client_spec.rb +125 -3
  459. data/spec/models/file_transfer_spec.rb +53 -4
  460. data/spec/support/transfer_server.rb +113 -14
  461. data/spec/transfer/adaptive_download_spec.rb +1102 -0
  462. data/spec/transfer/adaptive_upload_spec.rb +1602 -0
  463. data/spec/transfer/concurrency_controller_spec.rb +382 -0
  464. data/spec/workspace_id_spec.rb +19 -14
  465. metadata +59 -1
@@ -0,0 +1,778 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Files
4
+ module Transfer
5
+ # What adaptive transfers share: their Options, one Controller for each kind of destination with the request limit
6
+ # it sets, the request permits, the buffer budget, the file slots, how long a throttle holds each destination back
7
+ # (Holds), whichever transfer, transport or controller met it, and the worker threads that send content requests.
8
+ #
9
+ # A Hash or true given as transfer: makes a Job for that one call, closed before it returns. Pass a Job instead to
10
+ # share it between calls, from any threads: transfers to the same storage then share one controller and what it
11
+ # learned, every transfer counts against one buffer_bytes and one request limit (a fixed concurrency, or
12
+ # max_concurrency, or 64), and at most max_files run at once. Close it when they are done, or use Job.open with a
13
+ # block.
14
+ class Job
15
+ CLOSE_WAIT_SECONDS = 5
16
+ MAX_NOTES = 64
17
+ # The longest a waiting transfer sleeps before it looks at its deadlines again, so a long Retry-After is waited
18
+ # out in steps, and a cancellation or pause still ends the wait at once.
19
+ MAX_WAIT_SECONDS = 60.0
20
+
21
+ attr_reader :options, :budget, :transport
22
+
23
+ def self.open(**options)
24
+ job = new(**options)
25
+ return job unless block_given?
26
+
27
+ finished = false
28
+ begin
29
+ result = yield job
30
+ finished = true
31
+ result
32
+ ensure
33
+ job.close(strict: finished)
34
+ end
35
+ end
36
+
37
+ # Runs the block with the Job transfer names: transfer itself, or a new Job closed afterwards.
38
+ def self.using(transfer)
39
+ return yield(transfer) if transfer.is_a?(Job)
40
+
41
+ job = new(Options.from(transfer))
42
+ finished = false
43
+ begin
44
+ result = yield job
45
+ finished = true
46
+ result
47
+ ensure
48
+ job.close(strict: finished)
49
+ end
50
+ end
51
+
52
+ def initialize(options = nil, **keywords)
53
+ @options = Options.from(options || keywords)
54
+ @lock = Mutex.new
55
+ @changed = ConditionVariable.new
56
+ @work_ready = ConditionVariable.new
57
+ @controllers = {}
58
+ @budget = ByteBudget.new(@options.buffer_bytes)
59
+ @files = FileSlots.new(@options.max_files)
60
+ @requests = RequestPermits.new(@options.automatic? ? (@options.max_concurrency || Policy::DEFAULT_MAX) : @options.concurrency)
61
+ @holds = Holds.new
62
+ @running = 0
63
+ @tasks = []
64
+ @workers = []
65
+ @idle = 0
66
+ @closed = false
67
+ @transport = HttpTransport.new
68
+ @client_transports = {}.compare_by_identity
69
+ @notes = []
70
+ @started_at = Clock.now
71
+ @accepted_bytes = 0
72
+ @retries = 0
73
+ @options.control&.subscribe(self)
74
+ end
75
+
76
+ def control
77
+ @options.control
78
+ end
79
+
80
+ def closed?
81
+ @closed
82
+ end
83
+
84
+ # Bytes held right now for parts waiting to be sent or written.
85
+ def held_bytes
86
+ @lock.synchronize { @budget.held }
87
+ end
88
+
89
+ # A snapshot of how the job's transfers ran:
90
+ #
91
+ # accepted_bytes:: Bytes storage acknowledged (uploads) or that were received for writing (downloads), each
92
+ # counted once however often its request was sent.
93
+ # elapsed_seconds:: Seconds since the job began.
94
+ # goodput_bytes_per_second:: accepted_bytes over elapsed_seconds.
95
+ # retries:: Requests sent again after a failure, a throttle or an expired URL.
96
+ # requests:: Content requests in flight now, of at most request_limit.
97
+ # held_bytes:: Bytes held now for parts waiting to be sent or written.
98
+ # controllers:: Each controller's key, policy, phase, limit, attempts admitted against it (admitted) and on the
99
+ # network (inflight), epoch, judged_windows and ineligible_windows (windows closed without judging,
100
+ # because too few requests were on the network) and decisions.
101
+ # throttled:: Each origin a throttle's Retry-After holds back now (Holds), with the seconds left.
102
+ # notes:: How transfers ran, such as a caller's client carrying them one request at a time.
103
+ #
104
+ # It holds no URLs or credentials: a controller's key names only the kind of destination and its host, and a
105
+ # throttled origin only its scheme, host and port.
106
+ def diagnostics
107
+ @lock.synchronize do
108
+ elapsed = Clock.now - @started_at
109
+ {
110
+ accepted_bytes: @accepted_bytes,
111
+ elapsed_seconds: elapsed,
112
+ goodput_bytes_per_second: elapsed.positive? ? @accepted_bytes / elapsed : 0.0,
113
+ retries: @retries,
114
+ requests: @requests.used,
115
+ request_limit: @requests.limit,
116
+ controllers: @controllers.map { |key, controller| controller.snapshot.merge(key: key, decisions: controller.decisions) },
117
+ held_bytes: @budget.held,
118
+ throttled: @holds.to_h,
119
+ notes: @notes.dup
120
+ }
121
+ end
122
+ end
123
+
124
+ # Stops the job: transfers still running on it stop with an error, and it waits for them and the requests they
125
+ # have in flight, at most five seconds in all. Returns true once nothing is left running. When something still is,
126
+ # it raises Files::Error; with strict: false, as when a transfer on its own Job has already failed, it returns
127
+ # false instead and what is left finishes in the background.
128
+ def close(strict: true)
129
+ deadline = Clock.now + CLOSE_WAIT_SECONDS
130
+ workers = @lock.synchronize do
131
+ unless @closed
132
+ @closed = true
133
+ @work_ready.broadcast
134
+ @changed.broadcast
135
+ end
136
+ loop do
137
+ remaining = deadline - Clock.now
138
+ break if @running.zero? || !remaining.positive?
139
+
140
+ @changed.wait(@lock, remaining)
141
+ end
142
+ @workers.dup
143
+ end
144
+ @options.control&.unsubscribe(self)
145
+ workers.each { |worker| worker.join([ deadline - Clock.now, 0 ].max) }
146
+ live = workers.count(&:alive?)
147
+ running = @lock.synchronize { @running }
148
+ return true if live.zero? && running.zero?
149
+ return false unless strict
150
+
151
+ raise Files::Error, "The transfer job closed with #{running} transfers and #{live} requests still running after #{CLOSE_WAIT_SECONDS} seconds"
152
+ end
153
+
154
+ # Called by the control after it changes.
155
+ def wake
156
+ @lock.synchronize { @changed.broadcast }
157
+ end
158
+
159
+ def synchronize(&block)
160
+ @lock.synchronize(&block)
161
+ end
162
+
163
+ # With the lock held: waits for a change, at most seconds when given, and never more than MAX_WAIT_SECONDS at a
164
+ # time when given; the caller looks at its deadline again after it.
165
+ def wait(seconds = nil)
166
+ @changed.wait(@lock, seconds && [ seconds, MAX_WAIT_SECONDS ].min)
167
+ end
168
+
169
+ # With the lock held: wakes everything waiting for a change.
170
+ def signal
171
+ @changed.broadcast
172
+ end
173
+
174
+ # Runs the block under the lock and wakes everything waiting.
175
+ def post
176
+ @lock.synchronize do
177
+ yield
178
+ @changed.broadcast
179
+ end
180
+ end
181
+
182
+ # With the lock held: the controller for key, made from the policy the block returns the first time.
183
+ def controller(key)
184
+ @controllers[key] ||= Controller.new(yield)
185
+ end
186
+
187
+ # The transport for a transfer's content requests through client, which the caller's thread resolved: the job's
188
+ # own only when client is the SDK's own kind with its default connection unchanged and the job's own proxy
189
+ # (HttpTransport#compatible?), otherwise client itself, one request at a time. A download refuses, with
190
+ # BufferLimitError, a client whose adapter is not one it relies on to stream a response (ClientTransport.streams?),
191
+ # before it sends a content request or writes anything.
192
+ def transport_for(client, download: false)
193
+ return @transport if @transport.compatible?(client)
194
+ raise BufferLimitError, ClientTransport::NOT_STREAMING if download && !ClientTransport.streams?(client)
195
+
196
+ @lock.synchronize do
197
+ @client_transports[client] ||= begin
198
+ note("A caller's client with its own connection or request handling sends this job's content requests, one request at a time")
199
+ ClientTransport.new(client)
200
+ end
201
+ end
202
+ end
203
+
204
+ # With the lock held.
205
+ def note(message)
206
+ @notes.shift if @notes.size >= MAX_NOTES
207
+ @notes << message.dup.freeze
208
+ end
209
+
210
+ # Waits for a file slot: returns :running once the caller has one, or control's :cancelled or :paused. Nothing of
211
+ # the transfer is opened before it.
212
+ def acquire_file(control)
213
+ acquire(@files, control) { @running += 1 }
214
+ end
215
+
216
+ def release_file
217
+ post do
218
+ @files.give
219
+ @running -= 1
220
+ end
221
+ end
222
+
223
+ # Waits, on a transfer's own thread, for a request permit for a request that thread sends itself to url, once no
224
+ # throttle holds url's destination back: returns :running once it holds one, or control's :cancelled or :paused as
225
+ # soon as it stops. Give it back with release_request.
226
+ def acquire_request(control, url)
227
+ acquire(@requests, control, Holds.destination(url))
228
+ end
229
+
230
+ # Gives back a permit acquire_request took. A throttle the request met, when seconds is given, holds url's
231
+ # destination back first, under the same lock and in the same order as a worker's request (Coordinator#launch),
232
+ # so nothing waiting for the permit is sent before that hold.
233
+ def release_request(url = nil, seconds = nil)
234
+ post do
235
+ hold(url, seconds) if seconds
236
+ @requests.give
237
+ end
238
+ end
239
+
240
+ # With the lock held: takes a request permit for a worker's request when one is free.
241
+ def take_request
242
+ @requests.take
243
+ end
244
+
245
+ # With the lock held: gives a worker's request permit back and wakes everything waiting for one.
246
+ def give_request
247
+ @requests.give
248
+ @changed.broadcast
249
+ end
250
+
251
+ # With the lock held: holds url's destination back for seconds, a throttle's Retry-After, from every new request
252
+ # and retry of the job's transfers, unless it already is for longer, and wakes everything waiting.
253
+ def hold(url, seconds)
254
+ @holds.hold(Holds.destination(url), seconds)
255
+ @changed.broadcast
256
+ end
257
+
258
+ # With the lock held: the monotonic time until which a throttle holds url's destination back, or nil.
259
+ def held_until(url)
260
+ @holds.deadline(Holds.destination(url))
261
+ end
262
+
263
+ # With the lock held: counts bytes accepted for the first time.
264
+ def count_accepted(bytes)
265
+ @accepted_bytes += bytes
266
+ end
267
+
268
+ # With the lock held: counts a request about to be sent again.
269
+ def count_retry
270
+ @retries += 1
271
+ end
272
+
273
+ # Takes one of slots, once no throttle holds destination, when given, back; a cancellation or pause ends the wait.
274
+ private def acquire(slots, control, destination = nil)
275
+ @lock.synchronize do
276
+ loop do
277
+ raise Files::Error, "The transfer job is closed" if @closed
278
+
279
+ state = control&.state || :running
280
+ return state unless state == :running
281
+
282
+ held = destination && @holds.deadline(destination)
283
+ if held.nil? && slots.take
284
+ yield if block_given?
285
+ return :running
286
+ end
287
+ wait(held && [ held - Clock.now, 0.001 ].max)
288
+ end
289
+ end
290
+ end
291
+
292
+ # With the lock held: runs work on a worker thread, which passes it the connections that thread owns.
293
+ def submit(&work)
294
+ raise Files::Error, "The transfer job is closed" if @closed
295
+
296
+ @tasks << work
297
+ if @tasks.size > @idle
298
+ spawn_worker
299
+ else
300
+ @work_ready.signal
301
+ end
302
+ end
303
+
304
+ private def spawn_worker
305
+ @workers << Thread.new do
306
+ Thread.current.report_on_exception = false
307
+ connections = {}
308
+ begin
309
+ while (work = next_task)
310
+ work.call(connections)
311
+ end
312
+ ensure
313
+ @transport.close_all(connections)
314
+ end
315
+ end
316
+ end
317
+
318
+ private def next_task
319
+ @lock.synchronize do
320
+ @idle += 1
321
+ @work_ready.wait(@lock) while @tasks.empty? && !@closed
322
+ @idle -= 1
323
+ @tasks.shift
324
+ end
325
+ end
326
+ end
327
+
328
+ # The loop one transfer runs on its caller's thread. It takes a file slot before it opens anything, plans work and
329
+ # calls the API there, with the context it fixed as it began (api_context), admits each part or range when it is
330
+ # ready and its controller's limit, its own cap, any throttle holding its destination back (Holds), the job's
331
+ # request permits and the buffer budget allow, hands the request to a worker and settles what comes back:
332
+ # acknowledgements, retries (each a new admission, its buffer kept), renewals and failures. Each attempt goes to
333
+ # the target fixed as it was admitted (request_target), whatever is renewed after. The transport tells the
334
+ # controller when each request's send starts and when it is answered, and the worker gives back its permit as the
335
+ # request ends, publishing a throttle's Retry-After for that target's destination to the job under the same lock,
336
+ # so nothing waiting for the permit is sent before the hold. Bytes held for an item are given back to the buffer
337
+ # budget only when they are dropped. Callbacks and writes to the caller's
338
+ # destination happen only on the caller's thread. The first failure, cancellation or pause, in the order they
339
+ # happened, whichever thread saw it, stops admissions; it then waits for the requests in flight, releases everything
340
+ # it holds and raises that first one, finalizing nothing. An error the caller's thread meets itself, from opening
341
+ # the source or destination and checking a resume's through an API request's, a callback's or a refused request's,
342
+ # is ordered the same way: recorded as SDK code there recognizes it (stop!), and in any case before it is raised
343
+ # (arbitrate), so a cancellation or pause made after it never replaces it, and one made before it is raised instead.
344
+ class Coordinator
345
+ # What a transport calls as a request's send starts, as its response headers arrive and once its response body
346
+ # has been read and checked. An upload's attempt is answered as its response headers arrive, after its own body
347
+ # went; a download's (through_body) once its whole body has arrived and been checked, before any of it is written.
348
+ Timing = Struct.new(:controller, :attempt, :through_body) do
349
+ def sending
350
+ controller.sending(attempt)
351
+ end
352
+
353
+ def headers
354
+ controller.answered(attempt) unless through_body
355
+ end
356
+
357
+ def body
358
+ controller.answered(attempt)
359
+ end
360
+ end
361
+
362
+ # The API options every API request of one transfer starts with, resolved on the caller's thread as
363
+ # Api.send_request and ApiClient#execute_request would resolve them for a request sent now, from frozen copies of
364
+ # the caller's options: the client (options[:client] or the thread's active client), the credential (an options
365
+ # API key; otherwise an options session ID or session, saved first if it has no ID, or Files.session_id, when not
366
+ # empty; otherwise Files.api_key, or none at all), the origin (options[:base_url] or Files.base_url), the
367
+ # region_http_pairs (the options' own, checked, or the client's), the workspace (options[:workspace_id], or the
368
+ # client's, "" for none) and, as request_settings, copies of the library-wide default headers, language, app info,
369
+ # timeouts and region policy and handler (ApiClient.transfer_settings). A later change to the caller's options,
370
+ # even in place, to the thread's client or to any of those settings cannot change what the transfer's API requests
371
+ # send; adopt_context carries what an API request resolved, such as a regional origin, to those after it.
372
+ def self.api_context(options)
373
+ context = (options || {}).to_h.transform_values { |value| Checkpoint.frozen(value) }
374
+ if (session = context.delete(:session))
375
+ session.save unless session.id
376
+ context[:session_id] = Checkpoint.frozen(session.id)
377
+ end
378
+ client = context[:client] = Files::File.client(context)
379
+ if context[:api_key].nil?
380
+ session_id = context[:session_id].nil? ? Checkpoint.frozen(Files.session_id) : context[:session_id]
381
+ if session_id.nil? || session_id == ""
382
+ context[:api_key] = Checkpoint.frozen(Files.api_key)
383
+ else
384
+ context[:session_id] = session_id
385
+ end
386
+ end
387
+ context[:base_url] ||= Checkpoint.frozen(Files.base_url)
388
+ if context[:region_http_pairs]
389
+ context[:region_http_pairs] = Util.checked_region_http_pairs(context[:region_http_pairs])
390
+ elsif client.respond_to?(:region_http_pairs)
391
+ context[:region_http_pairs] = client.region_http_pairs
392
+ end
393
+ context[:workspace_id] = client.resolved_workspace_id if context[:workspace_id].nil? && client.respond_to?(:resolved_workspace_id)
394
+ context[:request_settings] = ApiClient.transfer_settings
395
+ context
396
+ end
397
+
398
+ def initialize(job)
399
+ @job = job
400
+ @control = job.control
401
+ @max_retries = Files.max_network_retries
402
+ @transport = job.transport
403
+ @inbox = []
404
+ @queue = []
405
+ @renewals = []
406
+ @completed = []
407
+ @inflight = 0
408
+ @stop = nil
409
+ @controller = nil
410
+ @connections = {}
411
+ @origin = nil
412
+ @lease = nil
413
+ # The caller's thread, which runs the transfer.
414
+ @thread = Thread.current
415
+ end
416
+
417
+ # Opens the source or destination, checking a resume's, and transfers, once it holds a file slot. An error on the
418
+ # way is raised only when it came before any cancellation or pause, and then has its say on the destination
419
+ # (after_failure); otherwise the first of those is raised, with a resume's unspent checkpoint when it stopped
420
+ # before it started.
421
+ def run
422
+ state = @job.acquire_file(@control)
423
+ raise CancelledError if state == :cancelled
424
+ raise PausedError, @origin if state == :paused
425
+
426
+ begin
427
+ begin
428
+ open_resources
429
+ transfer
430
+ rescue StandardError => e
431
+ error = arbitrate(e)
432
+ after_failure(error)
433
+ raise if error.equal?(e)
434
+
435
+ raise error, cause: nil
436
+ end
437
+ ensure
438
+ begin
439
+ drain
440
+ ensure
441
+ begin
442
+ close_resources
443
+ ensure
444
+ @job.release_file
445
+ end
446
+ end
447
+ end
448
+ end
449
+
450
+ private
451
+
452
+ def pump
453
+ loop do
454
+ plan if @stop.nil?
455
+ completed, done = @job.synchronize { await }
456
+ deliver(completed)
457
+ return if done
458
+ end
459
+ end
460
+
461
+ # With the lock held: returns what was accepted for deliver, and whether the transfer is done.
462
+ def await
463
+ loop do
464
+ observe_stops
465
+ completed = @completed.shift(@completed.size)
466
+ return [ completed, false ] unless completed.empty?
467
+
468
+ if @stop
469
+ release_waiting
470
+ return [ [], true ] if @inflight.zero?
471
+ else
472
+ return [ [], true ] if finished?
473
+
474
+ admit
475
+ return [ [], false ] if wants_planning?
476
+ end
477
+ @job.wait(wait_seconds)
478
+ end
479
+ end
480
+
481
+ def observe_control
482
+ return unless @control
483
+
484
+ if @control.cancelled?
485
+ @stop ||= CancelledError.new
486
+ elsif @control.paused?
487
+ @stop ||= :paused
488
+ end
489
+ end
490
+
491
+ # With the lock held: records every stop seen so far, oldest first: what the workers sent back, then a
492
+ # cancellation or pause and the job's closing. Every path that records a stop calls it first.
493
+ def observe_stops
494
+ settle_inbox
495
+ observe_control
496
+ @stop ||= Files::Error.new("The transfer job was closed") if @job.closed?
497
+ end
498
+
499
+ # With the lock held: settles what the workers sent back, in the order they sent it. A request that ended after
500
+ # the control had already stopped the transfer is settled after that stop, and one that ended before it, before,
501
+ # so the first of a failure and a cancellation or pause wins.
502
+ def settle_inbox
503
+ @inbox.shift(@inbox.size).each do |item, outcome, after_stop|
504
+ observe_control if after_stop
505
+ settle(item, outcome)
506
+ end
507
+ end
508
+
509
+ # Whether the control has stopped the transfer, as a worker's request ends.
510
+ def stopped?
511
+ !@control.nil? && @control.state != :running
512
+ end
513
+
514
+ # With the lock held. Nothing is admitted while a throttle's Retry-After holds its destination back, whichever of
515
+ # the job's transfers met it. Due items are admitted in queue order, but one waiting for buffer bytes the budget
516
+ # cannot give yet lets a later item that already holds its own, such as a retry keeping its bytes, go ahead of it:
517
+ # that takes nothing more from the budget, and gives its bytes back once it ends. No later item needing new bytes
518
+ # passes the one waiting.
519
+ def admit
520
+ now = Clock.now
521
+ waiting = false
522
+ while (item = @queue.find { |queued| queued.ready_at <= now && (!waiting || queued.reservation) })
523
+ break if @controller.admitted >= @controller.limit || @inflight >= item_cap
524
+
525
+ if expiring?(item)
526
+ @queue.delete(item)
527
+ @renewals << item
528
+ next
529
+ end
530
+ target = request_target(item)
531
+ break unless ready?(item) && @job.held_until(target[0]).nil?
532
+
533
+ bytes = item.reservation ? 0 : reservation_bytes(item)
534
+ unless @job.budget.available?(bytes)
535
+ waiting = true
536
+ next
537
+ end
538
+ break unless @job.take_request
539
+
540
+ item.reservation ||= @job.budget.reserve(bytes)
541
+ @queue.delete(item)
542
+ launch(item, target)
543
+ end
544
+ end
545
+
546
+ # With the lock held and a request permit taken for item, which goes to target, the request_target it was admitted
547
+ # for, whatever is renewed before its worker sends it. The attempt counts against the controller's limit from now,
548
+ # but the transport measures it only from its send until it is answered; the worker gives the permit back as soon
549
+ # as the request ends, its response read, before its outcome is settled here, and publishes a throttle's
550
+ # Retry-After for target's destination to the job first, so it holds that destination back for every transfer at
551
+ # once.
552
+ def launch(item, target)
553
+ controller = @controller
554
+ attempt = controller.admit
555
+ timing = Timing.new(controller, attempt, measured_through_body?)
556
+ @job.submit do |connections|
557
+ outcome = [ :fail, Files::Error.new("The transfer worker stopped") ]
558
+ begin
559
+ outcome = attempt_item(item, target, connections, timing)
560
+ rescue Passthrough => e
561
+ outcome = [ :fail, e.error ]
562
+ rescue NetworkFailure => e
563
+ outcome = e.retryable? ? [ :retry, StorageRetry::RETRY_DELAY, :failure, e ] : [ :fail, e ]
564
+ rescue StandardError => e
565
+ outcome = [ :fail, e ]
566
+ ensure
567
+ begin
568
+ measure(controller, attempt, outcome)
569
+ ensure
570
+ @job.post do
571
+ @job.hold(target[0], outcome[1]) if outcome[0] == :retry && outcome[2] == :throttle
572
+ @job.give_request
573
+ @inbox << [ item, outcome, stopped? ]
574
+ end
575
+ end
576
+ end
577
+ end
578
+ @inflight += 1
579
+ end
580
+
581
+ # On the worker's thread, as its request ends.
582
+ def measure(controller, attempt, outcome)
583
+ case outcome[0]
584
+ when :ok then controller.finish_attempt(attempt, :success, bytes: outcome[2])
585
+ when :retry then controller.finish_attempt(attempt, outcome[2], retry_after: outcome[2] == :throttle ? outcome[1] : nil)
586
+ else controller.finish_attempt(attempt, :ignored)
587
+ end
588
+ end
589
+
590
+ # With the lock held. outcome is [:ok, value, bytes], [:retry, seconds, :failure or :throttle, error],
591
+ # [:renew, error] or [:fail, error]. A retry, and a renewal of a URL that expired after the request was sent,
592
+ # each count against the item's retries and need it to allow them. Wakes everything waiting, since settling can
593
+ # release bytes another transfer waits for.
594
+ def settle(item, outcome)
595
+ @inflight -= 1
596
+ case outcome[0]
597
+ when :ok
598
+ entry = accept(item, outcome[1])
599
+ @completed << entry if entry
600
+ when :retry
601
+ _, delay, _signal, error = outcome
602
+ if again?(item)
603
+ item.ready_at = Clock.now + delay
604
+ enqueue(item)
605
+ else
606
+ give_up(item, error)
607
+ end
608
+ when :renew
609
+ again?(item) ? @renewals << item : give_up(item, outcome[1])
610
+ else
611
+ give_up(item, outcome[1])
612
+ end
613
+ @job.signal
614
+ end
615
+
616
+ # With the lock held: whether item may be sent again, counting it when it may.
617
+ def again?(item)
618
+ return false unless @stop.nil? && retry_allowed?(item) && item.attempts < @max_retries
619
+
620
+ item.attempts += 1
621
+ @job.count_retry
622
+ true
623
+ end
624
+
625
+ def enqueue(item)
626
+ @queue.insert(@queue.bsearch_index { |queued| queued.number > item.number } || @queue.size, item)
627
+ end
628
+
629
+ # With the lock held.
630
+ def give_up(item, error)
631
+ discard(item)
632
+ @stop ||= public_error(error)
633
+ @job.signal
634
+ end
635
+
636
+ # On the caller's thread: gives item up for error, after every stop recorded before it.
637
+ def fail_item(item, error)
638
+ @job.synchronize do
639
+ observe_stops
640
+ give_up(item, error)
641
+ end
642
+ end
643
+
644
+ # Records a failure on the caller's thread, as SDK code there recognizes it, after every stop recorded before it:
645
+ # the workers' failures sent back earlier, and a cancellation or pause that came first. Returns error, to raise.
646
+ def stop!(error)
647
+ @job.synchronize do
648
+ observe_stops
649
+ @stop ||= public_error(error)
650
+ @job.signal
651
+ end
652
+ error
653
+ end
654
+
655
+ # With the lock held: drops what item holds and gives its bytes back to the buffer budget, together, so the
656
+ # budget never counts less than is held.
657
+ def discard(item)
658
+ item.data = nil if item.respond_to?(:data=)
659
+ @job.budget.release(item.reservation)
660
+ end
661
+
662
+ def release_waiting
663
+ (@queue + @renewals).each { |item| discard(item) }
664
+ @queue.clear
665
+ @renewals.clear
666
+ @job.signal
667
+ end
668
+
669
+ # With the lock held: how long to wait at most: until the next retry is due or a throttle holding back the
670
+ # destination of a waiting item has passed.
671
+ def wait_seconds
672
+ now = Clock.now
673
+ deadlines = @queue.map(&:ready_at)
674
+ deadlines.concat(@queue.map { |item| request_target(item)[0] }.uniq.filter_map { |url| @job.held_until(url) })
675
+ pending = deadlines.select { |at| at > now }.min
676
+ pending && [ pending - now, 0.001 ].max
677
+ end
678
+
679
+ # Waits for the requests in flight, however the transfer ended, and releases what it holds.
680
+ def drain
681
+ @job.synchronize do
682
+ @stop ||= :aborted unless @inflight.zero?
683
+ until @inflight.zero?
684
+ settle_inbox
685
+ @job.wait if @inflight.positive?
686
+ end
687
+ @inbox.clear
688
+ all_items.each { |item| discard(item) }
689
+ @job.signal
690
+ end
691
+ @job.transport.close_all(@connections)
692
+ end
693
+
694
+ def raise_outcome
695
+ case @stop
696
+ when nil then nil
697
+ when :paused then raise PausedError, pause_checkpoint
698
+ else raise @stop
699
+ end
700
+ end
701
+
702
+ # A resume paused before it started gives back the checkpoint it was given, still usable; any other transfer its
703
+ # own, none while its source or destination is not yet open (checkpoint).
704
+ def pause_checkpoint
705
+ @lease && !@lease.started? ? @origin : checkpoint
706
+ end
707
+
708
+ # On the caller's thread, for an error that ended the transfer there, from opening its source or destination to
709
+ # its last request: records it after every stop recorded before it, a cancellation or pause the control already
710
+ # holds included, unless stop! recorded it already as SDK code recognized it, and returns the first of them,
711
+ # which the transfer raises. Nothing recorded later replaces it, and an error raise_outcome raised is that first
712
+ # stop already.
713
+ def arbitrate(error)
714
+ first = @job.synchronize do
715
+ observe_stops
716
+ @stop ||= public_error(error)
717
+ end
718
+ return error if first.equal?(error) || (first == :paused && error.is_a?(PausedError))
719
+
720
+ first == :paused ? PausedError.new(pause_checkpoint) : first
721
+ end
722
+
723
+ # Carries the context an API request resolved, such as the regional origin a lockout region mismatch moved it to,
724
+ # to the transfer's API requests after it. What it leaves unset stays as the transfer began.
725
+ def adopt_context(record)
726
+ options = record.respond_to?(:options) ? record.options : nil
727
+ @options = @options.merge(options.compact).merge(client: @options[:client]) if options.is_a?(Hash)
728
+ end
729
+
730
+ # Opens the transfer's source or destination, once it holds a file slot.
731
+ def open_resources; end
732
+
733
+ # Closes what open_resources opened, however the transfer ended.
734
+ def close_resources; end
735
+
736
+ # What the transfer does with its destination once error, the first stop, ended it, before what it holds is
737
+ # released.
738
+ def after_failure(_error); end
739
+
740
+ # Where item's request goes now, frozen, its URL first, whose destination a throttle may hold back. An admitted
741
+ # attempt keeps the one it was admitted for (launch), is sent there by attempt_item and holds back its destination.
742
+ def request_target(_item)
743
+ [ nil ].freeze
744
+ end
745
+
746
+ def item_cap
747
+ Float::INFINITY
748
+ end
749
+
750
+ # Whether an attempt is on the network until its response body has been read and checked, as a download's is, or
751
+ # only until its response headers arrive, as an upload's is.
752
+ def measured_through_body?
753
+ false
754
+ end
755
+
756
+ # Whether item can be sent: queued items are admitted in order, so one that is not stops admission there.
757
+ def ready?(_item)
758
+ true
759
+ end
760
+
761
+ def wants_planning?
762
+ !@renewals.empty?
763
+ end
764
+
765
+ def expiring?(_item)
766
+ false
767
+ end
768
+
769
+ def retry_allowed?(_item)
770
+ true
771
+ end
772
+
773
+ def public_error(error)
774
+ error
775
+ end
776
+ end
777
+ end
778
+ end