files.com 1.1.813 → 1.1.814

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 (461) 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 +1 -1
  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 +1 -1
  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 +4703 -2415
  435. data/doc/top-level-namespace.html +1 -1
  436. data/docs/file.md +346 -0
  437. data/lib/files.com/api.rb +6 -1
  438. data/lib/files.com/api_client.rb +220 -32
  439. data/lib/files.com/models/file.rb +124 -17
  440. data/lib/files.com/transfer/budgets.rb +123 -0
  441. data/lib/files.com/transfer/checkpoint.rb +113 -0
  442. data/lib/files.com/transfer/clock.rb +14 -0
  443. data/lib/files.com/transfer/controller.rb +549 -0
  444. data/lib/files.com/transfer/download_coordinator.rb +906 -0
  445. data/lib/files.com/transfer/errors.rb +100 -0
  446. data/lib/files.com/transfer/http_transport.rb +337 -0
  447. data/lib/files.com/transfer/job.rb +778 -0
  448. data/lib/files.com/transfer/options.rb +126 -0
  449. data/lib/files.com/transfer/policy.rb +91 -0
  450. data/lib/files.com/transfer/storage_retry.rb +169 -0
  451. data/lib/files.com/transfer/upload_coordinator.rb +657 -0
  452. data/lib/files.com/transfer/upload_plan.rb +157 -0
  453. data/lib/files.com.rb +13 -0
  454. data/spec/lib/api_client_spec.rb +125 -3
  455. data/spec/models/file_transfer_spec.rb +53 -4
  456. data/spec/support/transfer_server.rb +113 -14
  457. data/spec/transfer/adaptive_download_spec.rb +1102 -0
  458. data/spec/transfer/adaptive_upload_spec.rb +1602 -0
  459. data/spec/transfer/concurrency_controller_spec.rb +382 -0
  460. data/spec/workspace_id_spec.rb +19 -14
  461. metadata +59 -1
@@ -57,14 +57,70 @@ module Files
57
57
  # Downloads a remote file to a local file, replacing its contents.
58
58
  # @param path [String] remote file path.
59
59
  # @param local_path [String, nil] local destination; defaults to the remote basename.
60
+ # @param transfer [Boolean, Hash, Files::Transfer::Options, Files::Transfer::Job, nil] adaptive options or a shared job.
61
+ # @param progress [#call, nil] progress callback; requires transfer.
60
62
  # @return [void]
61
63
  # @raise [Files::InvalidParameterError] if the default name is unsafe on Windows; supply local_path explicitly.
62
64
  # @raise [StandardError] if the transfer or local write fails.
63
- def self.download_file(path, local_path = nil)
65
+ # With transfer:, the download is adaptive, and can report progress (see #download_file).
66
+ def self.download_file(path, local_path = nil, progress: nil, transfer: nil)
64
67
  local_path ||= default_local_path(path)
68
+ return new(path).download_file(local_path, progress: progress, transfer: transfer) if transfer
69
+ raise InvalidParameterError, "Bad parameter: progress needs transfer" if progress
70
+
65
71
  new(path).download_file(local_path)
66
72
  end
67
73
 
74
+ # Continues a download a Files::Transfer::PausedError stopped, in the same process, from its checkpoint: into the
75
+ # local file it was writing, or into destination, the same IO it was writing. options are the API options, which a
76
+ # checkpoint never keeps; progress and transfer: are as for #download_file, transfer: true for the defaults, with
77
+ # written_bytes counting the bytes from before the pause. One resume at a time can use a checkpoint, and once a
78
+ # resume has started its transfer the checkpoint is spent, however it ends; a pause gives a new one. Raises
79
+ # Files::Transfer::CheckpointError, before anything is sent, written or truncated, when the destination or the bytes
80
+ # already written to it changed or cannot be read back, and Files::Transfer::VersionChangedError when the remote
81
+ # file did.
82
+ def self.resume_download(checkpoint, destination = nil, options = {}, progress: nil, transfer: true)
83
+ raise InvalidParameterError, "Bad parameter: checkpoint must be a Files::Transfer::Checkpoint" unless checkpoint.is_a?(Transfer::Checkpoint)
84
+
85
+ Transfer::Job.using(transfer) { |job| Transfer::DownloadCoordinator.resume(job, checkpoint, destination, options, progress: progress) }
86
+ end
87
+
88
+ # Continues an upload a Files::Transfer::PausedError stopped, in the same process, from its checkpoint, sending only
89
+ # the parts storage had not acknowledged. options are the API options, which a checkpoint never keeps; progress and
90
+ # transfer: are as for upload_file, with progress counting the acknowledged bytes from before the pause. One resume
91
+ # at a time can use a checkpoint, and once a resume has started its transfer the checkpoint is spent, however it
92
+ # ends; a pause gives a new one. Raises Files::Transfer::CheckpointError, before anything is sent, when the local
93
+ # file changed. Returns what upload_file returns.
94
+ def self.resume_upload(checkpoint, options = {}, progress: nil, transfer: true)
95
+ raise InvalidParameterError, "Bad parameter: checkpoint must be a Files::Transfer::Checkpoint" unless checkpoint.is_a?(Transfer::Checkpoint)
96
+
97
+ Transfer::Job.using(transfer) { |job| Transfer::UploadCoordinator.resume(job, checkpoint, options, progress: progress) }
98
+ end
99
+
100
+ # Uploads what io, a caller's IO or StringIO, holds from where it stands to destination, and returns what
101
+ # upload_file returns. With size, an Integer, and transfer: (as for upload_file), the upload is adaptive: exactly
102
+ # size bytes are read on this thread with read(length), part by part in order, through as many reads as it takes,
103
+ # each part only once buffer_bytes can hold it, and a copy of what each read returns, taken before the next read,
104
+ # is kept until storage acknowledges the part, so a retry sends the same bytes even when io's reads reuse one
105
+ # String; io's Strings are never changed. io ending before size bytes raises
106
+ # Files::Transfer::SourceChangedError, and nothing past them is read. Without size or transfer:, io is read to its
107
+ # end and uploaded a part at a time, as upload_chunks does. io is never rewound or closed. A paused stream upload
108
+ # cannot be resumed: its PausedError has no checkpoint. progress is as for upload_file, with total_bytes size (nil
109
+ # when it is not given).
110
+ def self.upload_io(io, destination, options = {}, size: nil, params: {}, progress: nil, transfer: nil)
111
+ raise InvalidParameterError, "Bad parameter: size must be a non-negative Integer" unless size.nil? || (size.is_a?(Integer) && size >= 0)
112
+
113
+ return Transfer::Job.using(transfer) { |job| Transfer::UploadCoordinator.upload_io(job, io, size, destination, options, params: params, progress: progress) } if transfer && size
114
+
115
+ params = params.dup
116
+ params[:size] ||= size if size
117
+ report = progress && ->(part) { progress.call(part.merge(total_bytes: size)) }
118
+ upload, etags, bytes = upload_chunks(io, destination, options, params: params, progress: report)
119
+ raise InvalidParameterError, "Bad parameter: io held #{bytes} bytes, not size #{size}" if size && bytes != size
120
+
121
+ create(destination, { action: "end", etags: etags, ref: upload.ref, size: bytes }, options)
122
+ end
123
+
68
124
  private_class_method def self.default_local_path(path)
69
125
  name = PathUtil.normalize(path).split("/").last.to_s
70
126
  # On Windows a colon makes the name a drive-relative path or an NTFS
@@ -90,6 +146,11 @@ module Files
90
146
  PathUtil.normalize("_", root, id.to_s, relative_path)
91
147
  end
92
148
 
149
+ # The transfer: keyword to pass on, only when the caller gave one.
150
+ private_class_method def self.transfer_keywords(transfer)
151
+ transfer ? { transfer: transfer } : {}
152
+ end
153
+
93
154
  # Uploads a local file into the RemoteServers destination namespace.
94
155
  # @param path [String] local file path.
95
156
  # @param remote_server_id [Integer] destination ID.
@@ -97,10 +158,11 @@ module Files
97
158
  # @param options [Hash] request configuration; see {Files::Api.send_request}.
98
159
  # @param params [Hash] upload API parameters.
99
160
  # @param progress [#call, nil] receives the acknowledged-part Hash described by {.upload_file}.
161
+ # @param transfer [Boolean, Hash, Files::Transfer::Options, Files::Transfer::Job, nil] adaptive options or a shared job.
100
162
  # @return [Files::File] the completed upload.
101
- def self.upload_to_remote_server(path, remote_server_id, destination = nil, options = {}, params: {}, progress: nil)
163
+ def self.upload_to_remote_server(path, remote_server_id, destination = nil, options = {}, params: {}, progress: nil, transfer: nil)
102
164
  destination ||= ::File.basename(path)
103
- upload_file(path, underscore_destination_path("RemoteServers", remote_server_id, destination), options, params: params, progress: progress)
165
+ upload_file(path, underscore_destination_path("RemoteServers", remote_server_id, destination), options, params: params, progress: progress, **transfer_keywords(transfer))
104
166
  end
105
167
 
106
168
  # Copies a remote file into the RemoteServers destination namespace.
@@ -132,10 +194,11 @@ module Files
132
194
  # @param options [Hash] request configuration; see {Files::Api.send_request}.
133
195
  # @param params [Hash] upload API parameters.
134
196
  # @param progress [#call, nil] receives the acknowledged-part Hash described by {.upload_file}.
197
+ # @param transfer [Boolean, Hash, Files::Transfer::Options, Files::Transfer::Job, nil] adaptive options or a shared job.
135
198
  # @return [Files::File] the completed upload.
136
- def self.upload_to_snapshot(path, snapshot_id, destination = nil, options = {}, params: {}, progress: nil)
199
+ def self.upload_to_snapshot(path, snapshot_id, destination = nil, options = {}, params: {}, progress: nil, transfer: nil)
137
200
  destination ||= ::File.basename(path)
138
- upload_file(path, underscore_destination_path("Snapshots", snapshot_id, destination), options, params: params, progress: progress)
201
+ upload_file(path, underscore_destination_path("Snapshots", snapshot_id, destination), options, params: params, progress: progress, **transfer_keywords(transfer))
139
202
  end
140
203
 
141
204
  # Copies a remote file into the Snapshots destination namespace.
@@ -167,10 +230,11 @@ module Files
167
230
  # @param options [Hash] request configuration; see {Files::Api.send_request}.
168
231
  # @param params [Hash] upload API parameters.
169
232
  # @param progress [#call, nil] receives the acknowledged-part Hash described by {.upload_file}.
233
+ # @param transfer [Boolean, Hash, Files::Transfer::Options, Files::Transfer::Job, nil] adaptive options or a shared job.
170
234
  # @return [Files::File] the completed upload.
171
- def self.upload_to_child_site(path, site_id, destination = nil, options = {}, params: {}, progress: nil)
235
+ def self.upload_to_child_site(path, site_id, destination = nil, options = {}, params: {}, progress: nil, transfer: nil)
172
236
  destination ||= ::File.basename(path)
173
- upload_file(path, underscore_destination_path("Sites", site_id, destination), options, params: params, progress: progress)
237
+ upload_file(path, underscore_destination_path("Sites", site_id, destination), options, params: params, progress: progress, **transfer_keywords(transfer))
174
238
  end
175
239
 
176
240
  # Copies a remote file into the Sites destination namespace.
@@ -336,10 +400,11 @@ module Files
336
400
  end
337
401
 
338
402
  # Uploads what io holds to path, a part at a time as the API grants each, and returns the last part, the parts'
339
- # ETags and the bytes sent. Each part is filled from as many reads as it takes (read_part). With progress, a
340
- # callable, each part's response must be a success (2xx) before the part counts as acknowledged; progress is then
341
- # called with total_bytes (nil: unknown here), acknowledged_bytes (this call's bytes so far), part_number and
342
- # part_bytes. Anything it raises ends the upload there, with no later part planned or sent.
403
+ # ETags and the bytes sent. Each part is filled from as many reads as it takes (read_part). Each part's response
404
+ # must be a success (2xx) before the part counts as acknowledged; any other status raises APIError before another
405
+ # part is planned or the upload is ended. With progress, a callable, progress is then called with total_bytes (nil:
406
+ # unknown here), acknowledged_bytes (this call's bytes so far), part_number and part_bytes. Anything it raises ends
407
+ # the upload there, with no later part planned or sent.
343
408
  # @param io [#read, #eof?] source, read from its current position.
344
409
  # @param path [String] remote destination path.
345
410
  # @param options [Hash] request configuration; see {Files::Api.send_request}.
@@ -360,7 +425,7 @@ module Files
360
425
  bytes_written += buf.length
361
426
  method = upload.http_method.downcase.to_sym
362
427
  response = client(options).remote_request(method, upload.upload_uri, { 'Content-Length': buf.length.to_s, 'Content-Type': 'application/octet-stream' }, buf)
363
- raise APIError.new("Transfer request failed", http_status: response.status), cause: nil if progress && !response.status.between?(200, 299)
428
+ raise APIError.new("Transfer request failed", http_status: response.status), cause: nil unless response.status.between?(200, 299)
364
429
 
365
430
  etags << { etag: response.headers["ETag"], part: upload.part_number }
366
431
  progress&.call({ total_bytes: nil, acknowledged_bytes: bytes_written, part_number: upload.part_number, part_bytes: buf.length })
@@ -390,15 +455,27 @@ module Files
390
455
 
391
456
  # With progress, a callable, it is called after each part is acknowledged, as upload_chunks reports it, with
392
457
  # total_bytes the local file's size.
458
+ #
459
+ # With transfer: (true, a Hash of Files::Transfer::Options, an Options or a shared Files::Transfer::Job), the
460
+ # upload is adaptive: its parts are sent concurrently, as many at once as the link sustains within those options,
461
+ # and progress is called on this thread once for each part storage acknowledged, in the order they were. A
462
+ # Files::Transfer::Control in the options can cancel or pause it. Without transfer:, parts go one at a time, as
463
+ # they always have. See docs/file.md.
393
464
  # @param path [String] local file path.
394
465
  # @param destination [String, nil] remote path; defaults to the local basename.
395
466
  # @param options [Hash] request configuration; see {Files::Api.send_request}.
396
467
  # @param params [Hash] upload API parameters.
397
468
  # @param progress [#call, nil] called with total_bytes, acknowledged_bytes, part_number and part_bytes keys.
398
469
  # Counts are bytes; part_number is the API part number. Raising stops the upload before any later part.
470
+ # @param transfer [Boolean, Hash, Files::Transfer::Options, Files::Transfer::Job, nil] adaptive options or a shared job.
399
471
  # @return [Files::File] the completed upload.
400
472
  # @raise [StandardError] if reading, a request or the progress callback fails; the local file is still closed.
401
- def self.upload_file(path, destination = nil, options = {}, params: {}, progress: nil)
473
+ def self.upload_file(path, destination = nil, options = {}, params: {}, progress: nil, transfer: nil)
474
+ if transfer
475
+ destination ||= ::File.basename(path)
476
+ return Transfer::Job.using(transfer) { |job| Transfer::UploadCoordinator.upload(job, path, destination, options, params: params, progress: progress) }
477
+ end
478
+
402
479
  local_file = ::File.open(path, 'r')
403
480
  destination ||= File.basename(path)
404
481
  params[:size] ||= local_file.size
@@ -607,9 +684,18 @@ module Files
607
684
 
608
685
  # Downloads this remote file to a local file, replacing its contents.
609
686
  # @param output_file [String] local destination path.
687
+ # @param transfer [Boolean, Hash, Files::Transfer::Options, Files::Transfer::Job, nil] adaptive options or a shared job.
688
+ # @param progress [#call, nil] progress callback; requires transfer.
610
689
  # @return [void]
611
690
  # @raise [StandardError] if the transfer or local write fails.
612
- def download_file(output_file)
691
+ # With transfer: (as for File.upload_file), the download is adaptive: when storage binds the file's version, its
692
+ # ranges are fetched concurrently and written in order, and progress, a callable, is called on this thread after
693
+ # each write with total_bytes (nil until known), written_bytes, elapsed_seconds and bytes_per_second. Anything it
694
+ # raises ends the download. progress needs transfer:. See docs/file.md.
695
+ def download_file(output_file, progress: nil, transfer: nil)
696
+ return Transfer::Job.using(transfer) { |job| Transfer::DownloadCoordinator.download_file(job, self, output_file, progress: progress) } if transfer
697
+ raise InvalidParameterError, "Bad parameter: progress needs transfer" if progress
698
+
613
699
  ::File.open(output_file, 'wb') do |file|
614
700
  download_content(file)
615
701
  end
@@ -619,9 +705,19 @@ module Files
619
705
  # @param io [#write, #<<] destination stream.
620
706
  # @param range [Array<Integer>] optional inclusive start and end byte offsets.
621
707
  # Applied by the Net::HTTP adapter; other adapters fetch the whole response.
708
+ # @param transfer [Boolean, Hash, Files::Transfer::Options, Files::Transfer::Job, nil] adaptive options or a shared job.
709
+ # @param progress [#call, nil] progress callback; requires transfer.
622
710
  # @return [void]
