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
@@ -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.
@@ -115,6 +115,12 @@ module Files
115
115
  @attributes[:allow_user_level_allowed_ip_override]
116
116
  end
117
117
 
118
+ # Allow per-user overrides of the FTP TLS session reuse requirement?
119
+ # @return [Boolean, nil] the field value, or nil when absent.
120
+ def allow_user_level_ftp_tls_session_reuse_override
121
+ @attributes[:allow_user_level_ftp_tls_session_reuse_override]
122
+ end
123
+
118
124
  # Allow the site-wide FTP SSL requirement to be overriden on a per-user-basis?
119
125
  # @return [Boolean, nil] the field value, or nil when absent.
120
126
  def allow_user_level_ssl_override
@@ -1012,6 +1018,12 @@ module Files
1012
1018
  @attributes[:snapshot_sharing_enabled]
1013
1019
  end
1014
1020
 
1021
+ # Require TLS-protected FTP data connections to reuse the control connection TLS session? Applies to all workspaces.
1022
+ # @return [Boolean, nil] the field value, or nil when absent.
1023
+ def ftp_tls_session_reuse_required
1024
+ @attributes[:ftp_tls_session_reuse_required]
1025
+ end
1026
+
1015
1027
  # Is SSL required? Disabling this is insecure.
1016
1028
  # @return [Boolean, nil] the field value, or nil when absent.
1017
1029
  def ssl_required
@@ -1256,6 +1268,7 @@ module Files
1256
1268
  # @option params [Integer] session_expiry_minutes Session expiry in minutes
1257
1269
  # @option params [Boolean] legacy_pairing_enabled Allow legacy app pairing in all workspaces? Disabled for new sites; existing sites remain enabled until changed. Disabling blocks new pairing exchanges without revoking existing app credentials or affecting OAuth sign-in. Site administrators can re-enable it.
1258
1270
  # @option params [Boolean] ssl_required Is SSL required? Disabling this is insecure.
1271
+ # @option params [Boolean] ftp_tls_session_reuse_required Require TLS-protected FTP data connections to reuse the control connection TLS session? Applies to all workspaces.
1259
1272
  # @option params [Boolean] sftp_insecure_ciphers If true, we will allow weak and known insecure ciphers to be used for SFTP connections. Enabling this setting severely weakens the security of your site and it is not recommend, except as a last resort for compatibility.
1260
1273
  # @option params [Boolean] sftp_insecure_diffie_hellman If true, we will allow weak Diffie Hellman parameters to be used within ciphers for SFTP that are otherwise on our secure list. This has the effect of making the cipher weaker than our normal threshold for security, but is required to support certain legacy or broken SSH and MFT clients. Enabling this weakens security, but not nearly as much as enabling the full `sftp_insecure_ciphers` option.
1261
1274
  # @option params [Boolean] disable_files_certificate_generation If set, Files.com will not set the CAA records required to generate future SSL certificates for this domain.
@@ -1270,6 +1283,7 @@ module Files
1270
1283
  # @option params [Boolean] allow_user_level_2fa_override Allow the site-wide two-factor authentication requirement to be overriden on a per-user-basis?
1271
1284
  # @option params [Boolean] allow_user_level_allowed_ip_override Allow the site-wide allowed IP restriction to be overriden on a per-user-basis?
1272
1285
  # @option params [Boolean] allow_user_level_ssl_override Allow the site-wide FTP SSL requirement to be overriden on a per-user-basis?
1286
+ # @option params [Boolean] allow_user_level_ftp_tls_session_reuse_override Allow per-user overrides of the FTP TLS session reuse requirement?
1273
1287
  # @option params [Boolean] enforce_parent_site_user_ip_restrictions Require Parent Site users entering this Child Site to match its allowed IP addresses in addition to their existing IP restrictions. Applies to all Workspaces. Defaults to false.
1274
1288
  # @option params [String] disallowed_countries Comma separated list of disallowed Country codes
1275
1289
  # @option params [Integer] days_to_retain_backups Number of days to keep deleted files
@@ -1442,6 +1456,7 @@ module Files
1442
1456
  raise InvalidParameterError.new("Bad parameter: session_expiry_minutes must be an Integer") if !params[:session_expiry_minutes].nil? and !params[:session_expiry_minutes].is_a?(Integer)
1443
1457
  raise InvalidParameterError.new("Bad parameter: legacy_pairing_enabled must be a single value, not #{params[:legacy_pairing_enabled].class}") if [ Array, Hash ].any? { |klass| params[:legacy_pairing_enabled].is_a?(klass) }
1444
1458
  raise InvalidParameterError.new("Bad parameter: ssl_required must be a single value, not #{params[:ssl_required].class}") if [ Array, Hash ].any? { |klass| params[:ssl_required].is_a?(klass) }
1459
+ raise InvalidParameterError.new("Bad parameter: ftp_tls_session_reuse_required must be a single value, not #{params[:ftp_tls_session_reuse_required].class}") if [ Array, Hash ].any? { |klass| params[:ftp_tls_session_reuse_required].is_a?(klass) }
1445
1460
  raise InvalidParameterError.new("Bad parameter: sftp_insecure_ciphers must be a single value, not #{params[:sftp_insecure_ciphers].class}") if [ Array, Hash ].any? { |klass| params[:sftp_insecure_ciphers].is_a?(klass) }
1446
1461
  raise InvalidParameterError.new("Bad parameter: sftp_insecure_diffie_hellman must be a single value, not #{params[:sftp_insecure_diffie_hellman].class}") if [ Array, Hash ].any? { |klass| params[:sftp_insecure_diffie_hellman].is_a?(klass) }
1447
1462
  raise InvalidParameterError.new("Bad parameter: disable_files_certificate_generation must be a single value, not #{params[:disable_files_certificate_generation].class}") if [ Array, Hash ].any? { |klass| params[:disable_files_certificate_generation].is_a?(klass) }
@@ -1456,6 +1471,7 @@ module Files
1456
1471
  raise InvalidParameterError.new("Bad parameter: allow_user_level_2fa_override must be a single value, not #{params[:allow_user_level_2fa_override].class}") if [ Array, Hash ].any? { |klass| params[:allow_user_level_2fa_override].is_a?(klass) }
1457
1472
  raise InvalidParameterError.new("Bad parameter: allow_user_level_allowed_ip_override must be a single value, not #{params[:allow_user_level_allowed_ip_override].class}") if [ Array, Hash ].any? { |klass| params[:allow_user_level_allowed_ip_override].is_a?(klass) }
1458
1473
  raise InvalidParameterError.new("Bad parameter: allow_user_level_ssl_override must be a single value, not #{params[:allow_user_level_ssl_override].class}") if [ Array, Hash ].any? { |klass| params[:allow_user_level_ssl_override].is_a?(klass) }
1474
+ raise InvalidParameterError.new("Bad parameter: allow_user_level_ftp_tls_session_reuse_override must be a single value, not #{params[:allow_user_level_ftp_tls_session_reuse_override].class}") if [ Array, Hash ].any? { |klass| params[:allow_user_level_ftp_tls_session_reuse_override].is_a?(klass) }
1459
1475
  raise InvalidParameterError.new("Bad parameter: enforce_parent_site_user_ip_restrictions must be a single value, not #{params[:enforce_parent_site_user_ip_restrictions].class}") if [ Array, Hash ].any? { |klass| params[:enforce_parent_site_user_ip_restrictions].is_a?(klass) }
1460
1476
  raise InvalidParameterError.new("Bad parameter: disallowed_countries must be an String") if !params[:disallowed_countries].nil? and !params[:disallowed_countries].is_a?(String)
1461
1477
  raise InvalidParameterError.new("Bad parameter: days_to_retain_backups must be an Integer") if !params[:days_to_retain_backups].nil? and !params[:days_to_retain_backups].is_a?(Integer)
@@ -1011,6 +1011,19 @@ module Files
1011
1011
  @attributes[:skip_welcome_screen] = value
1012
1012
  end
1013
1013
 
1014
+ # FTP TLS session reuse requirement: use_system_setting, always_require, or never_require
1015
+ # @return [String, nil] the field value, or nil when absent.
1016
+ def ftp_tls_session_reuse_required
1017
+ @attributes[:ftp_tls_session_reuse_required]
1018
+ end
1019
+
1020
+ # Changes the local field value. Call {#save} to persist changes.
1021
+ # @param value [String, nil] the new field value.
1022
+ # @return [String, nil] the assigned value.
1023
+ def ftp_tls_session_reuse_required=(value)
1024
+ @attributes[:ftp_tls_session_reuse_required] = value
1025
+ end
1026
+
1014
1027
  # SSL required setting
1015
1028
  # @return [String, nil] the field value, or nil when absent.
1016
1029
  def ssl_required
@@ -1429,6 +1442,7 @@ module Files
1429
1442
  # @option params [Boolean] site_admin Is the user an administrator for this site?
1430
1443
  # @option params [Boolean] skip_welcome_screen Skip Welcome page in the UI?
1431
1444
  # @option params [String] ssl_required SSL required setting
1445
+ # @option params [String] ftp_tls_session_reuse_required FTP TLS session reuse requirement: use_system_setting, always_require, or never_require
1432
1446
  # @option params [Integer] sso_strategy_id SSO (Single Sign On) strategy ID for the user, if applicable.
1433
1447
  # @option params [String] require_2fa 2FA required setting. `use_system_setting` uses the site-wide setting, including SSO exemptions. `always_require` and `never_require` override the site-wide setting when user-level overrides are allowed.
1434
1448
  # @option params [String] tags Comma-separated list of Tags for this user. Tags are used for other features, such as UserLifecycleRules, which can target specific tags. Tags must only contain lowercase letters, numbers, and hyphens.
@@ -1507,6 +1521,7 @@ module Files
1507
1521
  raise InvalidParameterError.new("Bad parameter: site_admin must be a single value, not #{params[:site_admin].class}") if [ Array, Hash ].any? { |klass| params[:site_admin].is_a?(klass) }
1508
1522
  raise InvalidParameterError.new("Bad parameter: skip_welcome_screen must be a single value, not #{params[:skip_welcome_screen].class}") if [ Array, Hash ].any? { |klass| params[:skip_welcome_screen].is_a?(klass) }
1509
1523
  raise InvalidParameterError.new("Bad parameter: ssl_required must be an String") if !params[:ssl_required].nil? and !params[:ssl_required].is_a?(String)
1524
+ raise InvalidParameterError.new("Bad parameter: ftp_tls_session_reuse_required must be an String") if !params[:ftp_tls_session_reuse_required].nil? and !params[:ftp_tls_session_reuse_required].is_a?(String)
1510
1525
  raise InvalidParameterError.new("Bad parameter: sso_strategy_id must be an Integer") if !params[:sso_strategy_id].nil? and !params[:sso_strategy_id].is_a?(Integer)
1511
1526
  raise InvalidParameterError.new("Bad parameter: require_2fa must be an String") if !params[:require_2fa].nil? and !params[:require_2fa].is_a?(String)
1512
1527
  raise InvalidParameterError.new("Bad parameter: tags must be an String") if !params[:tags].nil? and !params[:tags].is_a?(String)
@@ -1707,6 +1722,7 @@ module Files
1707
1722
  # @option params [Boolean] site_admin Is the user an administrator for this site?
1708
1723
  # @option params [Boolean] skip_welcome_screen Skip Welcome page in the UI?
1709
1724
  # @option params [String] ssl_required SSL required setting
1725
+ # @option params [String] ftp_tls_session_reuse_required FTP TLS session reuse requirement: use_system_setting, always_require, or never_require
1710
1726
  # @option params [Integer] sso_strategy_id SSO (Single Sign On) strategy ID for the user, if applicable.
1711
1727
  # @option params [String] require_2fa 2FA required setting. `use_system_setting` uses the site-wide setting, including SSO exemptions. `always_require` and `never_require` override the site-wide setting when user-level overrides are allowed.
1712
1728
  # @option params [String] tags Comma-separated list of Tags for this user. Tags are used for other features, such as UserLifecycleRules, which can target specific tags. Tags must only contain lowercase letters, numbers, and hyphens.
