blob: c3c2d9d8d4f4fa1fbfef1aeec69b87e6d902f876 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_ENTERPRISE_CONNECTORS_FILE_SYSTEM_BOX_API_CALL_FLOW_H_
#define CHROME_BROWSER_ENTERPRISE_CONNECTORS_FILE_SYSTEM_BOX_API_CALL_FLOW_H_
#include "base/callback.h"
#include "base/files/file_path.h"
#include "google_apis/gaia/oauth2_api_call_flow.h"
#include "services/data_decoder/public/cpp/data_decoder.h"
namespace enterprise_connectors {
struct BoxApiCallResponse;
// Helper for making Box API calls.
//
// This class is abstract. The methods OAuth2ApiCallFlow::ProcessApiCallXXX must
// be implemented by subclasses.
class BoxApiCallFlow : public OAuth2ApiCallFlow {
public:
using Response = BoxApiCallResponse;
using TaskCallback = base::OnceCallback<void(Response)>;
BoxApiCallFlow();
~BoxApiCallFlow() override;
// OAuth2ApiCallFlow interface.
GURL CreateApiCallUrl() override;
std::string CreateApiCallBody() override;
std::string CreateApiCallBodyContentType() override;
net::PartialNetworkTrafficAnnotationTag GetNetworkTrafficAnnotationTag()
override;
void ProcessApiCallFailure(int net_error,
const network::mojom::URLResponseHead* head,
std::unique_ptr<std::string> body) override;
static std::string FormatSHA1Digest(const std::string& sha_digest);
static GURL MakeUrlToShowFile(const std::string& file_id);
static GURL MakeUrlToShowFolder(const std::string& folder_id);
// Used by BoxApiCallFlow inherited classes and BoxUploader
// to determine whether to use WholeFileUpload or ChunkedFileUpload
static const size_t kChunkFileUploadMinSize;
static const size_t kWholeFileUploadMaxSize;
using ParseResult = data_decoder::DataDecoder::ValueOrError;
protected:
virtual void ProcessFailure(Response response) = 0;
void OnFailureJsonParsed(int http_error, ParseResult result);
base::WeakPtrFactory<BoxApiCallFlow> weak_factory_{this};
};
// Helper for finding the downloads folder in Box.
class BoxFindUpstreamFolderApiCallFlow : public BoxApiCallFlow {
public:
// Additional callback arg is: folder_id for the downloads folder found in
// Box.
using TaskCallback = base::OnceCallback<void(Response, const std::string&)>;
explicit BoxFindUpstreamFolderApiCallFlow(TaskCallback callback);
~BoxFindUpstreamFolderApiCallFlow() override;
protected:
// BoxApiCallFlow interface.
GURL CreateApiCallUrl() override;
void ProcessApiCallSuccess(const network::mojom::URLResponseHead* head,
std::unique_ptr<std::string> body) override;
void ProcessFailure(Response response) override;
private:
// Callback for JsonParser that extracts folder id in ProcessApiCallSuccess().
void OnSuccessJsonParsed(ParseResult result);
// Callback from the uploader to report success, http_code, folder_id.
TaskCallback callback_;
base::WeakPtrFactory<BoxFindUpstreamFolderApiCallFlow> weak_factory_{this};
};
// Helper for creating an upstream downloads folder in box.
class BoxCreateUpstreamFolderApiCallFlow : public BoxApiCallFlow {
public:
// Additional callback arg is: folder_id for the downloads folder created in
// Box.
using TaskCallback = base::OnceCallback<void(Response, const std::string&)>;
explicit BoxCreateUpstreamFolderApiCallFlow(TaskCallback callback);
~BoxCreateUpstreamFolderApiCallFlow() override;
protected:
// BoxApiCallFlow interface.
GURL CreateApiCallUrl() override;
std::string CreateApiCallBody() override;
bool IsExpectedSuccessCode(int code) const override;
void ProcessApiCallSuccess(const network::mojom::URLResponseHead* head,
std::unique_ptr<std::string> body) override;
void ProcessFailure(Response response) override;
private:
// Callback for JsonParser that extracts folder id in ProcessApiCallSuccess().
void OnSuccessJsonParsed(ParseResult result);
// Callback from the uploader to report success, http_code, folder_id.
TaskCallback callback_;
base::WeakPtrFactory<BoxCreateUpstreamFolderApiCallFlow> weak_factory_{this};
};
// Helper for performing preflight checks before uploading a file.
class BoxPreflightCheckApiCallFlow : public BoxApiCallFlow {
public:
BoxPreflightCheckApiCallFlow(TaskCallback callback,
const base::FilePath& target_file_name,
const std::string& folder_id);
~BoxPreflightCheckApiCallFlow() override;
protected:
// BoxApiCallFlow interface.
GURL CreateApiCallUrl() override;
std::string CreateApiCallBody() override;
std::string GetRequestTypeForBody(const std::string& body) override;
bool IsExpectedSuccessCode(int code) const override;
void ProcessApiCallSuccess(const network::mojom::URLResponseHead* head,
std::unique_ptr<std::string> body) override;
void ProcessFailure(Response response) override;
private:
// Callback from the controller to report success, http_code, folder_id.
TaskCallback callback_;
const base::FilePath target_file_name_;
const std::string folder_id_;
base::WeakPtrFactory<BoxPreflightCheckApiCallFlow> weak_factory_{this};
};
// Helper for uploading a small (<= kWholeFileUploadMaxSize) file to upstream
// downloads folder in box.
class BoxWholeFileUploadApiCallFlow : public BoxApiCallFlow {
public:
// Additional args are: file id to show the uploaded item on Box.
using TaskCallback = base::OnceCallback<void(Response, const std::string&)>;
BoxWholeFileUploadApiCallFlow(TaskCallback callback,
const std::string& folder_id,
const base::FilePath& target_file_name,
const base::FilePath& local_file_path);
~BoxWholeFileUploadApiCallFlow() override;
// Overrides OAuth2ApiCallFlow::Start() to first read local file content
// before kicking off OAuth2ApiCallFlow::Start().
void Start(scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const std::string& access_token) override;
protected:
// BoxApiCallFlow interface.
GURL CreateApiCallUrl() override;
std::string CreateApiCallBody() override;
std::string CreateApiCallBodyContentType() override;
bool IsExpectedSuccessCode(int code) const override;
void ProcessApiCallSuccess(const network::mojom::URLResponseHead* head,
std::unique_ptr<std::string> body) override;
void ProcessFailure(Response response) override;
void SetFileReadForTesting(std::string content, std::string mime_type);
private:
struct FileRead {
std::string content;
std::string mime;
};
// Post a task to ThreadPool to read the local file, forward the
// parameters from Start() into OnFileRead(), which is the callback that then
// kicks off OAuth2CallFlow::Start() after file content is read.
void PostReadFileTask(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const std::string& access_token);
// Callback attached in PostReadFileTask(). Take in read file content and
// kick off OAuth2CallFlow::Start().
void OnFileRead(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const std::string& access_token,
absl::optional<FileRead> file_read);
// Task posted to ThreadPool to read the local file. Return type is
// base::Optional in case file is read successfully but the file content is
// really empty.
static absl::optional<FileRead> ReadFile(
const base::FilePath& path,
const base::FilePath& target_file_name);
const std::string folder_id_;
const base::FilePath target_file_name_;
const base::FilePath local_file_path_;
const std::string multipart_boundary_;
FileRead file_read_;
// Callback from the uploader to report success.
TaskCallback callback_;
base::WeakPtrFactory<BoxWholeFileUploadApiCallFlow> weak_factory_{this};
};
// Helper for starting an upload session to designated Chrome downloads folder
// in Box.
class BoxCreateUploadSessionApiCallFlow : public BoxApiCallFlow {
public:
// Additional callback args are: session endpoints provided in API request
// response, and part_size for each chunk to be uploaded.
using TaskCallback = base::OnceCallback<void(Response, base::Value, size_t)>;
BoxCreateUploadSessionApiCallFlow(TaskCallback callback,
const std::string& folder_id,
const size_t file_size,
const base::FilePath& file_name);
~BoxCreateUploadSessionApiCallFlow() override;
protected:
// BoxApiCallFlow interface.
GURL CreateApiCallUrl() override;
std::string CreateApiCallBody() override;
bool IsExpectedSuccessCode(int code) const override;
void ProcessApiCallSuccess(const network::mojom::URLResponseHead* head,
std::unique_ptr<std::string> body) override;
void ProcessFailure(Response response) override;
private:
void OnSuccessJsonParsed(ParseResult result);
TaskCallback callback_;
const std::string folder_id_;
const size_t file_size_;
const base::FilePath file_name_;
base::WeakPtrFactory<BoxCreateUploadSessionApiCallFlow> weak_factory_{this};
};
// Base helper for API requests related to chunked file uploads. Since
// BoxCreateUploadSessionApiCallFlow gives all relevant endpoints for an upload
// session in its response, the subsequent steps can take each endpoint as
// constructor argument and just return in CreateApiCallUrl() without
// formatting.
class BoxChunkedUploadBaseApiCallFlow : public BoxApiCallFlow {
protected:
explicit BoxChunkedUploadBaseApiCallFlow(const GURL endpoint);
// BoxApiCallFlow interface.
GURL CreateApiCallUrl() final;
const GURL endpoint_;
};
// Helper for uploading a part of the file to Box.
class BoxPartFileUploadApiCallFlow : public BoxChunkedUploadBaseApiCallFlow {
public:
// Additional callback arg is: uploaded file part info in API request response
// that needs to be attached in CommitUploadSession request.
// Callback invoked when the file part upload completes. The bool argument is
// true if the upload succeeded and false otherwise. The int argument
// represents the final HTTP status code of the request. The Value holds a
// JSON part object as returned by the Box Upload Part API, which is valid
// only on success.
using TaskCallback = base::OnceCallback<void(Response, base::Value)>;
BoxPartFileUploadApiCallFlow(TaskCallback callback,
const std::string& session_endpoint,
const std::string& file_part_content,
const size_t byte_from,
const size_t byte_to,
const size_t byte_total);
~BoxPartFileUploadApiCallFlow() override;
// Helper method.
static std::string CreateFileDigest(const std::string& content);
protected:
// BoxApiCallFlow interface.
net::HttpRequestHeaders CreateApiCallHeaders() override;
std::string CreateApiCallBody() override;
std::string CreateApiCallBodyContentType() override;
std::string GetRequestTypeForBody(const std::string& body) override;
bool IsExpectedSuccessCode(int code) const override;
void ProcessApiCallSuccess(const network::mojom::URLResponseHead* head,
std::unique_ptr<std::string> body) override;
void ProcessFailure(Response response) override;
private:
void OnSuccessJsonParsed(ParseResult result);
TaskCallback callback_;
const std::string& part_content_;
const std::string content_range_;
base::WeakPtrFactory<BoxPartFileUploadApiCallFlow> weak_factory_{this};
};
// Helper for aborting an upload session if there's unrecoverable failure during
// uploading file chunks.
class BoxAbortUploadSessionApiCallFlow
: public BoxChunkedUploadBaseApiCallFlow {
public:
BoxAbortUploadSessionApiCallFlow(TaskCallback callback,
const std::string& session_endpoint);
~BoxAbortUploadSessionApiCallFlow() override;
protected:
// BoxApiCallFlow interface.
std::string GetRequestTypeForBody(const std::string& body) override;
bool IsExpectedSuccessCode(int code) const override;
void ProcessApiCallSuccess(const network::mojom::URLResponseHead* head,
std::unique_ptr<std::string> body) override;
void ProcessFailure(Response response) override;
private:
TaskCallback callback_;
};
// Helper for committing an upload session once all the parts are uploaded
// successfully.
class BoxCommitUploadSessionApiCallFlow
: public BoxChunkedUploadBaseApiCallFlow {
public:
// Additional args are: Retry-After header duration, and file id to show the
// uploaded item on Box.
using TaskCallback =
base::OnceCallback<void(Response, base::TimeDelta, const std::string&)>;
BoxCommitUploadSessionApiCallFlow(TaskCallback callback,
const std::string& session_endpoint,
const base::Value& parts,
const std::string digest);
~BoxCommitUploadSessionApiCallFlow() override;
protected:
// BoxApiCallFlow interface.
net::HttpRequestHeaders CreateApiCallHeaders() override;
std::string CreateApiCallBody() override;
bool IsExpectedSuccessCode(int code) const override;
void ProcessApiCallSuccess(const network::mojom::URLResponseHead* head,
std::unique_ptr<std::string> body) override;
void ProcessFailure(Response response) override;
private:
TaskCallback callback_;
const GURL commit_endpoint_;
const std::string sha_digest_;
base::Value upload_session_parts_;
base::TimeDelta retry_after_;
};
} // namespace enterprise_connectors
#endif // CHROME_BROWSER_ENTERPRISE_CONNECTORS_FILE_SYSTEM_BOX_API_CALL_FLOW_H_