623
711
  # @raise [StandardError] if the transfer or destination fails.
624
- def download_content(io, range: [])
712
+ # With transfer:, as for #download_file, into io; it cannot be combined with range.
713
+ def download_content(io, range: [], progress: nil, transfer: nil)
714
+ if transfer
715
+ raise InvalidParameterError, "Bad parameter: range cannot be combined with transfer" unless range.nil? || range.empty?
716
+
717
+ return Transfer::Job.using(transfer) { |job| Transfer::DownloadCoordinator.download_content(job, self, io, progress: progress) }
718
+ end
719
+ raise InvalidParameterError, "Bad parameter: progress needs transfer" if progress
720
+
625
721
  Files::ApiClient.download_client.stream_download(download_uri_with_load, io, range)
626
722
  end
627
723
 
@@ -758,7 +854,11 @@ module Files
758
854
  # @param options [Hash] keyword arguments for {#download_content}, such as range.
759
855
  # @return [Files::SizableIO] the pipe reader; the caller owns closing it.
760
856
  # @raise [StandardError] if the download fails while the reader is being prepared.
857
+ # Never adaptive: transfer: and progress: are refused here, before any pipe or download thread exists.
761
858
  def read_io(**options)
859
+ raise InvalidParameterError, "Bad parameter: read_io cannot transfer adaptively; use #download_content with transfer:" if options[:transfer]
860
+ raise InvalidParameterError, "Bad parameter: progress needs transfer" if options[:progress]
861
+
762
862
  @read_io ||= begin
763
863
  r, w = SizableIO.pipe
764
864
  Thread.new do
@@ -994,8 +1094,15 @@ module Files
994
1094
  raise NotImplementedError
995
1095
  end
996
1096
 
997
- def upload_file(local_file)
998
- File.upload_file(local_file.path, params: @attributes)
1097
+ # Uploads local_file, an opened local file, to this record's path with this record's API options, and returns
1098
+ # what File.upload_file returns. It reads local_file's path through its own handle and leaves local_file open.
1099
+ # Only the record's mkdir_parents and with_rename go to begin_upload, never its other attributes, such as its size
1100
+ # or download_uri. transfer: is as for File.upload_file.
1101
+ def upload_file(local_file, transfer: nil)
1102
+ params = @attributes.slice(:mkdir_parents, :with_rename)
1103
+ return File.upload_file(local_file.path, path, @options, params: params, transfer: transfer) if transfer
1104
+
1105
+ File.upload_file(local_file.path, path, @options, params: params)
999
1106
  end
1000
1107
 
1001
1108
  # Buffers bytes, or uses a readable IO as the upload source.
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Files
4
+ module Transfer
5
+ # The bytes a job may hold in memory at once, for parts waiting to be sent or written. A part reserves its bytes
6
+ # before anything is read for it, keeps them through its retries and gives them back once it is acknowledged,
7
+ # written, abandoned or stopped. A part larger than the whole budget is refused before it reads anything, even when
8
+ # nothing else is held. Its methods run under the job's lock.
9
+ class ByteBudget
10
+ Reservation = Struct.new(:bytes, :released)
11
+
12
+ attr_reader :limit, :held
13
+
14
+ def initialize(limit)
15
+ @limit = limit
16
+ @held = 0
17
+ end
18
+
19
+ def check!(bytes)
20
+ return if bytes <= @limit
21
+
22
+ raise BufferLimitError, "A #{bytes}-byte part needs more memory than the transfer's buffer_bytes of #{@limit}"
23
+ end
24
+
25
+ def available?(bytes)
26
+ check!(bytes)
27
+ @held + bytes <= @limit
28
+ end
29
+
30
+ def reserve(bytes)
31
+ raise ArgumentError, "the transfer buffer cannot hold #{bytes} more bytes" unless available?(bytes)
32
+
33
+ @held += bytes
34
+ Reservation.new(bytes, false)
35
+ end
36
+
37
+ # Gives back what reservation holds, once however often it is called.
38
+ def release(reservation)
39
+ return if reservation.nil? || reservation.released
40
+
41
+ reservation.released = true
42
+ @held -= reservation.bytes
43
+ end
44
+
45
+ # Gives back all but bytes of what reservation holds, which holds bytes from then on. Nothing changes for a
46
+ # reservation released already or holding no more than bytes.
47
+ def shrink(reservation, bytes)
48
+ return if reservation.nil? || reservation.released || bytes >= reservation.bytes
49
+
50
+ @held -= reservation.bytes - bytes
51
+ reservation.bytes = bytes
52
+ end
53
+ end
54
+
55
+ # How many files a job transfers at once. Its methods run under the job's lock.
56
+ class FileSlots
57
+ attr_reader :limit, :used
58
+
59
+ def initialize(limit)
60
+ @limit = limit
61
+ @used = 0
62
+ end
63
+
64
+ def take
65
+ return false if @used >= @limit
66
+
67
+ @used += 1
68
+ true
69
+ end
70
+
71
+ def give
72
+ @used -= 1 if @used.positive?
73
+ end
74
+ end
75
+
76
+ # How many content requests a job has in flight at once, across all its transfers and destinations: heads, whole
77
+ # responses, parts and ranges alike. A request holds its permit from just before it is sent until its response is
78
+ # read or it fails, never while it waits to be retried. Its methods run under the job's lock.
79
+ class RequestPermits < FileSlots; end
80
+
81
+ # How long each destination is held back after a throttle: no new content request or retry of a job's, from any of
82
+ # its transfers and through whichever transport or controller, goes to it until the latest Retry-After any of them
83
+ # got from it has passed. A later Retry-After can extend that, never shorten it, and requests already sent finish. A
84
+ # destination is an origin: its scheme, host and port. Its methods run under the job's lock.
85
+ class Holds
86
+ # The destination url names, or nil when it names none.
87
+ def self.destination(url)
88
+ uri = ::URI.parse(url.to_s)
89
+ [ uri.scheme.to_s.downcase, uri.host.downcase, uri.port ].freeze if uri.host
90
+ rescue ::URI::InvalidURIError
91
+ nil
92
+ end
93
+
94
+ def initialize(clock: Clock)
95
+ @clock = clock
96
+ @until = {}
97
+ end
98
+
99
+ # Holds destination back for seconds from now, unless it already is for longer.
100
+ def hold(destination, seconds)
101
+ return unless destination && seconds.to_f.positive?
102
+
103
+ deadline = @clock.now + seconds.to_f
104
+ @until[destination] = [ @until[destination] || deadline, deadline ].max
105
+ end
106
+
107
+ # The monotonic time until which destination is held back, or nil once that has passed.
108
+ def deadline(destination)
109
+ deadline = @until[destination]
110
+ return deadline if deadline && deadline > @clock.now
111
+
112
+ @until.delete(destination)
113
+ nil
114
+ end
115
+
116
+ # Each destination held back now, as its origin, with the seconds left.
117
+ def to_h
118
+ now = @clock.now
119
+ @until.select { |_, deadline| deadline > now }.to_h { |(scheme, host, port), deadline| [ "#{scheme}://#{host}:#{port}", deadline - now ] }
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Files
4
+ module Transfer
5
+ # Where a paused transfer stopped, for Files::File.resume_upload or Files::File.resume_download in the same process.
6
+ #
7
+ # It holds no credentials, signed URLs, API options or API response fields; resuming takes the options again. It
8
+ # records, frozen, what it must find unchanged: an upload's local file (device, inode, size and modification
9
+ # time), its reference, the upload parameters it began with (only mkdir_parents, with_rename and size), a
10
+ # fingerprint of each part's instructions (a SHA-256 digest of their method, headers, size and retry rules, never
11
+ # their URL) and the parts storage acknowledged; a download's destination (a local file's device and inode, or the
12
+ # caller's IO object itself), the number of bytes written to it with a SHA-256 digest of them, and the remote
13
+ # file's version. Resuming refuses with CheckpointError, before it sends, writes or truncates anything, when any of
14
+ # them changed; a renewed part whose instructions differ stops the upload, and a download stops with
15
+ # VersionChangedError when storage no longer has that version.
16
+ #
17
+ # One resume at a time holds it. Once that resume has checked the local file or destination and starts its
18
+ # transfer, the checkpoint is spent for good, however the transfer ends: a pause then gives a new checkpoint. Only a
19
+ # resume refused before it started, as above, or paused or cancelled before its turn or before it finished those
20
+ # checks, leaves it usable. It cannot be dumped or resumed in another process.
21
+ class Checkpoint
22
+ # One resume's hold on a checkpoint, which only that resume's transfer has. start! spends the checkpoint.
23
+ class Lease
24
+ def initialize
25
+ @started = false
26
+ end
27
+
28
+ def start!
29
+ @started = true
30
+ end
31
+
32
+ def started?
33
+ @started
34
+ end
35
+ end
36
+ private_constant :Lease
37
+
38
+ attr_reader :direction, :path, :local_path, :total_bytes, :completed_bytes, :source, :sink, :version, :state
39
+
40
+ # The identity of an opened local file, from its stat: [device, inode], with size and modification time unless
41
+ # contents is false.
42
+ def self.identity(stat, contents: true)
43
+ contents ? [ stat.dev, stat.ino, stat.size, stat.mtime.to_r ] : [ stat.dev, stat.ino ]
44
+ end
45
+
46
+ # A frozen copy of value's Hashes, Arrays and Strings, all the way down. Anything else, such as a caller's IO, is
47
+ # kept as it is.
48
+ def self.frozen(value)
49
+ case value
50
+ when Hash then value.to_h { |key, item| [ frozen(key), frozen(item) ] }.freeze
51
+ when Array then value.map { |item| frozen(item) }.freeze
52
+ when String then value.frozen? ? value : value.dup.freeze
53
+ else value
54
+ end
55
+ end
56
+
57
+ def initialize(direction:, path:, total_bytes:, completed_bytes:, local_path: nil, source: nil, sink: nil, version: nil, state: {})
58
+ @process = Process.pid
59
+ @direction = direction
60
+ @path = path.dup.freeze
61
+ @local_path = local_path&.dup&.freeze
62
+ @total_bytes = total_bytes
63
+ @completed_bytes = completed_bytes
64
+ @source = Checkpoint.frozen(source)
65
+ @sink = Checkpoint.frozen(sink)
66
+ @version = version&.dup&.freeze
67
+ @state = Checkpoint.frozen(state)
68
+ @hold_lock = Mutex.new
69
+ @hold = [ :ready ]
70
+ freeze
71
+ end
72
+
73
+ # Raises CheckpointError unless this checkpoint can resume a transfer in direction (:upload or :download).
74
+ def verify!(direction)
75
+ raise CheckpointError, "Bad parameter: a transfer checkpoint resumes only in the process that paused it" unless @process == Process.pid
76
+ raise CheckpointError, "Bad parameter: this checkpoint resumes #{@direction == :upload ? 'an upload' : 'a download'}" unless @direction == direction
77
+ end
78
+
79
+ # Runs the block, which resumes the transfer in direction, as the one resume holding this checkpoint, and returns
80
+ # what it returns. The block gets the resume's lease and calls its start! once its checks pass, before it sends,
81
+ # writes or truncates anything; from then on the checkpoint is spent, whatever happens. Raises CheckpointError as
82
+ # verify! does, while another resume holds it, and once it is spent.
83
+ def resuming(direction)
84
+ verify!(direction)
85
+ lease = Lease.new
86
+ @hold_lock.synchronize do
87
+ case @hold[0]
88
+ when Lease then raise CheckpointError, "Bad parameter: another resume is already using this checkpoint"
89
+ when :spent then raise CheckpointError, "Bad parameter: this checkpoint has already been used to resume its transfer; resume from the checkpoint its own pause gave"
90
+ end
91
+ @hold[0] = lease
92
+ end
93
+ begin
94
+ yield lease
95
+ ensure
96
+ @hold_lock.synchronize { @hold[0] = lease.started? ? :spent : :ready }
97
+ end
98
+ end
99
+
100
+ def inspect
101
+ "#<#{self.class.name} #{@direction} #{@path.inspect} #{@completed_bytes} of #{@total_bytes.inspect} bytes>"
102
+ end
103
+
104
+ def to_s
105
+ inspect
106
+ end
107
+
108
+ def marshal_dump
109
+ raise TypeError, "a Files::Transfer::Checkpoint resumes only in the process that paused it, so it cannot be dumped"
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Files
4
+ module Transfer
5
+ # Seconds on the monotonic clock, which changes to the system time never move. Measurements, retry delays and
6
+ # cooldowns use it; only a signed URL's expiry, which storage states in wall-clock time, is compared with Time.now.
7
+ # Anything with the same now method can stand in for it, as a test's controlled clock does.
8
+ module Clock
9
+ def self.now
10
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
11
+ end
12
+ end
13
+ end
14
+ end