@@ -1780,6 +1796,7 @@ module Files
1780
1796
  raise InvalidParameterError.new("Bad parameter: site_admin must be a single value, not #{params[:site_admin].class}") if [ Array, Hash ].any? { |klass| params[:site_admin].is_a?(klass) }
1781
1797
  raise InvalidParameterError.new("Bad parameter: skip_welcome_screen must be a single value, not #{params[:skip_welcome_screen].class}") if [ Array, Hash ].any? { |klass| params[:skip_welcome_screen].is_a?(klass) }
1782
1798
  raise InvalidParameterError.new("Bad parameter: ssl_required must be an String") if !params[:ssl_required].nil? and !params[:ssl_required].is_a?(String)
1799
+ raise InvalidParameterError.new("Bad parameter: ftp_tls_session_reuse_required must be an String") if !params[:ftp_tls_session_reuse_required].nil? and !params[:ftp_tls_session_reuse_required].is_a?(String)
1783
1800
  raise InvalidParameterError.new("Bad parameter: sso_strategy_id must be an Integer") if !params[:sso_strategy_id].nil? and !params[:sso_strategy_id].is_a?(Integer)
1784
1801
  raise InvalidParameterError.new("Bad parameter: require_2fa must be an String") if !params[:require_2fa].nil? and !params[:require_2fa].is_a?(String)
1785
1802
  raise InvalidParameterError.new("Bad parameter: tags must be an String") if !params[:tags].nil? and !params[:tags].is_a?(String)
@@ -1915,6 +1932,7 @@ module Files
1915
1932
  # @option params [Boolean] site_admin Is the user an administrator for this site?
1916
1933
  # @option params [Boolean] skip_welcome_screen Skip Welcome page in the UI?
1917
1934
  # @option params [String] ssl_required SSL required setting
1935
+ # @option params [String] ftp_tls_session_reuse_required FTP TLS session reuse requirement: use_system_setting, always_require, or never_require
1918
1936
  # @option params [Integer] sso_strategy_id SSO (Single Sign On) strategy ID for the user, if applicable.
1919
1937
  # @option params [String] require_2fa 2FA required setting. `use_system_setting` uses the site-wide setting, including SSO exemptions. `always_require` and `never_require` override the site-wide setting when user-level overrides are allowed.
1920
1938
  # @option params [String] tags Comma-separated list of Tags for this user. Tags are used for other features, such as UserLifecycleRules, which can target specific tags. Tags must only contain lowercase letters, numbers, and hyphens.
@@ -1993,6 +2011,7 @@ module Files
1993
2011
  raise InvalidParameterError.new("Bad parameter: site_admin must be a single value, not #{params[:site_admin].class}") if [ Array, Hash ].any? { |klass| params[:site_admin].is_a?(klass) }
1994
2012
  raise InvalidParameterError.new("Bad parameter: skip_welcome_screen must be a single value, not #{params[:skip_welcome_screen].class}") if [ Array, Hash ].any? { |klass| params[:skip_welcome_screen].is_a?(klass) }
1995
2013
  raise InvalidParameterError.new("Bad parameter: ssl_required must be an String") if !params[:ssl_required].nil? and !params[:ssl_required].is_a?(String)
2014
+ raise InvalidParameterError.new("Bad parameter: ftp_tls_session_reuse_required must be an String") if !params[:ftp_tls_session_reuse_required].nil? and !params[:ftp_tls_session_reuse_required].is_a?(String)
1996
2015
  raise InvalidParameterError.new("Bad parameter: sso_strategy_id must be an Integer") if !params[:sso_strategy_id].nil? and !params[:sso_strategy_id].is_a?(Integer)
1997
2016
  raise InvalidParameterError.new("Bad parameter: require_2fa must be an String") if !params[:require_2fa].nil? and !params[:require_2fa].is_a?(String)
1998
2017
  raise InvalidParameterError.new("Bad parameter: tags must be an String") if !params[:tags].nil? and !params[:tags].is_a?(String)
@@ -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