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.
- 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 +911 -731
- 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 +597 -386
- 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 +5368 -3048
- data/doc/top-level-namespace.html +1 -1
- data/docs/file.md +346 -0
- data/docs/site.md +10 -0
- data/docs/user.md +8 -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/models/site.rb +16 -0
- data/lib/files.com/models/user.rb +19 -0
- 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
data/lib/files.com/api_client.rb
CHANGED
|
@@ -76,6 +76,7 @@ module Files
|
|
|
76
76
|
end
|
|
77
77
|
|
|
78
78
|
def self.build_default_conn(force_net_http: false)
|
|
79
|
+
proxy = Files.proxy
|
|
79
80
|
conn = Faraday.new do |builder|
|
|
80
81
|
if Gem::Version.new(Faraday::VERSION) < Gem::Version.new("2.0.0") && defined?(Faraday::Request::Multipart)
|
|
81
82
|
begin
|
|
@@ -98,12 +99,120 @@ module Files
|
|
|
98
99
|
end
|
|
99
100
|
end
|
|
100
101
|
|
|
101
|
-
conn.proxy =
|
|
102
|
+
conn.proxy = proxy if proxy
|
|
102
103
|
conn.ssl.verify = true
|
|
104
|
+
# How it was made, and with which proxy, so unchanged_default_conn? can tell whether anything about it has changed
|
|
105
|
+
# since.
|
|
106
|
+
conn.instance_variable_set(:@files_sdk_configuration, [ connection_configuration(conn), proxy_identity(proxy) ].freeze)
|
|
103
107
|
|
|
104
108
|
conn
|
|
105
109
|
end
|
|
106
110
|
|
|
111
|
+
# Whether conn is a connection build_default_conn made with proxy, as proxy_identity gives it (nil when it was given
|
|
112
|
+
# none, so it follows the proxy environment variables), with nothing about it changed since: the same middleware and
|
|
113
|
+
# adapter (the same objects, so neither another adapter nor one with a configuration block of its own), and the same
|
|
114
|
+
# TLS, request and proxy options, default headers and default parameters. An adaptive transfer sends over its own
|
|
115
|
+
# connections, made with its own proxy, in place of only such a connection with that same proxy; through any other,
|
|
116
|
+
# such as one with a minimum TLS version or another proxy, it sends as that connection does.
|
|
117
|
+
def self.unchanged_default_conn?(conn, proxy)
|
|
118
|
+
return false unless conn.is_a?(Faraday::Connection)
|
|
119
|
+
|
|
120
|
+
conn.instance_variable_get(:@files_sdk_configuration) == [ connection_configuration(conn), proxy ]
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# A Files.proxy value, a URL or a Hash of its uri, user and password, as one frozen [URL, user, password], its user
|
|
124
|
+
# and password included, or nil for none.
|
|
125
|
+
def self.proxy_identity(proxy)
|
|
126
|
+
options = proxy && Faraday::ProxyOptions.from(proxy)
|
|
127
|
+
options&.uri && [ options.uri.to_s, options.user, options.password ].freeze
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
private_class_method def self.connection_configuration(conn)
|
|
131
|
+
[ conn.builder.handlers.map(&:object_id), conn.builder.adapter&.object_id, conn.ssl.to_hash, conn.options.to_hash,
|
|
132
|
+
conn.headers.to_h { |name, value| [ name.dup, value.dup ] }, conn.params.to_h { |name, value| [ name.dup, value.dup ] }, conn.proxy&.to_hash ].freeze
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Runs the block with each transfer request this thread sends through remote_request sent once: a failure is
|
|
136
|
+
# logged as usual but neither retried nor converted, so Faraday's own error, with any response it got, reaches
|
|
137
|
+
# the caller. An adaptive transfer sends its content requests through a caller's client this way, since it
|
|
138
|
+
# schedules and counts every retry itself.
|
|
139
|
+
def self.single_attempt
|
|
140
|
+
previous = Thread.current[:files_api_client_single_attempt]
|
|
141
|
+
Thread.current[:files_api_client_single_attempt] = true
|
|
142
|
+
yield
|
|
143
|
+
ensure
|
|
144
|
+
Thread.current[:files_api_client_single_attempt] = previous
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# What an adaptive transfer's API requests use in place of the library-wide settings, fixed on the caller's thread
|
|
148
|
+
# as the transfer begins (transfer_settings) and passed as their request_settings option.
|
|
149
|
+
TransferSettings = Struct.new(:default_headers, :language, :app_info, :open_timeout, :read_timeout, :region_policy, :region_handler, keyword_init: true)
|
|
150
|
+
|
|
151
|
+
# The library-wide settings as they are now: frozen copies of Files.default_headers (a Proc is kept as it is, and
|
|
152
|
+
# still called for each request), Files.language and Files.app_info, the timeouts, Files.region_policy, and
|
|
153
|
+
# Files.region_handler, kept as it is. A request given them uses them instead of the library-wide ones, and never
|
|
154
|
+
# falls back to Files.api_key or Files.session_id: a credential it was not given stays absent.
|
|
155
|
+
def self.transfer_settings
|
|
156
|
+
TransferSettings.new(default_headers: settings_copy(Files.default_headers), language: settings_copy(Files.language), app_info: settings_copy(Files.app_info),
|
|
157
|
+
open_timeout: Files.open_timeout, read_timeout: Files.read_timeout, region_policy: Files.region_policy,
|
|
158
|
+
region_handler: Files.region_handler
|
|
159
|
+
).freeze
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# A frozen copy of value's Hashes, Arrays and Strings, all the way down; anything else, such as a Proc, as it is.
|
|
163
|
+
private_class_method def self.settings_copy(value)
|
|
164
|
+
case value
|
|
165
|
+
when Hash then value.to_h { |key, item| [ settings_copy(key), settings_copy(item) ] }.freeze
|
|
166
|
+
when Array then value.map { |item| settings_copy(item) }.freeze
|
|
167
|
+
when String then value.dup.freeze
|
|
168
|
+
else value
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# The comment the SDK's own User-Agent ends with, and the most bytes that comment and the whole User-Agent may take.
|
|
173
|
+
USER_AGENT_METADATA_MARKER = "files-sdk-meta/1"
|
|
174
|
+
MAX_USER_AGENT_BYTES = 190
|
|
175
|
+
MAX_USER_AGENT_METADATA_BYTES = 128
|
|
176
|
+
private_constant :USER_AGENT_METADATA_MARKER, :MAX_USER_AGENT_BYTES, :MAX_USER_AGENT_METADATA_BYTES
|
|
177
|
+
|
|
178
|
+
# The User-Agent the SDK sends with its own API requests: identity, the SDK's name and version and any app info,
|
|
179
|
+
# followed by one comment naming the coarse family of the operating system host_os (RbConfig's) names and the Ruby
|
|
180
|
+
# runtime, with ruby_version for CRuby, such as "Files.com Ruby SDK v1.2.3 (files-sdk-meta/1; os=linux;
|
|
181
|
+
# runtime=ruby/3.4.4)". The comment carries nothing else: no host or user name, path, architecture or build. A
|
|
182
|
+
# version it cannot carry as given is left out rather than changed. When the whole User-Agent would be longer than
|
|
183
|
+
# 190 bytes, the version is left out first and then the whole comment, so identity is always sent as it is; an
|
|
184
|
+
# identity that is not printable ASCII, such as one whose app info is not, gets no comment. A User-Agent from
|
|
185
|
+
# Files.default_headers or a request's own headers is sent instead, exactly as given, and a transfer request
|
|
186
|
+
# (remote_request) sends only the headers it is given.
|
|
187
|
+
def self.user_agent_with_metadata(identity, host_os: RbConfig::CONFIG["host_os"], ruby_engine: RUBY_ENGINE, ruby_version: RUBY_VERSION)
|
|
188
|
+
return identity unless identity.is_a?(String) && identity.ascii_only? && identity.match?(/\A[\x20-\x7e]+\z/)
|
|
189
|
+
|
|
190
|
+
carried = ruby_engine == "ruby" && ruby_version.is_a?(String) && ruby_version.ascii_only? && ruby_version.match?(/\A[0-9][A-Za-z0-9.+_-]{0,47}\z/)
|
|
191
|
+
runtimes = carried ? [ "ruby/#{ruby_version}", "ruby" ] : [ "ruby" ]
|
|
192
|
+
os = user_agent_os(host_os)
|
|
193
|
+
runtimes.each do |runtime|
|
|
194
|
+
metadata = " (#{USER_AGENT_METADATA_MARKER}; os=#{os}; runtime=#{runtime})"
|
|
195
|
+
return "#{identity}#{metadata}" if metadata.bytesize <= MAX_USER_AGENT_METADATA_BYTES && identity.bytesize + metadata.bytesize <= MAX_USER_AGENT_BYTES
|
|
196
|
+
end
|
|
197
|
+
identity
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# The coarse family of the operating system RbConfig's host_os names: "unknown" for none, and "other" for one the
|
|
201
|
+
# User-Agent's comment does not name.
|
|
202
|
+
private_class_method def self.user_agent_os(host_os)
|
|
203
|
+
return "unknown" unless host_os.is_a?(String) && !host_os.empty?
|
|
204
|
+
return "other" unless host_os.ascii_only?
|
|
205
|
+
|
|
206
|
+
case host_os
|
|
207
|
+
when /mswin|mingw|cygwin/i then "windows"
|
|
208
|
+
when /darwin/i then "macos"
|
|
209
|
+
when /android/i then "android"
|
|
210
|
+
when /linux/i then "linux"
|
|
211
|
+
when /freebsd/i then "freebsd"
|
|
212
|
+
else "other"
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
107
216
|
def self.should_retry?(error, num_retries)
|
|
108
217
|
return false if num_retries >= Files.max_network_retries
|
|
109
218
|
return true if error.is_a?(Faraday::TimeoutError)
|
|
@@ -144,13 +253,15 @@ module Files
|
|
|
144
253
|
end
|
|
145
254
|
|
|
146
255
|
# Returns the response and the API key and session ID the request used. +region_http_pairs+, such as the pairs a
|
|
147
|
-
# returned record keeps, is used instead of this client's.
|
|
148
|
-
#
|
|
149
|
-
#
|
|
150
|
-
|
|
256
|
+
# returned record keeps, is used instead of this client's. +settings+, an adaptive transfer's TransferSettings, is
|
|
257
|
+
# used instead of the library-wide default headers, language, app info, timeouts and region policy and handler, and
|
|
258
|
+
# with it a credential the request was not given is not looked up in Files.api_key or Files.session_id. The block,
|
|
259
|
+
# if given, receives the request's context once its response has arrived: the regional origin a lockout region
|
|
260
|
+
# mismatch moved it to, or nil, and the frozen region_http_pairs it used.
|
|
261
|
+
def execute_request(method, path, base_url: nil, api_key: nil, session_id: nil, workspace_id: nil, region_http_pairs: nil, settings: nil, headers: {}, params: {}, &on_request_context)
|
|
151
262
|
base_url ||= Files.base_url
|
|
152
263
|
if api_key.nil?
|
|
153
|
-
session_id ||= Files.session_id
|
|
264
|
+
session_id ||= Files.session_id unless settings
|
|
154
265
|
else
|
|
155
266
|
session_id = nil
|
|
156
267
|
end
|
|
@@ -159,7 +270,7 @@ module Files
|
|
|
159
270
|
if session_id and session_id != ""
|
|
160
271
|
check_session_id!(session_id)
|
|
161
272
|
elsif path !~ /^\/sessions/ # TODO: automate this to refer to any unauthenticated endpoint
|
|
162
|
-
api_key ||= Files.api_key
|
|
273
|
+
api_key ||= Files.api_key unless settings
|
|
163
274
|
check_api_key!(api_key)
|
|
164
275
|
end
|
|
165
276
|
|
|
@@ -172,7 +283,7 @@ module Files
|
|
|
172
283
|
body = params
|
|
173
284
|
end
|
|
174
285
|
|
|
175
|
-
headers = request_headers(api_key, session_id, method, workspace_id).update(headers)
|
|
286
|
+
headers = request_headers(api_key, session_id, method, workspace_id, settings).update(headers)
|
|
176
287
|
url = api_url(path, base_url)
|
|
177
288
|
|
|
178
289
|
context = RequestLogContext.new
|
|
@@ -188,15 +299,15 @@ module Files
|
|
|
188
299
|
# Fixed before the request starts, so later changes to the caller's or the library-wide pairs cannot affect it.
|
|
189
300
|
http_pairs = region_http_pairs.nil? ? self.region_http_pairs : Util.checked_region_http_pairs(region_http_pairs)
|
|
190
301
|
relocate = lambda do |error_response|
|
|
191
|
-
regional_base_url = region_continuation_base_url(error_response, base_url, body, http_pairs)
|
|
302
|
+
regional_base_url = region_continuation_base_url(error_response, base_url, body, http_pairs, settings)
|
|
192
303
|
url = api_url(path, regional_base_url) if regional_base_url
|
|
193
304
|
regional_base_url
|
|
194
305
|
end
|
|
195
306
|
|
|
196
307
|
http_resp = execute_request_with_rescues(base_url, context, relocate: relocate) do
|
|
197
308
|
conn.run_request(method, url, body, headers) do |req|
|
|
198
|
-
req.options.open_timeout =
|
|
199
|
-
req.options.timeout =
|
|
309
|
+
req.options.open_timeout = setting(settings, :open_timeout)
|
|
310
|
+
req.options.timeout = setting(settings, :read_timeout)
|
|
200
311
|
req.params = query_params unless query_params.nil?
|
|
201
312
|
end
|
|
202
313
|
end
|
|
@@ -212,26 +323,59 @@ module Files
|
|
|
212
323
|
[ resp, api_key, session_id ]
|
|
213
324
|
end
|
|
214
325
|
|
|
326
|
+
# Whether this client sends transfer requests just as the SDK's own default client does: an ApiClient itself, not
|
|
327
|
+
# a subclass or an object with methods of its own, with no logger or log level of its own. Only then may an
|
|
328
|
+
# adaptive transfer send its content requests over its own connections instead of through remote_request.
|
|
329
|
+
def default_transfer_behavior?
|
|
330
|
+
instance_of?(ApiClient) && singleton_methods.empty? && @logger.equal?(USE_GLOBAL) && @log_level.equal?(USE_GLOBAL)
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
# The workspace_id option that names, for every later request, the workspace this client's requests name now when
|
|
334
|
+
# their own workspace_id is nil: its own, or Files.workspace_id, and "" for none. An adaptive upload fixes it as it
|
|
335
|
+
# begins, so a later change to Files.workspace_id cannot move it.
|
|
336
|
+
def resolved_workspace_id
|
|
337
|
+
workspace_id = default_workspace_id
|
|
338
|
+
workspace_id.nil? ? "" : workspace_id
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
# Sends a request to a transfer URL, such as a signed storage URL, through this client's connection, with its
|
|
342
|
+
# middleware, adapter, settings and logging. The URL's query reaches the adapter as URI parses it (SignedQuery): the
|
|
343
|
+
# request's params, the connection's default ones included, are not added to it, unlike an API request's. URI keeps
|
|
344
|
+
# its order, its percent escapes and the characters a signed URL's query uses, so such a query arrives exactly as
|
|
345
|
+
# given, as its signature needs; it escapes others, such as a space, and drops tabs and line breaks. A URL URI cannot
|
|
346
|
+
# parse, as one with %zz in its query or a character outside ASCII, is left to Faraday as it is, which refuses it
|
|
347
|
+
# or sends its query decoded and encoded again, as it does any params.
|
|
215
348
|
def remote_request(method, url, headers = {}, body = nil)
|
|
216
349
|
context = RequestLogContext.new
|
|
217
350
|
context.method = method
|
|
218
351
|
context.path = "[transfer]"
|
|
219
352
|
Util.log_debug("Transfer request", method: method, url: url)
|
|
353
|
+
query = transfer_query(url)
|
|
220
354
|
|
|
221
355
|
execute_request_with_rescues(Files.base_url, context, skip_body_logging: true, is_transfer: true) do
|
|
222
356
|
conn.run_request(method, url, body, headers) do |req|
|
|
223
357
|
req.options.open_timeout = Files.open_timeout
|
|
224
358
|
req.options.timeout = Files.read_timeout
|
|
359
|
+
req.options.params_encoder = SignedQuery.new(query, req.options.params_encoder || Faraday::Utils.default_params_encoder) if query
|
|
225
360
|
yield(req) if block_given?
|
|
226
361
|
end
|
|
227
362
|
end
|
|
228
363
|
end
|
|
229
364
|
|
|
365
|
+
# The query a request to a transfer URL carries to the adapter (SignedQuery): the one URI parses from the URL, ""
|
|
366
|
+
# for none, or nil when URI cannot parse it.
|
|
367
|
+
private def transfer_query(url)
|
|
368
|
+
::URI.parse(url.to_s).query || ""
|
|
369
|
+
rescue ::URI::InvalidURIError
|
|
370
|
+
nil
|
|
371
|
+
end
|
|
372
|
+
|
|
230
373
|
def stream_download(uri, io, range)
|
|
231
374
|
if conn.adapter == Faraday::Adapter::NetHttp
|
|
232
375
|
stream_download_with_net_http(URI(uri), io, range)
|
|
233
376
|
else
|
|
234
377
|
response = remote_request(:get, uri)
|
|
378
|
+
reject_frontend_response(response.headers.key?(Transfer::StorageRetry::FRONTEND_HEADER), io)
|
|
235
379
|
io.fulfill_content_length(response.content_length) if io.respond_to?(:fulfill_content_length)
|
|
236
380
|
io.write(response.body)
|
|
237
381
|
end
|
|
@@ -261,6 +405,7 @@ module Files
|
|
|
261
405
|
|
|
262
406
|
private def write_download_body(response, io)
|
|
263
407
|
reject_unsuccessful_download(response, io)
|
|
408
|
+
reject_frontend_response(response.key?(Transfer::StorageRetry::FRONTEND_HEADER), io)
|
|
264
409
|
io.fulfill_content_length(response.content_length) if io.respond_to?(:fulfill_content_length)
|
|
265
410
|
|
|
266
411
|
expected_bytes = expected_download_bytes(response)
|
|
@@ -298,6 +443,16 @@ module Files
|
|
|
298
443
|
raise
|
|
299
444
|
end
|
|
300
445
|
|
|
446
|
+
# A response carrying X-Files-Frontend-App came from the Files.com web app, such as a sign-in page, not from file
|
|
447
|
+
# storage. It is refused before anything of it is written, and closes the destination as a refused transfer does.
|
|
448
|
+
private def reject_frontend_response(frontend, io)
|
|
449
|
+
return unless frontend
|
|
450
|
+
|
|
451
|
+
error = Transfer::FrontendResponseError.new
|
|
452
|
+
close_destination_after_failure(io, error)
|
|
453
|
+
raise error
|
|
454
|
+
end
|
|
455
|
+
|
|
301
456
|
private def write_download_chunk(io, chunk)
|
|
302
457
|
io << chunk
|
|
303
458
|
rescue StandardError => e
|
|
@@ -344,9 +499,10 @@ module Files
|
|
|
344
499
|
# with that error, naming the site's own origin in data.host or X-Files-Host, when the site is pinned to another
|
|
345
500
|
# region or to a custom domain. Only the configured HTTPS origin, reached over this client's verified connection, may send the
|
|
346
501
|
# credentials elsewhere, apart from an http:// origin to the destination +http_pairs+ pairs it with.
|
|
347
|
-
private def region_continuation_base_url(error_response, base_url, body, http_pairs)
|
|
348
|
-
return if
|
|
502
|
+
private def region_continuation_base_url(error_response, base_url, body, http_pairs, settings = nil)
|
|
503
|
+
return if setting(settings, :region_policy) == :disabled
|
|
349
504
|
|
|
505
|
+
handler = setting(settings, :region_handler)
|
|
350
506
|
error_object = error_response.data
|
|
351
507
|
return unless error_response.http_status == 403 && error_object[:type] == "not-authenticated/lockout-region-mismatch"
|
|
352
508
|
return unless replayable?(body)
|
|
@@ -356,7 +512,7 @@ module Files
|
|
|
356
512
|
|
|
357
513
|
host = data && data[:host]
|
|
358
514
|
host = Faraday::Utils::Headers.new(error_response.http_headers || {})["X-Files-Host"] if host.nil? || host == ""
|
|
359
|
-
return http_pair_origin(base_url, host, http_pairs) unless verified_https_connection?(base_url)
|
|
515
|
+
return http_pair_origin(base_url, host, http_pairs, handler) unless verified_https_connection?(base_url)
|
|
360
516
|
|
|
361
517
|
configured = Addressable::URI.parse(base_url)
|
|
362
518
|
original_origin = https_origin(configured.normalized_host, configured.inferred_port)
|
|
@@ -364,8 +520,8 @@ module Files
|
|
|
364
520
|
# A mismatch that names the origin it came from cannot be resolved by moving, so the handler is not asked.
|
|
365
521
|
return if target_origin.nil? || target_origin == original_origin
|
|
366
522
|
|
|
367
|
-
if
|
|
368
|
-
target_origin = approved_region_origin(original_origin, target_origin, approval_required) { |decision| region_target(decision)&.first }
|
|
523
|
+
if handler
|
|
524
|
+
target_origin = approved_region_origin(handler, original_origin, target_origin, approval_required) { |decision| region_target(decision)&.first }
|
|
369
525
|
elsif approval_required
|
|
370
526
|
return
|
|
371
527
|
end
|
|
@@ -375,12 +531,13 @@ module Files
|
|
|
375
531
|
target_origin
|
|
376
532
|
end
|
|
377
533
|
|
|
378
|
-
# The origin Files.region_handler
|
|
379
|
-
#
|
|
380
|
-
# gets frozen copies, so it cannot change the
|
|
381
|
-
|
|
534
|
+
# The origin the region handler (Files.region_handler, or the one an adaptive transfer began with) approves: the
|
|
535
|
+
# target for true, or another origin it returns that the block gives back unchanged, as the canonical form of an
|
|
536
|
+
# origin the request may move to. nil when it declines. The handler gets frozen copies, so it cannot change the
|
|
537
|
+
# origins used here.
|
|
538
|
+
private def approved_region_origin(handler, original_origin, target_origin, approval_required)
|
|
382
539
|
event = { reason: :lockout_region_mismatch, original_origin: original_origin.dup.freeze, target_origin: target_origin.dup.freeze, approval_required: approval_required }.freeze
|
|
383
|
-
decision =
|
|
540
|
+
decision = handler.call(event)
|
|
384
541
|
return target_origin if decision == true
|
|
385
542
|
return unless decision.is_a?(String)
|
|
386
543
|
|
|
@@ -393,13 +550,13 @@ module Files
|
|
|
393
550
|
# continue. Within it a bare host means that host over plain HTTP, on port 80 unless it names another port. The
|
|
394
551
|
# region handler may still decline, or choose another destination paired with the same source; the request then
|
|
395
552
|
# continues at that pair's own frozen destination, not at the String the handler returned.
|
|
396
|
-
private def http_pair_origin(base_url, host, http_pairs)
|
|
553
|
+
private def http_pair_origin(base_url, host, http_pairs, handler)
|
|
397
554
|
original_origin = Util.http_origin(base_url)
|
|
398
555
|
target_origin = Util.http_origin(host.is_a?(String) && !host.include?("://") ? "http://#{host}" : host)
|
|
399
556
|
return unless original_origin && target_origin != original_origin && http_pairs.include?([ original_origin, target_origin ])
|
|
400
557
|
|
|
401
|
-
if
|
|
402
|
-
target_origin = approved_region_origin(original_origin, target_origin, false) do |decision|
|
|
558
|
+
if handler
|
|
559
|
+
target_origin = approved_region_origin(handler, original_origin, target_origin, false) do |decision|
|
|
403
560
|
http_pairs.find { |source, destination| source == original_origin && destination == decision }&.last
|
|
404
561
|
end
|
|
405
562
|
end
|
|
@@ -499,13 +656,15 @@ module Files
|
|
|
499
656
|
log_response_error(error_context, request_start, e)
|
|
500
657
|
end
|
|
501
658
|
|
|
502
|
-
|
|
659
|
+
single_attempt = is_transfer && Thread.current[:files_api_client_single_attempt]
|
|
660
|
+
if !single_attempt && self.class.should_retry?(e, num_retries)
|
|
503
661
|
num_retries += 1
|
|
504
662
|
wait_seconds = self.class.sleep_time(num_retries)
|
|
505
663
|
log_retry(context, num_retries, e, wait_seconds) unless is_transfer
|
|
506
664
|
sleep wait_seconds
|
|
507
665
|
retry
|
|
508
666
|
end
|
|
667
|
+
raise if single_attempt
|
|
509
668
|
|
|
510
669
|
Util.log_debug("Transfer error details", error_message: e.message) if is_transfer
|
|
511
670
|
|
|
@@ -692,25 +851,33 @@ module Files
|
|
|
692
851
|
raise APIConnectionError, message
|
|
693
852
|
end
|
|
694
853
|
|
|
695
|
-
|
|
854
|
+
# The library-wide setting name, or, for an adaptive transfer's request, what its TransferSettings hold for it.
|
|
855
|
+
private def setting(settings, name)
|
|
856
|
+
settings ? settings[name] : Files.public_send(name)
|
|
857
|
+
end
|
|
858
|
+
|
|
859
|
+
private def request_headers(api_key, session_id, method, workspace_id, settings = nil)
|
|
860
|
+
app_info = setting(settings, :app_info)
|
|
696
861
|
user_agent = "Files.com Ruby SDK v#{Files::VERSION}"
|
|
697
|
-
user_agent += " #{format_app_info(
|
|
862
|
+
user_agent += " #{format_app_info(app_info)}" unless app_info.nil?
|
|
698
863
|
|
|
699
|
-
headers = { "User-Agent" => user_agent }
|
|
864
|
+
headers = { "User-Agent" => self.class.user_agent_with_metadata(user_agent) }
|
|
700
865
|
# A request body gets its Content-Type from the multipart and URL-encoding middleware, which choose
|
|
701
866
|
# multipart/form-data when the body holds a file part and application/x-www-form-urlencoded otherwise.
|
|
702
867
|
headers["Content-Type"] = "application/x-www-form-urlencoded" if QUERY_METHODS.include?(method.to_s.downcase.to_sym)
|
|
703
868
|
|
|
704
|
-
|
|
705
|
-
|
|
869
|
+
default_headers = setting(settings, :default_headers)
|
|
870
|
+
if default_headers.is_a?(Proc)
|
|
871
|
+
proc_headers = default_headers.call
|
|
706
872
|
headers.merge!(proc_headers) if proc_headers.is_a?(Hash)
|
|
707
873
|
end
|
|
708
874
|
|
|
709
|
-
headers.merge!(
|
|
875
|
+
headers.merge!(default_headers) if default_headers.is_a?(Hash)
|
|
710
876
|
|
|
877
|
+
language = setting(settings, :language)
|
|
711
878
|
headers["X-FilesAPI-Key"] = api_key if api_key
|
|
712
879
|
headers["X-FilesAPI-Auth"] = session_id if session_id
|
|
713
|
-
headers["Accept-Language"] =
|
|
880
|
+
headers["Accept-Language"] = language if language
|
|
714
881
|
workspace_id = workspace_id.to_s
|
|
715
882
|
headers["X-Files-Workspace-Id"] = workspace_id unless workspace_id.empty?
|
|
716
883
|
|
|
@@ -761,5 +928,26 @@ module Files
|
|
|
761
928
|
class RequestLogContext
|
|
762
929
|
attr_accessor :body, :api_key, :headers, :method, :path, :query_params, :session_id
|
|
763
930
|
end
|
|
931
|
+
|
|
932
|
+
# The params encoder of one transfer request (remote_request). Faraday decodes a URL's query into the request's
|
|
933
|
+
# params and encodes them again as it builds the adapter's URL, sorted and escaped its own way, with the
|
|
934
|
+
# connection's default params among them. This encoder gives back the query remote_request read from the URL
|
|
935
|
+
# instead, character for character, whatever the params hold. Decoding, as an adapter may do to read the query
|
|
936
|
+
# back, is left to the encoder the request had.
|
|
937
|
+
class SignedQuery
|
|
938
|
+
def initialize(query, decoder)
|
|
939
|
+
@query = query
|
|
940
|
+
@decoder = decoder
|
|
941
|
+
end
|
|
942
|
+
|
|
943
|
+
def encode(_params)
|
|
944
|
+
@query
|
|
945
|
+
end
|
|
946
|
+
|
|
947
|
+
def decode(query)
|
|
948
|
+
@decoder.decode(query)
|
|
949
|
+
end
|
|
950
|
+
end
|
|
951
|
+
private_constant :SignedQuery
|
|
764
952
|
end
|
|
765
953
|
end
|