Commit 6b2a0e0d authored by Shakti Sahu's avatar Shakti Sahu Committed by Commit Bot

Video Tutoirals : Added network fetcher

This CL adds a network fetcher for fetching the video tutorial metadata.

Bug: 1130417
Change-Id: I272a7a040e83c48e542ddd7241b4129713696ba5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2421094Reviewed-by: default avatarRamin Halavati <rhalavati@chromium.org>
Reviewed-by: default avatarMin Qin <qinmin@chromium.org>
Commit-Queue: Shakti Sahu <shaktisahu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#809231}
parent ed798b71
...@@ -14,6 +14,8 @@ source_set("internal") { ...@@ -14,6 +14,8 @@ source_set("internal") {
"proto_conversions.cc", "proto_conversions.cc",
"proto_conversions.h", "proto_conversions.h",
"store.h", "store.h",
"tutorial_fetcher.cc",
"tutorial_fetcher.h",
"tutorial_group.cc", "tutorial_group.cc",
"tutorial_group.h", "tutorial_group.h",
"tutorial_manager.h", "tutorial_manager.h",
...@@ -91,6 +93,7 @@ source_set("unit_tests") { ...@@ -91,6 +93,7 @@ source_set("unit_tests") {
sources = [ sources = [
"config_unittest.cc", "config_unittest.cc",
"proto_conversions_unittest.cc", "proto_conversions_unittest.cc",
"tutorial_fetcher_unittest.cc",
"tutorial_group_unittest.cc", "tutorial_group_unittest.cc",
"tutorial_manager_impl_unittest.cc", "tutorial_manager_impl_unittest.cc",
"tutorial_store_unittest.cc", "tutorial_store_unittest.cc",
...@@ -106,6 +109,7 @@ source_set("unit_tests") { ...@@ -106,6 +109,7 @@ source_set("unit_tests") {
"//components/leveldb_proto", "//components/leveldb_proto",
"//components/leveldb_proto:test_support", "//components/leveldb_proto:test_support",
"//components/prefs:test_support", "//components/prefs:test_support",
"//services/network:test_support",
"//testing/gmock", "//testing/gmock",
"//testing/gtest", "//testing/gtest",
] ]
......
// 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.
#include "chrome/browser/video_tutorials/internal/tutorial_fetcher.h"
#include <utility>
#include "base/lazy_instance.h"
#include "net/base/url_util.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_status_code.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/simple_url_loader.h"
namespace video_tutorials {
namespace {
// An override server URL for testing.
base::LazyInstance<GURL>::Leaky g_override_url_for_testing;
const char kRequestContentType[] = "application/x-protobuf";
constexpr net::NetworkTrafficAnnotationTag
kVideoTutorialFetcherTrafficAnnotation =
net::DefineNetworkTrafficAnnotation("video_tutorial_fetcher", R"(
semantics {
sender: "Video Tutorial Fetcher"
description:
"Fetches metadata for showing video tutorials on Android."
trigger:
"Chrome startup. Only triggered if the cache is older "
"than two weeks."
data: "No user data."
destination: GOOGLE_OWNED_SERVICE
}
policy {
cookies_allowed: NO
setting:
"No user setting to disable this feature. The "
"feature is only enabled in certain countries."
policy_exception_justification:
"Not implemented. The fetch frequency is really low."
}
)");
class TutorialFetcherImpl : public TutorialFetcher {
public:
TutorialFetcherImpl(
const GURL& url,
const std::string& api_key,
const std::string& experiment_tag,
const std::string& client_version,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
: url_loader_factory_(url_loader_factory),
url_(url),
api_key_(api_key),
experiment_tag_(experiment_tag),
client_version_(client_version) {}
private:
// TutorialFetcher implementation.
void StartFetchForTutorials(FinishedCallback callback) override {
auto resource_request = BuildGetRequest();
url_loader_ = network::SimpleURLLoader::Create(
std::move(resource_request), kVideoTutorialFetcherTrafficAnnotation);
url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
url_loader_factory_.get(),
base::BindOnce(&TutorialFetcherImpl::OnDownloadComplete,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
// Build the request to get tutorial info.
std::unique_ptr<network::ResourceRequest> BuildGetRequest() {
auto request = std::make_unique<network::ResourceRequest>();
request->method = net::HttpRequestHeaders::kGetMethod;
request->headers.SetHeader("x-goog-api-key", api_key_);
request->headers.SetHeader("X-Client-Version", client_version_);
request->headers.SetHeader(net::HttpRequestHeaders::kContentType,
kRequestContentType);
request->url = url_;
if (!experiment_tag_.empty()) {
request->url = net::AppendOrReplaceQueryParameter(
request->url, "experiment_tag", experiment_tag_);
}
if (!g_override_url_for_testing.Get().is_empty())
request->url = g_override_url_for_testing.Get();
return request;
}
// Called after receiving HTTP response. Processes the response code and net
// error.
void OnDownloadComplete(FinishedCallback callback,
std::unique_ptr<std::string> response_body) {
int response_code = -1;
if (url_loader_->ResponseInfo() && url_loader_->ResponseInfo()->headers)
response_code = url_loader_->ResponseInfo()->headers->response_code();
bool success =
(response_code >= 200 && response_code < 300 && response_body);
// TODO(shaktisahu): Collect metrics on response code.
std::move(callback).Run(success, std::move(response_body));
url_loader_.reset();
}
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
// Simple URL loader to fetch proto from network.
std::unique_ptr<network::SimpleURLLoader> url_loader_;
// Params of resource request.
GURL url_;
std::string api_key_;
std::string experiment_tag_;
std::string client_version_;
base::WeakPtrFactory<TutorialFetcherImpl> weak_ptr_factory_{this};
};
} // namespace
// static
std::unique_ptr<TutorialFetcher> TutorialFetcher::Create(
const GURL& url,
const std::string& api_key,
const std::string& experiment_tag,
const std::string& client_version,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
return std::make_unique<TutorialFetcherImpl>(
url, api_key, experiment_tag, client_version, url_loader_factory);
}
// static
void TutorialFetcher::SetOverrideURLForTesting(const GURL& url) {
g_override_url_for_testing.Get() = url;
}
TutorialFetcher::TutorialFetcher() = default;
TutorialFetcher::~TutorialFetcher() = default;
} // namespace video_tutorials
// 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_VIDEO_TUTORIALS_INTERNAL_TUTORIAL_FETCHER_H_
#define CHROME_BROWSER_VIDEO_TUTORIALS_INTERNAL_TUTORIAL_FETCHER_H_
#include <memory>
#include <string>
#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "url/gurl.h"
namespace network {
class SharedURLLoaderFactory;
} // namespace network
namespace video_tutorials {
class TutorialFetcher {
public:
// Called after the fetch task is done, |status| and serialized response
// |data| will be returned. Invoked with |nullptr| if status is not success.
using FinishedCallback =
base::OnceCallback<void(bool success,
std::unique_ptr<std::string> response_body)>;
// Method to create a TutorialFetcher.
static std::unique_ptr<TutorialFetcher> Create(
const GURL& url,
const std::string& api_key,
const std::string& experiment_tag,
const std::string& client_version,
const scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
// For testing only.
static void SetOverrideURLForTesting(const GURL& url);
// Start the fetch to download tutorials.
virtual void StartFetchForTutorials(FinishedCallback callback) = 0;
virtual ~TutorialFetcher();
TutorialFetcher(const TutorialFetcher& other) = delete;
TutorialFetcher& operator=(const TutorialFetcher& other) = delete;
protected:
TutorialFetcher();
};
} // namespace video_tutorials
#endif // CHROME_BROWSER_VIDEO_TUTORIALS_INTERNAL_TUTORIAL_FETCHER_H_
// 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.
#include "chrome/browser/video_tutorials/internal/tutorial_fetcher.h"
#include "base/test/bind_test_util.h"
#include "base/test/task_environment.h"
#include "net/http/http_status_code.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/test/test_url_loader_factory.h"
#include "services/network/test/test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "url/gurl.h"
namespace {
const char kServerURL[] = "https://www.test.com";
const char kGoogleApiKey[] = "api-key";
const char kVideoTutorialsMessage[] = R"pb(
tutorial_groups {
locale: "en"
tutorial {
feature: "1"
title: "feature1"
video_url: "https://some_url.com/video.mp4"
poster_url: "https://some_url.com/poster.jpg"
caption_url: "https://some_url.com/caption.vtt"
}
})pb";
} // namespace
namespace video_tutorials {
class TutorialFetcherTest : public testing::Test {
public:
TutorialFetcherTest();
~TutorialFetcherTest() override = default;
TutorialFetcherTest(const TutorialFetcherTest& other) = delete;
TutorialFetcherTest& operator=(const TutorialFetcherTest& other) = delete;
void SetUp() override;
std::unique_ptr<TutorialFetcher> CreateFetcher();
bool RunFetcherWithNetError(net::Error net_error);
bool RunFetcherWithHttpError(net::HttpStatusCode http_error);
bool RunFetcherWithData(const std::string& response_data,
std::string* data_received);
void RespondWithNetError(int net_error);
void RespondWithHttpError(net::HttpStatusCode http_error);
void RespondWithData(const std::string& data);
network::ResourceRequest last_resource_request() const {
return last_resource_request_;
}
private:
TutorialFetcher::FinishedCallback StoreResult();
bool RunFetcher(base::OnceCallback<void(void)> respond_callback,
std::string* data_received);
network::TestURLLoaderFactory test_url_loader_factory_;
scoped_refptr<network::SharedURLLoaderFactory>
test_shared_url_loader_factory_;
std::unique_ptr<std::string> last_data_;
bool last_status_ = false;
network::ResourceRequest last_resource_request_;
base::test::SingleThreadTaskEnvironment task_environment_{
base::test::SingleThreadTaskEnvironment::MainThreadType::IO,
base::test::SingleThreadTaskEnvironment::TimeSource::MOCK_TIME};
};
TutorialFetcherTest::TutorialFetcherTest()
: test_shared_url_loader_factory_(
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory_)) {}
void TutorialFetcherTest::SetUp() {
test_url_loader_factory_.SetInterceptor(
base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
EXPECT_TRUE(request.url.is_valid() && !request.url.is_empty());
last_resource_request_ = request;
}));
}
TutorialFetcher::FinishedCallback TutorialFetcherTest::StoreResult() {
return base::BindLambdaForTesting(
[&](bool success, std::unique_ptr<std::string> data) {
last_status_ = success;
last_data_ = std::move(data);
});
}
std::unique_ptr<TutorialFetcher> TutorialFetcherTest::CreateFetcher() {
std::unique_ptr<TutorialFetcher> fetcher = TutorialFetcher::Create(
GURL(kServerURL), kGoogleApiKey, "", "", test_shared_url_loader_factory_);
return fetcher;
}
bool TutorialFetcherTest::RunFetcherWithNetError(net::Error net_error) {
std::string data_received;
bool status =
RunFetcher(base::BindOnce(&TutorialFetcherTest::RespondWithNetError,
base::Unretained(this), net_error),
&data_received);
EXPECT_TRUE(data_received.empty());
return status;
}
bool TutorialFetcherTest::RunFetcherWithHttpError(
net::HttpStatusCode http_error) {
std::string data_received;
bool status =
RunFetcher(base::BindOnce(&TutorialFetcherTest::RespondWithHttpError,
base::Unretained(this), http_error),
&data_received);
EXPECT_TRUE(data_received.empty());
return status;
}
bool TutorialFetcherTest::RunFetcherWithData(const std::string& response_data,
std::string* data_received) {
bool success =
RunFetcher(base::BindOnce(&TutorialFetcherTest::RespondWithData,
base::Unretained(this), response_data),
data_received);
return success;
}
void TutorialFetcherTest::RespondWithNetError(int net_error) {
network::URLLoaderCompletionStatus completion_status(net_error);
test_url_loader_factory_.SimulateResponseForPendingRequest(
last_resource_request_.url, completion_status,
network::mojom::URLResponseHead::New(), std::string(),
network::TestURLLoaderFactory::kMostRecentMatch);
}
void TutorialFetcherTest::RespondWithHttpError(net::HttpStatusCode http_error) {
auto url_response_head = network::CreateURLResponseHead(http_error);
test_url_loader_factory_.SimulateResponseForPendingRequest(
last_resource_request_.url, network::URLLoaderCompletionStatus(net::OK),
std::move(url_response_head), std::string(),
network::TestURLLoaderFactory::kMostRecentMatch);
}
void TutorialFetcherTest::RespondWithData(const std::string& data) {
test_url_loader_factory_.SimulateResponseForPendingRequest(
last_resource_request_.url.spec(), data, net::HTTP_OK,
network::TestURLLoaderFactory::kMostRecentMatch);
}
bool TutorialFetcherTest::RunFetcher(
base::OnceCallback<void(void)> respond_callback,
std::string* data_received) {
auto fetcher = CreateFetcher();
fetcher->StartFetchForTutorials(StoreResult());
std::move(respond_callback).Run();
task_environment_.RunUntilIdle();
if (last_data_)
*data_received = *last_data_;
return last_status_;
}
// Tests that net errors will result in failed status.
TEST_F(TutorialFetcherTest, NetErrors) {
EXPECT_EQ(false, RunFetcherWithNetError(net::ERR_BLOCKED_BY_ADMINISTRATOR));
}
// Tests that http errors will result in failed status.
TEST_F(TutorialFetcherTest, HttpErrors) {
EXPECT_EQ(false, RunFetcherWithHttpError(net::HTTP_NOT_FOUND));
}
// Tests that empty responses are handled properly.
TEST_F(TutorialFetcherTest, EmptyResponse) {
std::string data;
EXPECT_EQ(true, RunFetcherWithData("", &data));
EXPECT_TRUE(data.empty());
}
// Tests that a susscess response is received properly.
TEST_F(TutorialFetcherTest, Success) {
std::string data;
EXPECT_EQ(true, RunFetcherWithData(kVideoTutorialsMessage, &data));
EXPECT_EQ(kVideoTutorialsMessage, data);
EXPECT_EQ(last_resource_request().url.spec(), "https://www.test.com/");
}
} // namespace video_tutorials
...@@ -341,6 +341,7 @@ Refer to README.md for content description and update process. ...@@ -341,6 +341,7 @@ Refer to README.md for content description and update process.
<item id="url_fetcher_downloader" added_in_milestone="62" hash_code="113231892" type="0" deprecated="2019-02-09" content_hash_code="61085066" file_path=""/> <item id="url_fetcher_downloader" added_in_milestone="62" hash_code="113231892" type="0" deprecated="2019-02-09" content_hash_code="61085066" file_path=""/>
<item id="url_prevision_fetcher" added_in_milestone="62" hash_code="118389509" type="0" content_hash_code="66145513" os_list="linux,windows" file_path="content/browser/media/url_provision_fetcher.cc"/> <item id="url_prevision_fetcher" added_in_milestone="62" hash_code="118389509" type="0" content_hash_code="66145513" os_list="linux,windows" file_path="content/browser/media/url_provision_fetcher.cc"/>
<item id="user_info_fetcher" added_in_milestone="62" hash_code="22265491" type="0" content_hash_code="72016232" os_list="linux,windows" file_path="components/policy/core/common/cloud/user_info_fetcher.cc"/> <item id="user_info_fetcher" added_in_milestone="62" hash_code="22265491" type="0" content_hash_code="72016232" os_list="linux,windows" file_path="components/policy/core/common/cloud/user_info_fetcher.cc"/>
<item id="video_tutorial_fetcher" added_in_milestone="87" hash_code="69879956" type="0" content_hash_code="121911479" os_list="linux,windows" file_path="chrome/browser/video_tutorials/internal/tutorial_fetcher.cc"/>
<item id="viz_devtools_server" added_in_milestone="72" hash_code="16292315" type="0" content_hash_code="70061664" os_list="linux,windows" file_path="components/ui_devtools/devtools_server.cc"/> <item id="viz_devtools_server" added_in_milestone="72" hash_code="16292315" type="0" content_hash_code="70061664" os_list="linux,windows" file_path="components/ui_devtools/devtools_server.cc"/>
<item id="web_bundle_loader" added_in_milestone="84" hash_code="114615359" type="0" content_hash_code="57390734" os_list="linux,windows" file_path="content/browser/web_package/web_bundle_utils.cc"/> <item id="web_bundle_loader" added_in_milestone="84" hash_code="114615359" type="0" content_hash_code="57390734" os_list="linux,windows" file_path="content/browser/web_package/web_bundle_utils.cc"/>
<item id="web_history_counter" added_in_milestone="62" hash_code="137457845" type="1" second_id="110307337" content_hash_code="49663381" os_list="linux,windows" semantics_fields="2,3,4" policy_fields="4" file_path="components/browsing_data/core/counters/history_counter.cc"/> <item id="web_history_counter" added_in_milestone="62" hash_code="137457845" type="1" second_id="110307337" content_hash_code="49663381" os_list="linux,windows" semantics_fields="2,3,4" policy_fields="4" file_path="components/browsing_data/core/counters/history_counter.cc"/>
......
...@@ -87,6 +87,7 @@ hidden="true" so that these annotations don't show up in the document. ...@@ -87,6 +87,7 @@ hidden="true" so that these annotations don't show up in the document.
<traffic_annotation unique_id="proxy_config_settings"/> <traffic_annotation unique_id="proxy_config_settings"/>
<traffic_annotation unique_id="query_tiles_image_loader"/> <traffic_annotation unique_id="query_tiles_image_loader"/>
<traffic_annotation unique_id="query_tiles_fetcher"/> <traffic_annotation unique_id="query_tiles_fetcher"/>
<traffic_annotation unique_id="video_tutorial_fetcher"/>
<traffic_annotation unique_id="qr_code_save"/> <traffic_annotation unique_id="qr_code_save"/>
<traffic_annotation unique_id="safe_browsing_cache_collector"/> <traffic_annotation unique_id="safe_browsing_cache_collector"/>
<traffic_annotation unique_id="safety_check_update_connectivity"/> <traffic_annotation unique_id="safety_check_update_connectivity"/>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment