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.
- checksums.yaml +4 -4
- data/_VERSION +1 -1
- data/doc/Files/APIConnectionError.html +1 -1
- data/doc/Files/APIError.html +1 -1
- data/doc/Files/AccountAlreadyExistsError.html +1 -1
- data/doc/Files/AccountLineItem.html +1 -1
- data/doc/Files/AccountOverdueError.html +1 -1
- data/doc/Files/Action.html +1 -1
- data/doc/Files/ActionLog.html +1 -1
- data/doc/Files/ActionNotificationExport.html +1 -1
- data/doc/Files/ActionNotificationExportResult.html +1 -1
- data/doc/Files/AdditionalAuthenticationRequiredError.html +1 -1
- data/doc/Files/AgentNode.html +1 -1
- data/doc/Files/AgentNodeConnection.html +1 -1
- data/doc/Files/AgentNodeInstance.html +1 -1
- data/doc/Files/AgentPushUpdate.html +1 -1
- data/doc/Files/AgentPushUpdateBlockedError.html +1 -1
- data/doc/Files/AgentUnavailableError.html +1 -1
- data/doc/Files/AgentUpgradeRequiredError.html +1 -1
- data/doc/Files/AiAssistantPersonality.html +1 -1
- data/doc/Files/AiTask.html +1 -1
- data/doc/Files/AiTaskCannotBeRunManuallyError.html +1 -1
- data/doc/Files/AlreadyCompletedError.html +1 -1
- data/doc/Files/Api.html +20 -13
- data/doc/Files/ApiClient/RequestLogContext.html +29 -29
- data/doc/Files/ApiClient/TransferSettings.html +721 -0
- data/doc/Files/ApiClient.html +756 -204
- data/doc/Files/ApiKey.html +1 -1
- data/doc/Files/ApiKeyIsDisabledError.html +1 -1
- data/doc/Files/ApiKeyIsPathRestrictedError.html +1 -1
- data/doc/Files/ApiKeyNotFoundError.html +1 -1
- data/doc/Files/ApiKeyOnlyForDesktopAppError.html +1 -1
- data/doc/Files/ApiKeyOnlyForFileOperationsError.html +1 -1
- data/doc/Files/ApiKeyOnlyForMobileAppError.html +1 -1
- data/doc/Files/ApiKeyOnlyForOfficeIntegrationError.html +1 -1
- data/doc/Files/ApiKeySessionsNotSupportedError.html +1 -1
- data/doc/Files/ApiRequestLog.html +1 -1
- data/doc/Files/App.html +1 -1
- data/doc/Files/As2IncomingMessage.html +1 -1
- data/doc/Files/As2OutgoingMessage.html +1 -1
- data/doc/Files/As2Partner.html +1 -1
- data/doc/Files/As2Station.html +1 -1
- data/doc/Files/AttachmentTooLargeError.html +1 -1
- data/doc/Files/AuthenticationError.html +1 -1
- data/doc/Files/AuthenticationRequiredError.html +1 -1
- data/doc/Files/Auto.html +1 -1
- data/doc/Files/Automation.html +1 -1
- data/doc/Files/AutomationAuthoringSchema.html +1 -1
- data/doc/Files/AutomationCannotBeRunManuallyError.html +1 -1
- data/doc/Files/AutomationExecutionNode.html +1 -1
- data/doc/Files/AutomationLog.html +1 -1
- data/doc/Files/AutomationRun.html +1 -1
- data/doc/Files/AutomationsUnavailableError.html +1 -1
- data/doc/Files/BadRequestError.html +1 -1
- data/doc/Files/BandwidthSnapshot.html +1 -1
- data/doc/Files/Behavior.html +1 -1
- data/doc/Files/BehaviorNotAllowedOnRemoteServerError.html +1 -1
- data/doc/Files/BillingInformationHiddenError.html +1 -1
- data/doc/Files/BillingPermissionRequiredError.html +1 -1
- data/doc/Files/BufferedUploadDisabledForThisDestinationError.html +1 -1
- data/doc/Files/Bundle.html +1 -1
- data/doc/Files/BundleAction.html +1 -1
- data/doc/Files/BundleDownload.html +1 -1
- data/doc/Files/BundleMaximumUsesReachedError.html +1 -1
- data/doc/Files/BundleNotification.html +1 -1
- data/doc/Files/BundleOnlyAllowsPreviewsError.html +1 -1
- data/doc/Files/BundleOperationRequiresSubfolderError.html +1 -1
- data/doc/Files/BundlePath.html +1 -1
- data/doc/Files/BundlePathNotFoundError.html +1 -1
- data/doc/Files/BundlePermissionRequiredError.html +1 -1
- data/doc/Files/BundleRecipient.html +1 -1
- data/doc/Files/BundleRegistration.html +1 -1
- data/doc/Files/BundleRegistrationCodeFailedError.html +1 -1
- data/doc/Files/BundleRegistrationNotFoundError.html +1 -1
- data/doc/Files/CannotAdministerHigherLevelUserError.html +1 -1
- data/doc/Files/CannotDownloadDirectoryError.html +1 -1
- data/doc/Files/CannotLoginWhileUsingKeyError.html +1 -1
- data/doc/Files/CantActForOtherUserError.html +1 -1
- data/doc/Files/CantMoveWithMultipleLocationsError.html +1 -1
- data/doc/Files/ChatMessage.html +1 -1
- data/doc/Files/ChatSession.html +1 -1
- data/doc/Files/ChildSiteManagementPolicy.html +1 -1
- data/doc/Files/Clickwrap.html +1 -1
- data/doc/Files/CodeNotFoundError.html +1 -1
- data/doc/Files/ConfigurationLockedPathError.html +1 -1
- data/doc/Files/ContactAdminForPasswordChangeHelpError.html +1 -1
- data/doc/Files/CouldNotCreateParentError.html +1 -1
- data/doc/Files/CustomDomain.html +1 -1
- data/doc/Files/DatetimeParseError.html +1 -1
- data/doc/Files/DesktopConfigurationProfile.html +1 -1
- data/doc/Files/DestinationExistsError.html +1 -1
- data/doc/Files/DestinationFolderLimitedError.html +1 -1
- data/doc/Files/DestinationParentConflictError.html +1 -1
- data/doc/Files/DestinationParentDoesNotExistError.html +1 -1
- data/doc/Files/DestinationSameError.html +1 -1
- data/doc/Files/DestinationSiteMismatchError.html +1 -1
- data/doc/Files/DirectConnectionInfo.html +1 -1
- data/doc/Files/DnsRecord.html +1 -1
- data/doc/Files/DoesNotSupportSortingError.html +1 -1
- data/doc/Files/DuplicateShareRecipientError.html +1 -1
- data/doc/Files/EmailIncomingMessage.html +1 -1
- data/doc/Files/EmailLog.html +1 -1
- data/doc/Files/Error.html +2 -2
- data/doc/Files/Errors.html +1 -1
- data/doc/Files/EventChannel.html +1 -1
- data/doc/Files/EventDeliveryAttempt.html +1 -1
- data/doc/Files/EventRecord.html +1 -1
- data/doc/Files/EventSubscription.html +1 -1
- data/doc/Files/EventTarget.html +1 -1
- data/doc/Files/ExavaultApiRequestLog.html +1 -1
- data/doc/Files/ExceededRuntimeLimitError.html +1 -1
- data/doc/Files/Expectation.html +1 -1
- data/doc/Files/ExpectationAlreadyHasOpenWindowError.html +1 -1
- data/doc/Files/ExpectationEvaluation.html +1 -1
- data/doc/Files/ExpectationIncident.html +1 -1
- data/doc/Files/ExpectationNotManualTriggerError.html +1 -1
- data/doc/Files/ExpiredPrivateKeyError.html +1 -1
- data/doc/Files/ExpiredPublicKeyError.html +1 -1
- data/doc/Files/ExportFailureError.html +1 -1
- data/doc/Files/ExportNotReadyError.html +1 -1
- data/doc/Files/ExternalEvent.html +1 -1
- data/doc/Files/FailedToChangePasswordError.html +1 -1
- data/doc/Files/File.html +2019 -1515
- data/doc/Files/FileAction.html +1 -1
- data/doc/Files/FileActionError.html +1 -1
- data/doc/Files/FileComment.html +1 -1
- data/doc/Files/FileCommentReaction.html +1 -1
- data/doc/Files/FileLockedError.html +1 -1
- data/doc/Files/FileMigration.html +1 -1
- data/doc/Files/FileMigrationLog.html +1 -1
- data/doc/Files/FileNotFoundError.html +1 -1
- data/doc/Files/FileNotUploadedError.html +1 -1
- data/doc/Files/FilePendingProcessingError.html +1 -1
- data/doc/Files/FileProcessingErrorError.html +1 -1
- data/doc/Files/FileTooBigToDecryptError.html +1 -1
- data/doc/Files/FileTooBigToEncryptError.html +1 -1
- data/doc/Files/FileUploadNotFoundError.html +1 -1
- data/doc/Files/FileUploadPart.html +1 -1
- data/doc/Files/FileUploadedToWrongRegionError.html +1 -1
- data/doc/Files/FileUtils.html +1 -1
- data/doc/Files/FilenameTooLongError.html +1 -1
- data/doc/Files/FilesAgentFailedAuthorizationError.html +1 -1
- data/doc/Files/FilesNativeStorageOverloadedError.html +1 -1
- data/doc/Files/Folder.html +1 -1
- data/doc/Files/FolderAdminOrBillingPermissionRequiredError.html +1 -1
- data/doc/Files/FolderAdminPermissionRequiredError.html +1 -1
- data/doc/Files/FolderLockedError.html +1 -1
- data/doc/Files/FolderMustNotBeAFileError.html +1 -1
- data/doc/Files/FolderNotEmptyError.html +1 -1
- data/doc/Files/FoldersNotAllowedError.html +1 -1
- data/doc/Files/FormField.html +1 -1
- data/doc/Files/FormFieldSet.html +1 -1
- data/doc/Files/FtpActionLog.html +1 -1
- data/doc/Files/FullPermissionRequiredError.html +1 -1
- data/doc/Files/GpgKey.html +1 -1
- data/doc/Files/Group.html +1 -1
- data/doc/Files/GroupNotFoundError.html +1 -1
- data/doc/Files/GroupUser.html +1 -1
- data/doc/Files/History.html +1 -1
- data/doc/Files/HistoryExport.html +1 -1
- data/doc/Files/HistoryExportResult.html +1 -1
- data/doc/Files/HistoryPermissionRequiredError.html +1 -1
- data/doc/Files/HistoryUnavailableError.html +1 -1
- data/doc/Files/HolidayCalendar.html +1 -1
- data/doc/Files/HolidayRegion.html +1 -1
- data/doc/Files/Image.html +1 -1
- data/doc/Files/InAppAiAssistantUnavailableError.html +1 -1
- data/doc/Files/InboundS3Log.html +1 -1
- data/doc/Files/InboxNotFoundError.html +1 -1
- data/doc/Files/InboxRecipient.html +1 -1
- data/doc/Files/InboxRegistration.html +1 -1
- data/doc/Files/InboxRegistrationCodeFailedError.html +1 -1
- data/doc/Files/InboxUpload.html +1 -1
- data/doc/Files/InsufficientPermissionForParamsError.html +1 -1
- data/doc/Files/InsufficientPermissionForSiteError.html +1 -1
- data/doc/Files/IntegrationCentricProfile.html +1 -1
- data/doc/Files/InternalGeneralErrorError.html +1 -1
- data/doc/Files/InvalidBodyError.html +1 -1
- data/doc/Files/InvalidBundleCodeError.html +1 -1
- data/doc/Files/InvalidCredentialsError.html +1 -1
- data/doc/Files/InvalidCursorError.html +1 -1
- data/doc/Files/InvalidCursorTypeForSortError.html +1 -1
- data/doc/Files/InvalidEtagsError.html +1 -1
- data/doc/Files/InvalidFileTypeError.html +1 -1
- data/doc/Files/InvalidFilenameError.html +1 -1
- data/doc/Files/InvalidFilterAliasCombinationError.html +1 -1
- data/doc/Files/InvalidFilterFieldError.html +1 -1
- data/doc/Files/InvalidFilterParamError.html +1 -1
- data/doc/Files/InvalidFilterParamFormatError.html +1 -1
- data/doc/Files/InvalidFilterParamValueError.html +1 -1
- data/doc/Files/InvalidInputEncodingError.html +1 -1
- data/doc/Files/InvalidInterfaceError.html +1 -1
- data/doc/Files/InvalidOauthError.html +1 -1
- data/doc/Files/InvalidOauthProviderError.html +1 -1
- data/doc/Files/InvalidOrExpiredCodeError.html +1 -1
- data/doc/Files/InvalidParameterError.html +5 -1
- data/doc/Files/InvalidPathError.html +1 -1
- data/doc/Files/InvalidPriorityColorError.html +1 -1
- data/doc/Files/InvalidRangeError.html +1 -1
- data/doc/Files/InvalidReturnToUrlError.html +1 -1
- data/doc/Files/InvalidSearchQueryError.html +1 -1
- data/doc/Files/InvalidSessionError.html +1 -1
- data/doc/Files/InvalidSiteError.html +1 -1
- data/doc/Files/InvalidSortFieldError.html +1 -1
- data/doc/Files/InvalidSortFilterCombinationError.html +1 -1
- data/doc/Files/InvalidUploadOffsetError.html +1 -1
- data/doc/Files/InvalidUploadPartGapError.html +1 -1
- data/doc/Files/InvalidUploadPartSizeError.html +1 -1
- data/doc/Files/InvalidUsernameOrPasswordError.html +1 -1
- data/doc/Files/InvalidWorkspaceIdHeaderError.html +1 -1
- data/doc/Files/InvalidZipFileError.html +1 -1
- data/doc/Files/Invoice.html +1 -1
- data/doc/Files/InvoiceLineItem.html +1 -1
- data/doc/Files/IpAddress.html +1 -1
- data/doc/Files/KeyLifecycleRule.html +1 -1
- data/doc/Files/List.html +1 -1
- data/doc/Files/Lock.html +1 -1
- data/doc/Files/LockOperationBusyError.html +1 -1
- data/doc/Files/LockedOutError.html +1 -1
- data/doc/Files/LockoutRegionMismatchError.html +1 -1
- data/doc/Files/MetadataCategory.html +1 -1
- data/doc/Files/MetadataNotSupportedOnRemotesError.html +1 -1
- data/doc/Files/MethodNotAllowedError.html +1 -1
- data/doc/Files/MigrationInProgressError.html +1 -1
- data/doc/Files/MissingParameterError.html +1 -1
- data/doc/Files/ModelSaveErrorError.html +1 -1
- data/doc/Files/MoverAccessDeniedError.html +1 -1
- data/doc/Files/MoverPackageRequiredError.html +1 -1
- data/doc/Files/MultipleProcessingErrorsError.html +1 -1
- data/doc/Files/MultipleSortParamsNotAllowedError.html +1 -1
- data/doc/Files/MustAuthenticateWithApiKeyError.html +1 -1
- data/doc/Files/NeedAdminPermissionForInboxError.html +1 -1
- data/doc/Files/NestedNotFoundError.html +1 -1
- data/doc/Files/NoAccountForSiteError.html +1 -1
- data/doc/Files/NoValidInputParamsError.html +1 -1
- data/doc/Files/NonAdminsMustQueryByFolderOrPathError.html +1 -1
- data/doc/Files/NotAllowedToCreateBundleError.html +1 -1
- data/doc/Files/NotAuthenticatedError.html +1 -1
- data/doc/Files/NotAuthorizedError.html +1 -1
- data/doc/Files/NotEnqueuableSyncError.html +1 -1
- data/doc/Files/NotFoundError.html +1 -1
- data/doc/Files/NotImplementedError.html +1 -1
- data/doc/Files/Notification.html +1 -1
- data/doc/Files/OffsetUploadNotAllowedWithMalwareScanningError.html +1 -1
- data/doc/Files/OneTimePasswordIncorrectError.html +1 -1
- data/doc/Files/OutboundConnectionLog.html +1 -1
- data/doc/Files/PartNumberTooLargeError.html +1 -1
- data/doc/Files/Partner.html +1 -1
- data/doc/Files/PartnerChannel.html +1 -1
- data/doc/Files/PartnerChannelTemplate.html +1 -1
- data/doc/Files/PartnerConnection.html +1 -1
- data/doc/Files/PartnerSite.html +1 -1
- data/doc/Files/PartnerSiteRequest.html +1 -1
- data/doc/Files/PasswordChangeNotRequiredError.html +1 -1
- data/doc/Files/PasswordChangeRequiredError.html +1 -1
- data/doc/Files/PathCannotHaveTrailingWhitespaceError.html +1 -1
- data/doc/Files/PathTooLongError.html +1 -1
- data/doc/Files/PathUtil.html +1 -1
- data/doc/Files/Payment.html +1 -1
- data/doc/Files/PaymentLineItem.html +1 -1
- data/doc/Files/PaymentMethodErrorError.html +1 -1
- data/doc/Files/PendingWorkEvent.html +1 -1
- data/doc/Files/Permission.html +1 -1
- data/doc/Files/PlanNotFoundError.html +1 -1
- data/doc/Files/Preview.html +1 -1
- data/doc/Files/PreviewOnlyPermissionCannotDownloadError.html +1 -1
- data/doc/Files/ProcessingFailureError.html +1 -1
- data/doc/Files/PublicHostingRequestLog.html +1 -1
- data/doc/Files/PublicIpAddress.html +1 -1
- data/doc/Files/PublicKey.html +1 -1
- data/doc/Files/RateLimitedError.html +1 -1
- data/doc/Files/ReadOnlySessionError.html +1 -1
- data/doc/Files/ReadPermissionRequiredError.html +1 -1
- data/doc/Files/ReauthenticationFailedError.html +1 -1
- data/doc/Files/ReauthenticationFailedFinalError.html +1 -1
- data/doc/Files/ReauthenticationNeededActionError.html +1 -1
- data/doc/Files/ReauthenticationNeededFieldsError.html +1 -1
- data/doc/Files/ReauthenticationRateLimitedError.html +1 -1
- data/doc/Files/RecaptchaFailedError.html +1 -1
- data/doc/Files/RecipientAlreadySharedError.html +1 -1
- data/doc/Files/RemoteBandwidthSnapshot.html +1 -1
- data/doc/Files/RemoteDesktopDebugLoggingDisabledError.html +1 -1
- data/doc/Files/RemoteEntryReadOnlyError.html +1 -1
- data/doc/Files/RemoteMountBackend.html +1 -1
- data/doc/Files/RemoteServer.html +1 -1
- data/doc/Files/RemoteServerConfigurationFile.html +1 -1
- data/doc/Files/RemoteServerCredential.html +1 -1
- data/doc/Files/RemoteServerErrorError.html +1 -1
- data/doc/Files/Request.html +1 -1
- data/doc/Files/RequestBodyTooLargeError.html +1 -1
- data/doc/Files/RequestParamsContainInvalidCharacterError.html +1 -1
- data/doc/Files/RequestParamsInvalidError.html +1 -1
- data/doc/Files/RequestParamsRequiredError.html +1 -1
- data/doc/Files/ResourceBelongsToParentSiteError.html +1 -1
- data/doc/Files/ResourceLockedError.html +1 -1
- data/doc/Files/Response.html +1 -1
- data/doc/Files/Restore.html +1 -1
- data/doc/Files/RootFolderBehaviorSiteAdminRequiredError.html +1 -1
- data/doc/Files/RootFolderBehaviorSkipSiteAdminRequiredError.html +1 -1
- data/doc/Files/Schedule.html +1 -1
- data/doc/Files/ScheduledExport.html +1 -1
- data/doc/Files/ScimLog.html +1 -1
- data/doc/Files/SearchAllOnChildPathError.html +1 -1
- data/doc/Files/SearchUnavailableError.html +1 -1
- data/doc/Files/Secret.html +1 -1
- data/doc/Files/SelfManagedRequiredError.html +1 -1
- data/doc/Files/ServiceUnavailableError.html +1 -1
- data/doc/Files/Session.html +1 -1
- data/doc/Files/SettingsChange.html +1 -1
- data/doc/Files/SftpActionLog.html +1 -1
- data/doc/Files/SftpHostKey.html +1 -1
- data/doc/Files/ShareGroup.html +1 -1
- data/doc/Files/ShareGroupMember.html +1 -1
- data/doc/Files/SiemHttpDestination.html +1 -1
- data/doc/Files/SiemHttpDestinationEvent.html +1 -1
- data/doc/Files/Site.html +1 -1
- data/doc/Files/SiteAdminOrPartnerAdminPermissionRequiredError.html +1 -1
- data/doc/Files/SiteAdminOrWorkspaceAdminOrFolderAdminPermissionRequiredError.html +1 -1
- data/doc/Files/SiteAdminOrWorkspaceAdminOrPartnerAdminOrFolderAdminPermissionRequiredError.html +1 -1
- data/doc/Files/SiteAdminOrWorkspaceAdminOrPartnerAdminPermissionRequiredError.html +1 -1
- data/doc/Files/SiteAdminOrWorkspaceAdminPermissionRequiredError.html +1 -1
- data/doc/Files/SiteAdminRequiredError.html +1 -1
- data/doc/Files/SiteConfigurationError.html +1 -1
- data/doc/Files/SiteDisabledError.html +1 -1
- data/doc/Files/SiteFilesAreImmutableError.html +1 -1
- data/doc/Files/SiteNotFoundError.html +1 -1
- data/doc/Files/SiteSubdomainRedirect.html +1 -1
- data/doc/Files/SiteWasRemovedError.html +1 -1
- data/doc/Files/SizableIO.html +1 -1
- data/doc/Files/Snapshot.html +1 -1
- data/doc/Files/SsoEvent.html +1 -1
- data/doc/Files/SsoStrategy.html +1 -1
- data/doc/Files/Status.html +1 -1
- data/doc/Files/Style.html +1 -1
- data/doc/Files/SubfolderLockedError.html +1 -1
- data/doc/Files/Sync.html +1 -1
- data/doc/Files/SyncInProgressError.html +1 -1
- data/doc/Files/SyncLog.html +1 -1
- data/doc/Files/SyncRun.html +1 -1
- data/doc/Files/SyncRunLiveTransfer.html +1 -1
- data/doc/Files/SystemProfiler.html +1 -1
- data/doc/Files/TooManyConcurrentLoginsError.html +1 -1
- data/doc/Files/TooManyConcurrentRequestsError.html +1 -1
- data/doc/Files/TooManyLoginAttemptsError.html +1 -1
- data/doc/Files/TooManyRequestsError.html +1 -1
- data/doc/Files/TooManySharesError.html +1 -1
- data/doc/Files/Transfer/BufferLimitError.html +175 -0
- data/doc/Files/Transfer/ByteBudget/Reservation.html +301 -0
- data/doc/Files/Transfer/ByteBudget.html +748 -0
- data/doc/Files/Transfer/CancelledError.html +235 -0
- data/doc/Files/Transfer/Checkpoint.html +1371 -0
- data/doc/Files/Transfer/CheckpointError.html +173 -0
- data/doc/Files/Transfer/ClientTransport/ClientResponse.html +661 -0
- data/doc/Files/Transfer/ClientTransport/Reader.html +530 -0
- data/doc/Files/Transfer/ClientTransport.html +675 -0
- data/doc/Files/Transfer/Clock.html +189 -0
- data/doc/Files/Transfer/Control.html +667 -0
- data/doc/Files/Transfer/Controller/Attempt.html +465 -0
- data/doc/Files/Transfer/Controller.html +1166 -0
- data/doc/Files/Transfer/Coordinator/Timing.html +568 -0
- data/doc/Files/Transfer/Coordinator.html +534 -0
- data/doc/Files/Transfer/DownloadCoordinator/FileSink.html +976 -0
- data/doc/Files/Transfer/DownloadCoordinator/IoSink.html +1168 -0
- data/doc/Files/Transfer/DownloadCoordinator/Piece.html +875 -0
- data/doc/Files/Transfer/DownloadCoordinator.html +728 -0
- data/doc/Files/Transfer/FileSlots.html +480 -0
- data/doc/Files/Transfer/FrontendResponseError.html +235 -0
- data/doc/Files/Transfer/Holds.html +505 -0
- data/doc/Files/Transfer/HttpTransport.html +579 -0
- data/doc/Files/Transfer/Job.html +2338 -0
- data/doc/Files/Transfer/Options.html +924 -0
- data/doc/Files/Transfer/PausedError.html +319 -0
- data/doc/Files/Transfer/Policy.html +1642 -0
- data/doc/Files/Transfer/RequestPermits.html +160 -0
- data/doc/Files/Transfer/SourceChangedError.html +235 -0
- data/doc/Files/Transfer/StorageResponse.html +546 -0
- data/doc/Files/Transfer/StorageRetry.html +1098 -0
- data/doc/Files/Transfer/UploadCoordinator/Grant.html +1048 -0
- data/doc/Files/Transfer/UploadCoordinator/Part.html +1048 -0
- data/doc/Files/Transfer/UploadCoordinator/Region.html +295 -0
- data/doc/Files/Transfer/UploadCoordinator/Segments.html +299 -0
- data/doc/Files/Transfer/UploadCoordinator/Source.html +725 -0
- data/doc/Files/Transfer/UploadCoordinator/StreamSource.html +664 -0
- data/doc/Files/Transfer/UploadCoordinator.html +546 -0
- data/doc/Files/Transfer/UploadPlan.html +522 -0
- data/doc/Files/Transfer/VersionChangedError.html +235 -0
- data/doc/Files/Transfer.html +134 -0
- data/doc/Files/TrialExpiredError.html +1 -1
- data/doc/Files/TrialLockedError.html +1 -1
- data/doc/Files/TwoFactorAuthenticationCodeAlreadySentError.html +1 -1
- data/doc/Files/TwoFactorAuthenticationCountryBlacklistedError.html +1 -1
- data/doc/Files/TwoFactorAuthenticationErrorError.html +1 -1
- data/doc/Files/TwoFactorAuthenticationGeneralErrorError.html +1 -1
- data/doc/Files/TwoFactorAuthenticationMethodUnsupportedErrorError.html +1 -1
- data/doc/Files/TwoFactorAuthenticationRequiredError.html +1 -1
- data/doc/Files/TwoFactorAuthenticationSetupExpiredError.html +1 -1
- data/doc/Files/TwoFactorAuthenticationUnsubscribedRecipientError.html +1 -1
- data/doc/Files/URI.html +1 -1
- data/doc/Files/UnrecognizedSortIndexError.html +1 -1
- data/doc/Files/UnsupportedCurrencyError.html +1 -1
- data/doc/Files/UnsupportedHttpResponseFormatError.html +1 -1
- data/doc/Files/UnsupportedMediaTypeError.html +1 -1
- data/doc/Files/UpdatesNotAllowedForRemotesError.html +1 -1
- data/doc/Files/UploadsUnavailableError.html +1 -1
- data/doc/Files/UsageByTopLevelDir.html +1 -1
- data/doc/Files/UsageDailySnapshot.html +1 -1
- data/doc/Files/UsageSnapshot.html +1 -1
- data/doc/Files/User.html +1 -1
- data/doc/Files/UserAdditionalEmailRecipient.html +1 -1
- data/doc/Files/UserCipherUse.html +1 -1
- data/doc/Files/UserIdInvalidError.html +1 -1
- data/doc/Files/UserIdOnUserEndpointError.html +1 -1
- data/doc/Files/UserIdWithoutSiteAdminError.html +1 -1
- data/doc/Files/UserLifecycleRule.html +1 -1
- data/doc/Files/UserNotFoundError.html +1 -1
- data/doc/Files/UserRequest.html +1 -1
- data/doc/Files/UserRequestsEnabledRequiredError.html +1 -1
- data/doc/Files/UserRequiredError.html +1 -1
- data/doc/Files/UserSecurityEvent.html +1 -1
- data/doc/Files/UserSftpClientUse.html +1 -1
- data/doc/Files/Util/LogSettings.html +1 -1
- data/doc/Files/Util.html +1 -1
- data/doc/Files/WebDavActionLog.html +1 -1
- data/doc/Files/WebhookTest.html +1 -1
- data/doc/Files/Workspace.html +1 -1
- data/doc/Files/WriteAndBundlePermissionRequiredError.html +1 -1
- data/doc/Files/WritePermissionRequiredError.html +1 -1
- data/doc/Files/ZipListEntry.html +1 -1
- data/doc/Files.html +116 -116
- data/doc/_index.html +314 -4
- data/doc/class_list.html +1 -1
- data/doc/file.README.html +1 -1
- data/doc/index.html +1 -1
- data/doc/method_list.html +4703 -2415
- data/doc/top-level-namespace.html +1 -1
- data/docs/file.md +346 -0
- data/lib/files.com/api.rb +6 -1
- data/lib/files.com/api_client.rb +220 -32
- data/lib/files.com/models/file.rb +124 -17
- data/lib/files.com/transfer/budgets.rb +123 -0
- data/lib/files.com/transfer/checkpoint.rb +113 -0
- data/lib/files.com/transfer/clock.rb +14 -0
- data/lib/files.com/transfer/controller.rb +549 -0
- data/lib/files.com/transfer/download_coordinator.rb +906 -0
- data/lib/files.com/transfer/errors.rb +100 -0
- data/lib/files.com/transfer/http_transport.rb +337 -0
- data/lib/files.com/transfer/job.rb +778 -0
- data/lib/files.com/transfer/options.rb +126 -0
- data/lib/files.com/transfer/policy.rb +91 -0
- data/lib/files.com/transfer/storage_retry.rb +169 -0
- data/lib/files.com/transfer/upload_coordinator.rb +657 -0
- data/lib/files.com/transfer/upload_plan.rb +157 -0
- data/lib/files.com.rb +13 -0
- data/spec/lib/api_client_spec.rb +125 -3
- data/spec/models/file_transfer_spec.rb +53 -4
- data/spec/support/transfer_server.rb +113 -14
- data/spec/transfer/adaptive_download_spec.rb +1102 -0
- data/spec/transfer/adaptive_upload_spec.rb +1602 -0
- data/spec/transfer/concurrency_controller_spec.rb +382 -0
- data/spec/workspace_id_spec.rb +19 -14
- metadata +59 -1
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
# The real controller and policies, driven by a clock the examples move by hand. Every attempt starts and finishes at
|
|
4
|
+
# a time the example chooses; nothing in the controller is stubbed.
|
|
5
|
+
RSpec.describe Files::Transfer::Controller do
|
|
6
|
+
let(:mib) { 1024 * 1024 }
|
|
7
|
+
let(:clock) do
|
|
8
|
+
Class.new do
|
|
9
|
+
attr_reader :now
|
|
10
|
+
|
|
11
|
+
def initialize
|
|
12
|
+
@now = 0.0
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def advance(seconds)
|
|
16
|
+
@now += seconds
|
|
17
|
+
end
|
|
18
|
+
end.new
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def controller(policy)
|
|
22
|
+
described_class.new(policy, clock: clock)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def storage(startup = :default, **options)
|
|
26
|
+
Files::Transfer::Policy.storage_upload(startup: startup, **options)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Starts count attempts at once, lets seconds pass and finishes each with bytes.
|
|
30
|
+
def round(controller, seconds:, bytes: mib, count: controller.limit)
|
|
31
|
+
attempts = Array.new(count) { controller.start_attempt }
|
|
32
|
+
clock.advance(seconds)
|
|
33
|
+
attempts.each { |attempt| controller.finish_attempt(attempt, :success, bytes: bytes) }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def reasons(controller)
|
|
37
|
+
controller.decisions.map { |decision| decision[:reason] }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
describe "starting" do
|
|
41
|
+
it "starts an upload to cloud storage at one request by default and at 32 when aggressive" do
|
|
42
|
+
expect([ controller(storage(:default)).limit, controller(storage(:default)).phase ]).to eq([ 1, :warm ])
|
|
43
|
+
expect([ controller(storage(:aggressive)).limit, controller(storage(:aggressive)).phase ]).to eq([ 32, :ordinary ])
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "starts direct destinations at 4 and downloads at 32, without a warm start" do
|
|
47
|
+
direct = controller(Files::Transfer::Policy.direct_upload)
|
|
48
|
+
download = controller(Files::Transfer::Policy.download)
|
|
49
|
+
|
|
50
|
+
expect([ direct.limit, direct.phase, direct.policy.max ]).to eq([ 4, :ordinary, 16 ])
|
|
51
|
+
expect([ download.limit, download.phase, download.policy.max ]).to eq([ 32, :ordinary, 64 ])
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it "lets the caller's max_concurrency cap every start and maximum" do
|
|
55
|
+
expect(controller(storage(:aggressive, max_concurrency: 8)).limit).to eq(8)
|
|
56
|
+
expect(controller(Files::Transfer::Policy.direct_upload(max_concurrency: 2)).limit).to eq(2)
|
|
57
|
+
expect(Files::Transfer::Policy.direct_upload(max_concurrency: 200).max).to eq(16)
|
|
58
|
+
expect(storage(max_concurrency: 200).max).to eq(200)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
describe "the warm start" do
|
|
63
|
+
it "warms the connection with its first window, takes its second as the baseline and probes upward while each step pays off" do
|
|
64
|
+
warm = controller(storage)
|
|
65
|
+
round(warm, seconds: 0.25)
|
|
66
|
+
expect([ warm.limit, reasons(warm) ]).to eq([ 1, %i[start warm_connection] ])
|
|
67
|
+
|
|
68
|
+
2.times { round(warm, seconds: 0.25) } # the baseline, one request at a time: 4 MiB/s
|
|
69
|
+
expect(warm.limit).to eq(2)
|
|
70
|
+
|
|
71
|
+
round(warm, seconds: 0.25) # the round that filled the raised limit ends
|
|
72
|
+
expect(warm.limit).to eq(2)
|
|
73
|
+
round(warm, seconds: 0.25) # two at once: 8 MiB/s, at least 4 * (1 + 0.5 * 1 / 1)
|
|
74
|
+
expect(warm.limit).to eq(3)
|
|
75
|
+
expect(reasons(warm)).to eq(%i[start warm_connection warm_probe warm_settling warm_settled warm_probe])
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it "judges a probe once every attempt that filled its raised limit has ended, or once enough answers came without one" do
|
|
79
|
+
warm = controller(storage)
|
|
80
|
+
3.times { round(warm, seconds: 0.25) }
|
|
81
|
+
first, slow = Array.new(2) { warm.start_attempt } # they fill the raised limit of two
|
|
82
|
+
clock.advance(0.25)
|
|
83
|
+
warm.finish_attempt(first, :success, bytes: mib)
|
|
84
|
+
# While the slow one is still out, one more attempt at a time keeps the two the limit allows on the network.
|
|
85
|
+
round(warm, seconds: 0.125, count: 1) # a later, quicker answer closes a window, but the slow one is still out
|
|
86
|
+
expect([ warm.limit, reasons(warm).last ]).to eq([ 2, :warm_settling ])
|
|
87
|
+
|
|
88
|
+
13.times { round(warm, seconds: 0.125, count: 1) } # fifteen answers since the raise
|
|
89
|
+
expect([ warm.limit, warm.admitted ]).to eq([ 2, 1 ])
|
|
90
|
+
round(warm, seconds: 0.125, count: 1) # sixteen, at 8 MiB/s: the window then closing judges the probe
|
|
91
|
+
expect(warm.limit).to eq(3)
|
|
92
|
+
warm.finish_attempt(slow, :success, bytes: mib)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it "returns after a probe that does not pay off, rests two windows of ordinary size and probes again by one" do
|
|
96
|
+
warm = controller(storage)
|
|
97
|
+
4.times { round(warm, seconds: 0.25) } # warmed, the baseline, raised to two and settled
|
|
98
|
+
round(warm, seconds: 0.5) # two at once, each twice as slow: no gain
|
|
99
|
+
expect(warm.limit).to eq(1)
|
|
100
|
+
|
|
101
|
+
16.times { round(warm, seconds: 0.25) } # one rest window of sixteen answers
|
|
102
|
+
expect([ warm.limit, reasons(warm).last ]).to eq([ 1, :rest ])
|
|
103
|
+
16.times { round(warm, seconds: 0.25) } # the last rest window is the next baseline
|
|
104
|
+
expect(warm.limit).to eq(2)
|
|
105
|
+
expect(reasons(warm)).to eq(%i[start warm_connection warm_probe warm_settling warm_settled warm_reject rest warm_probe])
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
it "keeps warming below its start after a second rejected probe" do
|
|
109
|
+
warm = controller(storage)
|
|
110
|
+
4.times { round(warm, seconds: 0.25) }
|
|
111
|
+
round(warm, seconds: 0.5)
|
|
112
|
+
32.times { round(warm, seconds: 0.25) }
|
|
113
|
+
round(warm, seconds: 0.25)
|
|
114
|
+
round(warm, seconds: 0.5) # no gain again
|
|
115
|
+
|
|
116
|
+
expect([ warm.limit, warm.phase ]).to eq([ 1, :warm ])
|
|
117
|
+
expect(reasons(warm).last(2)).to eq(%i[warm_settled warm_reject])
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
it "rests instead of probing when its baseline already queues" do
|
|
121
|
+
warm = controller(storage)
|
|
122
|
+
round(warm, seconds: 0.25)
|
|
123
|
+
2.times { round(warm, seconds: 0.75) } # each answer three times slower than the warming one
|
|
124
|
+
expect([ warm.limit, reasons(warm).last ]).to eq([ 1, :warm_queued ])
|
|
125
|
+
|
|
126
|
+
32.times { round(warm, seconds: 0.25) } # two rest windows of sixteen answers
|
|
127
|
+
expect(warm.limit).to eq(2)
|
|
128
|
+
expect(reasons(warm).last(3)).to eq(%i[warm_queued rest warm_probe])
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
it "abandons a probe after a failure in any epoch and rests where the failure left it, still warming" do
|
|
132
|
+
current = controller(storage)
|
|
133
|
+
3.times { round(current, seconds: 0.25) }
|
|
134
|
+
failing, other = Array.new(2) { current.start_attempt } # sent at the raised limit
|
|
135
|
+
current.finish_attempt(failing, :failure)
|
|
136
|
+
current.finish_attempt(other, :success, bytes: mib)
|
|
137
|
+
expect([ current.limit, current.phase, reasons(current).last ]).to eq([ 1, :warm, :failure ])
|
|
138
|
+
|
|
139
|
+
stale = controller(storage)
|
|
140
|
+
round(stale, seconds: 0.25)
|
|
141
|
+
early = stale.start_attempt # sent at the baseline's limit, in its epoch
|
|
142
|
+
2.times { round(stale, seconds: 0.25) }
|
|
143
|
+
expect(stale.limit).to eq(2)
|
|
144
|
+
stale.finish_attempt(early, :failure)
|
|
145
|
+
expect([ stale.limit, stale.phase, reasons(stale).last ]).to eq([ 2, :warm, :warm_interrupted ])
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
it "asks a probe for more answers when its baseline's rates varied" do
|
|
149
|
+
warm = controller(storage)
|
|
150
|
+
round(warm, seconds: 0.25)
|
|
151
|
+
round(warm, seconds: 0.25)
|
|
152
|
+
round(warm, seconds: 0.5)
|
|
153
|
+
expect(warm.limit).to eq(2)
|
|
154
|
+
|
|
155
|
+
round(warm, seconds: 0.25) # the raised round settles
|
|
156
|
+
7.times { round(warm, seconds: 0.25) } # fourteen of the sixteen answers it needs
|
|
157
|
+
expect(warm.limit).to eq(2)
|
|
158
|
+
round(warm, seconds: 0.25)
|
|
159
|
+
expect(warm.limit).to eq(3)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
it "goes straight to 32 from its first window on a link of at least 96 MiB/s" do
|
|
163
|
+
warm = controller(storage)
|
|
164
|
+
round(warm, seconds: 0.5, bytes: 64 * mib)
|
|
165
|
+
|
|
166
|
+
expect([ warm.limit, warm.phase ]).to eq([ 32, :ordinary ])
|
|
167
|
+
expect(reasons(warm)).to eq(%i[start fast_link])
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
it "keeps a probe whose larger parts take longer but less time per byte" do
|
|
171
|
+
warm = controller(storage)
|
|
172
|
+
3.times { round(warm, seconds: 1.0, bytes: 4 * mib) } # one 4 MiB part a second
|
|
173
|
+
expect(warm.limit).to eq(2)
|
|
174
|
+
|
|
175
|
+
2.times { round(warm, seconds: 2.0, bytes: 16 * mib) } # the raised round settles; then two 16 MiB parts, each two seconds
|
|
176
|
+
expect(warm.limit).to eq(3)
|
|
177
|
+
expect(reasons(warm)).to eq(%i[start warm_connection warm_probe warm_settling warm_settled warm_probe])
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
it "decides for parts of varying sizes as for equal parts at the same speed per byte" do
|
|
181
|
+
decide = lambda do |sizes|
|
|
182
|
+
warm = controller(storage)
|
|
183
|
+
sizes.each { |size| round(warm, seconds: size * 0.25, bytes: size * mib) }
|
|
184
|
+
[ warm.limit, reasons(warm) ]
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
expect(decide.call([ 1, 1, 4, 2, 3, 1, 2 ])).to eq(decide.call([ 1, 1, 1, 1, 1, 1, 1 ]))
|
|
188
|
+
expect(decide.call([ 1, 1, 1, 1, 1, 1, 1 ]))
|
|
189
|
+
.to eq([ 5, %i[start warm_connection warm_probe warm_settling warm_settled warm_probe warm_settling warm_settled warm_probe] ])
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
it "sizes a probe's window from the variation of every judged warm window, at its raised target" do
|
|
193
|
+
warm = controller(storage)
|
|
194
|
+
3.times { round(warm, seconds: 0.25) }
|
|
195
|
+
round(warm, seconds: 0.25) # the raised round settles
|
|
196
|
+
expect(warm.limit).to eq(2)
|
|
197
|
+
|
|
198
|
+
first, second = Array.new(2) { warm.start_attempt }
|
|
199
|
+
clock.advance(0.2)
|
|
200
|
+
warm.finish_attempt(first, :success, bytes: mib)
|
|
201
|
+
clock.advance(0.006)
|
|
202
|
+
warm.finish_attempt(second, :success, bytes: mib)
|
|
203
|
+
expect(warm.limit).to eq(3)
|
|
204
|
+
|
|
205
|
+
# S = 2 * (0.003 / 0.203)^2 over f = 2 degrees bounds v; at its raised target 3, d = 1, the probe needs
|
|
206
|
+
# ceil(2 * v * (3 * 2 / 0.5)^2) = 6 answers, where the target it came from would have asked for 3.
|
|
207
|
+
round(warm, seconds: 0.2) # the raised round settles
|
|
208
|
+
round(warm, seconds: 0.2)
|
|
209
|
+
expect(warm.limit).to eq(3)
|
|
210
|
+
round(warm, seconds: 0.2)
|
|
211
|
+
expect(warm.limit).to eq(5)
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
describe "measurement" do
|
|
216
|
+
it "counts an attempt against the limit from its admission, but as network time only from its send to its answer" do
|
|
217
|
+
warm = controller(storage)
|
|
218
|
+
2.times do
|
|
219
|
+
attempt = warm.admit
|
|
220
|
+
expect([ warm.admitted, warm.inflight ]).to eq([ 1, 0 ])
|
|
221
|
+
clock.advance(4.0) # waiting for a worker and reading the part
|
|
222
|
+
warm.sending(attempt)
|
|
223
|
+
expect(warm.inflight).to eq(1)
|
|
224
|
+
clock.advance(0.25)
|
|
225
|
+
warm.answered(attempt)
|
|
226
|
+
clock.advance(3.0) # settling the outcome and the caller's callbacks
|
|
227
|
+
warm.finish_attempt(attempt, :success, bytes: mib)
|
|
228
|
+
expect([ warm.admitted, warm.inflight ]).to eq([ 0, 0 ])
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# Each warming window, of one answer, was on the network for 0.25 of 7.25 seconds: far too little to judge.
|
|
232
|
+
expect([ warm.limit, reasons(warm) ]).to eq([ 1, %i[start ineligible ineligible] ])
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
describe "after the warm start" do
|
|
237
|
+
it "keeps probes that gain 1%, by one at and above soft" do
|
|
238
|
+
steady = controller(storage(:aggressive))
|
|
239
|
+
3.times { round(steady, seconds: 1.0) }
|
|
240
|
+
|
|
241
|
+
expect(steady.limit).to eq(35)
|
|
242
|
+
expect(reasons(steady)).to eq(%i[start probe probe_earned probe_earned])
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
it "grows by two below soft" do
|
|
246
|
+
steady = controller(storage(:aggressive))
|
|
247
|
+
steady.finish_attempt(steady.start_attempt, :failure)
|
|
248
|
+
round(steady, seconds: 1.0)
|
|
249
|
+
|
|
250
|
+
expect(steady.limit).to eq(22)
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
it "gives a probe up after two windows that do not gain, then holds a window" do
|
|
254
|
+
steady = controller(storage(:aggressive))
|
|
255
|
+
round(steady, seconds: 1.0) # 32 MiB/s
|
|
256
|
+
2.times { round(steady, seconds: 33.0 / 32) } # still 32 MiB/s with 33 in flight
|
|
257
|
+
expect(steady.limit).to eq(32)
|
|
258
|
+
|
|
259
|
+
round(steady, seconds: 1.0)
|
|
260
|
+
expect(steady.limit).to eq(32)
|
|
261
|
+
round(steady, seconds: 1.0)
|
|
262
|
+
expect(steady.limit).to eq(33)
|
|
263
|
+
expect(reasons(steady)).to eq(%i[start probe probe_miss probe_rejected hold probe])
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
it "gives a probe up and stops growing while the queue estimate is at least 20" do
|
|
267
|
+
steady = controller(storage(:aggressive))
|
|
268
|
+
round(steady, seconds: 1.0)
|
|
269
|
+
round(steady, seconds: 3.0) # Q = 33 * (1 - 1 / 3)
|
|
270
|
+
round(steady, seconds: 3.0) # Q = 32 * (1 - 1 / 3)
|
|
271
|
+
|
|
272
|
+
expect(steady.limit).to eq(32)
|
|
273
|
+
expect(reasons(steady).last(2)).to eq(%i[probe_rejected queue_suppress])
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
it "shrinks by 8% when the queue estimate reaches 34" do
|
|
277
|
+
steady = controller(storage(:aggressive))
|
|
278
|
+
6.times { round(steady, seconds: 1.0) }
|
|
279
|
+
expect(steady.limit).to eq(38)
|
|
280
|
+
|
|
281
|
+
round(steady, seconds: 20.0) # Q = 38 * (1 - 1 / 20)
|
|
282
|
+
expect(steady.limit).to eq(34)
|
|
283
|
+
expect(reasons(steady).last).to eq(:queue_backoff)
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
it "judges no window whose requests in flight averaged under 75% of the limit" do
|
|
287
|
+
steady = controller(storage(:aggressive))
|
|
288
|
+
4.times { round(steady, seconds: 1.0, count: 8) }
|
|
289
|
+
|
|
290
|
+
expect(steady.limit).to eq(32)
|
|
291
|
+
expect(reasons(steady)).to eq(%i[start ineligible])
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
describe "failures and throttles" do
|
|
296
|
+
it "shrinks by 35% once an epoch, never below the minimum" do
|
|
297
|
+
shrinking = controller(storage(:aggressive))
|
|
298
|
+
first, second = Array.new(2) { shrinking.start_attempt }
|
|
299
|
+
shrinking.finish_attempt(first, :failure)
|
|
300
|
+
shrinking.finish_attempt(second, :failure) # started before the change
|
|
301
|
+
expect(shrinking.limit).to eq(20)
|
|
302
|
+
|
|
303
|
+
limits = Array.new(5) { shrinking.finish_attempt(shrinking.start_attempt, :failure) }
|
|
304
|
+
expect(limits).to eq([ 13, 8, 5, 4, 4 ])
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
it "shrinks by 10% for a throttle from the current epoch and measures nothing until its cooldown ends" do
|
|
308
|
+
throttled = controller(storage(:aggressive))
|
|
309
|
+
first, second = Array.new(2) { throttled.start_attempt }
|
|
310
|
+
throttled.finish_attempt(first, :throttle)
|
|
311
|
+
throttled.finish_attempt(second, :throttle)
|
|
312
|
+
expect(throttled.limit).to eq(28)
|
|
313
|
+
expect(reasons(throttled)).to eq(%i[start throttle throttle_cooldown])
|
|
314
|
+
|
|
315
|
+
round(throttled, seconds: 0.5) # completes within the cooldown
|
|
316
|
+
expect(reasons(throttled).last).to eq(:throttle_cooldown)
|
|
317
|
+
|
|
318
|
+
clock.advance(0.5)
|
|
319
|
+
round(throttled, seconds: 1.0)
|
|
320
|
+
expect(throttled.limit).to eq(30)
|
|
321
|
+
expect(reasons(throttled).last).to eq(:probe)
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
it "learns nothing from an ignored outcome, and a fixed policy nothing at all" do
|
|
325
|
+
steady = controller(storage(:aggressive))
|
|
326
|
+
steady.finish_attempt(steady.start_attempt, :ignored)
|
|
327
|
+
expect(reasons(steady)).to eq(%i[start])
|
|
328
|
+
|
|
329
|
+
fixed = controller(Files::Transfer::Policy.fixed(3))
|
|
330
|
+
fixed.finish_attempt(fixed.start_attempt, :failure)
|
|
331
|
+
fixed.finish_attempt(fixed.start_attempt, :throttle, retry_after: 5)
|
|
332
|
+
5.times { round(fixed, seconds: 1.0) }
|
|
333
|
+
expect(fixed.limit).to eq(3)
|
|
334
|
+
expect(reasons(fixed)).to eq(%i[start])
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
it "counts a success sent before a failure's shrink in the window after it" do
|
|
338
|
+
steady = controller(storage(:aggressive))
|
|
339
|
+
early = steady.start_attempt
|
|
340
|
+
steady.finish_attempt(steady.start_attempt, :failure) # 32 to 20, in a new epoch
|
|
341
|
+
later = Array.new(19) { steady.start_attempt }
|
|
342
|
+
clock.advance(1.0)
|
|
343
|
+
([ early ] + later).each { |attempt| steady.finish_attempt(attempt, :success, bytes: mib) }
|
|
344
|
+
|
|
345
|
+
# Twenty answers, the early one included, close the window at the new limit, which then grows.
|
|
346
|
+
expect(steady.limit).to eq(22)
|
|
347
|
+
expect(reasons(steady)).to eq(%i[start failure probe])
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
it "keeps the current epoch through a stale throttle, so a later attempt's failure still shrinks the limit once" do
|
|
351
|
+
steady = controller(storage(:aggressive))
|
|
352
|
+
first, stale = Array.new(2) { steady.start_attempt }
|
|
353
|
+
steady.finish_attempt(first, :throttle) # 32 to 28, in a new epoch
|
|
354
|
+
later = steady.start_attempt
|
|
355
|
+
steady.finish_attempt(stale, :throttle, retry_after: 2) # sent before that change: restarts measurement, shrinks nothing
|
|
356
|
+
expect(steady.limit).to eq(28)
|
|
357
|
+
|
|
358
|
+
steady.finish_attempt(later, :failure) # sent after it: 28 to 18
|
|
359
|
+
expect(steady.limit).to eq(18)
|
|
360
|
+
expect(reasons(steady)).to eq(%i[start throttle throttle_cooldown failure])
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
it "holds a destination back until a throttle's Retry-After, which a later one extends but never shortens, whichever request met it" do
|
|
364
|
+
holds = Files::Transfer::Holds.new(clock: clock)
|
|
365
|
+
destination = Files::Transfer::Holds.destination("https://storage.example.test/parts/1?X-Amz-Signature=fixture")
|
|
366
|
+
expect(Files::Transfer::Holds.destination("https://Storage.example.test:443/objects/2")).to eq(destination)
|
|
367
|
+
|
|
368
|
+
holds.hold(destination, 5)
|
|
369
|
+
expect(holds.deadline(destination)).to eq(5.0)
|
|
370
|
+
clock.advance(1.0)
|
|
371
|
+
holds.hold(destination, 2)
|
|
372
|
+
expect(holds.deadline(destination)).to eq(5.0)
|
|
373
|
+
holds.hold(destination, 8)
|
|
374
|
+
holds.hold(destination, 0) # a throttle without a Retry-After holds nothing back
|
|
375
|
+
expect(holds.deadline(destination)).to eq(9.0)
|
|
376
|
+
expect(holds.to_h).to eq({ "https://storage.example.test:443" => 8.0 })
|
|
377
|
+
expect(holds.deadline(Files::Transfer::Holds.destination("https://other.example.test/parts/1"))).to be_nil
|
|
378
|
+
clock.advance(8.0)
|
|
379
|
+
expect(holds.deadline(destination)).to be_nil
|
|
380
|
+
end
|
|
381
|
+
end
|
|
382
|
+
end
|
data/spec/workspace_id_spec.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require "spec_helper"
|
|
2
|
+
require_relative "support/stubbed_api"
|
|
2
3
|
|
|
3
4
|
RSpec.describe "Workspace scoping" do
|
|
4
5
|
around do |example|
|
|
@@ -10,8 +11,24 @@ RSpec.describe "Workspace scoping" do
|
|
|
10
11
|
end
|
|
11
12
|
|
|
12
13
|
describe Files::ApiClient do
|
|
14
|
+
include StubbedApi
|
|
15
|
+
|
|
13
16
|
let(:client) { described_class.new }
|
|
14
17
|
|
|
18
|
+
# The X-Files-Workspace-Id of each request to list folders sent through the SDK's own connection, as Faraday's
|
|
19
|
+
# adapter received it.
|
|
20
|
+
def sent_workspace_ids(**options)
|
|
21
|
+
sent = []
|
|
22
|
+
stubbed = stubbed_api_client do |stub|
|
|
23
|
+
stub.get("/api/rest/v1/folders") do |env|
|
|
24
|
+
sent << env.request_headers["X-Files-Workspace-Id"]
|
|
25
|
+
json_response(200, [])
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
stubbed.execute_request(:get, "/folders", api_key: "api-key", **options)
|
|
29
|
+
sent
|
|
30
|
+
end
|
|
31
|
+
|
|
15
32
|
it "includes the configured workspace ID header" do
|
|
16
33
|
headers = client.send(:request_headers, "api-key", nil, :get, 123)
|
|
17
34
|
|
|
@@ -26,26 +43,14 @@ RSpec.describe "Workspace scoping" do
|
|
|
26
43
|
|
|
27
44
|
it "falls back to the configured workspace ID" do
|
|
28
45
|
Files.workspace_id = 123
|
|
29
|
-
response = double(status: 204, body: "", headers: {})
|
|
30
|
-
conn = double
|
|
31
|
-
client = described_class.new(conn)
|
|
32
46
|
|
|
33
|
-
expect(
|
|
34
|
-
expect(conn).to receive(:run_request).and_return(response)
|
|
35
|
-
|
|
36
|
-
client.execute_request(:get, "/folders", api_key: "api-key")
|
|
47
|
+
expect(sent_workspace_ids).to eq([ "123" ])
|
|
37
48
|
end
|
|
38
49
|
|
|
39
50
|
it "lets a per-request workspace ID override the configured workspace ID" do
|
|
40
51
|
Files.workspace_id = 123
|
|
41
|
-
response = double(status: 204, body: "", headers: {})
|
|
42
|
-
conn = double
|
|
43
|
-
client = described_class.new(conn)
|
|
44
|
-
|
|
45
|
-
expect(client).to receive(:request_headers).with("api-key", nil, :get, 456).and_return({})
|
|
46
|
-
expect(conn).to receive(:run_request).and_return(response)
|
|
47
52
|
|
|
48
|
-
|
|
53
|
+
expect(sent_workspace_ids(workspace_id: 456)).to eq([ "456" ])
|
|
49
54
|
end
|
|
50
55
|
end
|
|
51
56
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: files.com
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.1.
|
|
4
|
+
version: 1.1.814
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- files.com
|
|
@@ -196,6 +196,7 @@ files:
|
|
|
196
196
|
- doc/Files/Api.html
|
|
197
197
|
- doc/Files/ApiClient.html
|
|
198
198
|
- doc/Files/ApiClient/RequestLogContext.html
|
|
199
|
+
- doc/Files/ApiClient/TransferSettings.html
|
|
199
200
|
- doc/Files/ApiKey.html
|
|
200
201
|
- doc/Files/ApiKeyIsDisabledError.html
|
|
201
202
|
- doc/Files/ApiKeyIsPathRestrictedError.html
|
|
@@ -515,6 +516,47 @@ files:
|
|
|
515
516
|
- doc/Files/TooManyLoginAttemptsError.html
|
|
516
517
|
- doc/Files/TooManyRequestsError.html
|
|
517
518
|
- doc/Files/TooManySharesError.html
|
|
519
|
+
- doc/Files/Transfer.html
|
|
520
|
+
- doc/Files/Transfer/BufferLimitError.html
|
|
521
|
+
- doc/Files/Transfer/ByteBudget.html
|
|
522
|
+
- doc/Files/Transfer/ByteBudget/Reservation.html
|
|
523
|
+
- doc/Files/Transfer/CancelledError.html
|
|
524
|
+
- doc/Files/Transfer/Checkpoint.html
|
|
525
|
+
- doc/Files/Transfer/CheckpointError.html
|
|
526
|
+
- doc/Files/Transfer/ClientTransport.html
|
|
527
|
+
- doc/Files/Transfer/ClientTransport/ClientResponse.html
|
|
528
|
+
- doc/Files/Transfer/ClientTransport/Reader.html
|
|
529
|
+
- doc/Files/Transfer/Clock.html
|
|
530
|
+
- doc/Files/Transfer/Control.html
|
|
531
|
+
- doc/Files/Transfer/Controller.html
|
|
532
|
+
- doc/Files/Transfer/Controller/Attempt.html
|
|
533
|
+
- doc/Files/Transfer/Coordinator.html
|
|
534
|
+
- doc/Files/Transfer/Coordinator/Timing.html
|
|
535
|
+
- doc/Files/Transfer/DownloadCoordinator.html
|
|
536
|
+
- doc/Files/Transfer/DownloadCoordinator/FileSink.html
|
|
537
|
+
- doc/Files/Transfer/DownloadCoordinator/IoSink.html
|
|
538
|
+
- doc/Files/Transfer/DownloadCoordinator/Piece.html
|
|
539
|
+
- doc/Files/Transfer/FileSlots.html
|
|
540
|
+
- doc/Files/Transfer/FrontendResponseError.html
|
|
541
|
+
- doc/Files/Transfer/Holds.html
|
|
542
|
+
- doc/Files/Transfer/HttpTransport.html
|
|
543
|
+
- doc/Files/Transfer/Job.html
|
|
544
|
+
- doc/Files/Transfer/Options.html
|
|
545
|
+
- doc/Files/Transfer/PausedError.html
|
|
546
|
+
- doc/Files/Transfer/Policy.html
|
|
547
|
+
- doc/Files/Transfer/RequestPermits.html
|
|
548
|
+
- doc/Files/Transfer/SourceChangedError.html
|
|
549
|
+
- doc/Files/Transfer/StorageResponse.html
|
|
550
|
+
- doc/Files/Transfer/StorageRetry.html
|
|
551
|
+
- doc/Files/Transfer/UploadCoordinator.html
|
|
552
|
+
- doc/Files/Transfer/UploadCoordinator/Grant.html
|
|
553
|
+
- doc/Files/Transfer/UploadCoordinator/Part.html
|
|
554
|
+
- doc/Files/Transfer/UploadCoordinator/Region.html
|
|
555
|
+
- doc/Files/Transfer/UploadCoordinator/Segments.html
|
|
556
|
+
- doc/Files/Transfer/UploadCoordinator/Source.html
|
|
557
|
+
- doc/Files/Transfer/UploadCoordinator/StreamSource.html
|
|
558
|
+
- doc/Files/Transfer/UploadPlan.html
|
|
559
|
+
- doc/Files/Transfer/VersionChangedError.html
|
|
518
560
|
- doc/Files/TrialExpiredError.html
|
|
519
561
|
- doc/Files/TrialLockedError.html
|
|
520
562
|
- doc/Files/TwoFactorAuthenticationCodeAlreadySentError.html
|
|
@@ -874,6 +916,19 @@ files:
|
|
|
874
916
|
- lib/files.com/response.rb
|
|
875
917
|
- lib/files.com/sizable_io.rb
|
|
876
918
|
- lib/files.com/system_profiler.rb
|
|
919
|
+
- lib/files.com/transfer/budgets.rb
|
|
920
|
+
- lib/files.com/transfer/checkpoint.rb
|
|
921
|
+
- lib/files.com/transfer/clock.rb
|
|
922
|
+
- lib/files.com/transfer/controller.rb
|
|
923
|
+
- lib/files.com/transfer/download_coordinator.rb
|
|
924
|
+
- lib/files.com/transfer/errors.rb
|
|
925
|
+
- lib/files.com/transfer/http_transport.rb
|
|
926
|
+
- lib/files.com/transfer/job.rb
|
|
927
|
+
- lib/files.com/transfer/options.rb
|
|
928
|
+
- lib/files.com/transfer/policy.rb
|
|
929
|
+
- lib/files.com/transfer/storage_retry.rb
|
|
930
|
+
- lib/files.com/transfer/upload_coordinator.rb
|
|
931
|
+
- lib/files.com/transfer/upload_plan.rb
|
|
877
932
|
- lib/files.com/uri.rb
|
|
878
933
|
- lib/files.com/util.rb
|
|
879
934
|
- lib/files.com/version.rb
|
|
@@ -905,6 +960,9 @@ files:
|
|
|
905
960
|
- spec/spec_helper.rb
|
|
906
961
|
- spec/support/stubbed_api.rb
|
|
907
962
|
- spec/support/transfer_server.rb
|
|
963
|
+
- spec/transfer/adaptive_download_spec.rb
|
|
964
|
+
- spec/transfer/adaptive_upload_spec.rb
|
|
965
|
+
- spec/transfer/concurrency_controller_spec.rb
|
|
908
966
|
- spec/uri_spec.rb
|
|
909
967
|
- spec/workspace_id_spec.rb
|
|
910
968
|
- test.sh
|