boca Mojom implementation for fetching course assignments from google
classroom API. This CL implements fetching the assignments from Google classroom API. The assignments and the associated materials will be displayed in the Boca UI for both teachers and students. This CL also fixed the following lint issues: 1. ash/webui/boca_ui/boca_app_page_handler.cc line 260 2. ash/webui/boca_ui/provider/classroom_page_handler_impl.h line 45-46 Bug: 390256931 Test: unit tested. Change-Id: I299f08fc5a86fe3ce7e5160ad6f1ec05deead59e Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6196249 Reviewed-by: Benjamin Zielinski <bzielinski@google.com> Reviewed-by: Vignesh Shenvi <vshenvi@google.com> Commit-Queue: Zifan Zhang <zifanzhang@google.com> Cr-Commit-Position: refs/heads/main@{#1411839}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
00df177ab1
commit
c15e628d13
ash/webui/boca_ui
boca_app_page_handler.ccboca_app_page_handler.h
mojom
provider
BUILD.gnclassroom_page_handler_impl.ccclassroom_page_handler_impl.hclassroom_page_handler_impl_unittest.cc
resources
chrome/test/data/webui/chromeos/boca_ui
@ -240,6 +240,11 @@ void BocaAppHandler::ListStudents(const std::string& course_id,
|
||||
class_room_page_handler_->ListStudents(course_id, std::move(callback));
|
||||
}
|
||||
|
||||
void BocaAppHandler::ListAssignments(const std::string& course_id,
|
||||
ListAssignmentsCallback callback) {
|
||||
class_room_page_handler_->ListAssignments(course_id, std::move(callback));
|
||||
}
|
||||
|
||||
void BocaAppHandler::CreateSession(mojom::ConfigPtr config,
|
||||
CreateSessionCallback callback) {
|
||||
std::unique_ptr<CreateSessionRequest> request =
|
||||
@ -252,7 +257,7 @@ void BocaAppHandler::CreateSession(mojom::ConfigPtr config,
|
||||
[](CreateSessionCallback callback,
|
||||
base::expected<std::unique_ptr<::boca::Session>,
|
||||
google_apis::ApiErrorCode> result) {
|
||||
// TODO(b/358476060):Potentially parse error code to UI;
|
||||
// TODO(b/358476060): Potentially parse error code to UI;
|
||||
if (!result.has_value()) {
|
||||
std::move(callback).Run(false);
|
||||
} else {
|
||||
|
@ -59,6 +59,8 @@ class BocaAppHandler : public mojom::PageHandler,
|
||||
void ListCourses(ListCoursesCallback callback) override;
|
||||
void ListStudents(const std::string& course_id,
|
||||
ListStudentsCallback callback) override;
|
||||
void ListAssignments(const std::string& course_id,
|
||||
ListAssignmentsCallback callback) override;
|
||||
void CreateSession(mojom::ConfigPtr config,
|
||||
CreateSessionCallback callback) override;
|
||||
void GetSession(GetSessionCallback callback) override;
|
||||
|
@ -46,6 +46,31 @@ struct Course {
|
||||
string section;
|
||||
};
|
||||
|
||||
// The assignment material type.
|
||||
enum MaterialType {
|
||||
kUnknown,
|
||||
kSharedDriveFile,
|
||||
kYoutubeVideo,
|
||||
kLink,
|
||||
kForm,
|
||||
};
|
||||
|
||||
// Represents a material attached to a course assignment.
|
||||
struct Material {
|
||||
string title;
|
||||
MaterialType type;
|
||||
};
|
||||
|
||||
// Represents a course assignment.
|
||||
struct Assignment {
|
||||
string title;
|
||||
// Absolute link to this assignment for students to open.
|
||||
url.mojom.Url url;
|
||||
mojo_base.mojom.JSTime last_update_time;
|
||||
// Assignment materials.
|
||||
array<Material> materials;
|
||||
};
|
||||
|
||||
// Represents a browser window.
|
||||
struct Window {
|
||||
// User-customized window name.
|
||||
@ -206,6 +231,8 @@ interface PageHandler {
|
||||
// course. Must be called on a course_id received from the most recent
|
||||
// ListCourses call.
|
||||
ListStudents(string course_id) => (array<Identity> students);
|
||||
// Fetch a list of assignments for the courses.
|
||||
ListAssignments(string course_id) => (array<Assignment> assignments);
|
||||
// Create a session with `config`.
|
||||
CreateSession(Config config) => (bool success);
|
||||
// Retrieves the current session.
|
||||
|
@ -45,5 +45,6 @@ source_set("unit_tests") {
|
||||
"//services/network:test_support",
|
||||
"//testing/gmock",
|
||||
"//testing/gtest",
|
||||
"//url",
|
||||
]
|
||||
}
|
||||
|
@ -9,7 +9,9 @@
|
||||
#include "chromeos/ash/components/boca/boca_app_client.h"
|
||||
#include "components/signin/public/base/consent_level.h"
|
||||
#include "components/signin/public/identity_manager/identity_manager.h"
|
||||
#include "google_apis/classroom/classroom_api_course_work_response_types.h"
|
||||
#include "google_apis/classroom/classroom_api_courses_response_types.h"
|
||||
#include "google_apis/classroom/classroom_api_list_course_work_request.h"
|
||||
#include "google_apis/classroom/classroom_api_list_courses_request.h"
|
||||
#include "google_apis/classroom/classroom_api_list_students_request.h"
|
||||
#include "google_apis/classroom/classroom_api_students_response_types.h"
|
||||
@ -21,6 +23,7 @@ namespace ash::boca {
|
||||
namespace {
|
||||
using ::google_apis::ApiErrorCode;
|
||||
using ::google_apis::classroom::ListCoursesRequest;
|
||||
using ::google_apis::classroom::ListCourseWorkRequest;
|
||||
using ::google_apis::classroom::ListStudentsRequest;
|
||||
|
||||
// TODO(b/343731449): Update this once policy to control has been added.
|
||||
@ -79,6 +82,14 @@ void ClassroomPageHandlerImpl::ListStudents(const std::string& course_id,
|
||||
std::make_unique<StudentList>(), std::move(callback));
|
||||
}
|
||||
|
||||
void ClassroomPageHandlerImpl::ListAssignments(
|
||||
const std::string& course_id,
|
||||
ListAssignmentsCallback callback) {
|
||||
ListAssignmentsHelper(course_id, /*page_token=*/"",
|
||||
std::make_unique<AssignmentList>(),
|
||||
std::move(callback));
|
||||
}
|
||||
|
||||
void ClassroomPageHandlerImpl::ListCoursesHelper(
|
||||
const std::string& teacher_id,
|
||||
const std::string& page_token,
|
||||
@ -156,6 +167,77 @@ void ClassroomPageHandlerImpl::OnListStudentsFetched(
|
||||
}
|
||||
}
|
||||
|
||||
void ClassroomPageHandlerImpl::ListAssignmentsHelper(
|
||||
const std::string& course_id,
|
||||
const std::string& page_token,
|
||||
std::unique_ptr<AssignmentList> fetched_assignments,
|
||||
ListAssignmentsCallback callback) {
|
||||
sender_->StartRequestWithAuthRetry(std::make_unique<ListCourseWorkRequest>(
|
||||
sender_.get(), course_id, page_token,
|
||||
base::BindOnce(&ClassroomPageHandlerImpl::OnListAssignmentsFetched,
|
||||
weak_factory_.GetWeakPtr(), course_id,
|
||||
std::move(fetched_assignments), std::move(callback))));
|
||||
}
|
||||
|
||||
void ClassroomPageHandlerImpl::OnListAssignmentsFetched(
|
||||
const std::string& course_id,
|
||||
std::unique_ptr<AssignmentList> fetched_assignments,
|
||||
ListAssignmentsCallback callback,
|
||||
base::expected<std::unique_ptr<google_apis::classroom::CourseWork>,
|
||||
ApiErrorCode> result) {
|
||||
if (!result.has_value()) {
|
||||
std::move(callback).Run(std::move(*fetched_assignments));
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& item : result.value()->items()) {
|
||||
if (item->type() !=
|
||||
google_apis::classroom::CourseWorkItem::Type::kAssignment) {
|
||||
continue;
|
||||
}
|
||||
std::vector<mojom::MaterialPtr> materials = {};
|
||||
for (const auto& apiMaterial : item->materials()) {
|
||||
mojom::MaterialPtr material = mojom::Material::New();
|
||||
material->title = apiMaterial->title();
|
||||
switch (apiMaterial->type()) {
|
||||
case (google_apis::classroom::Material::Type::kSharedDriveFile):
|
||||
material->type = mojom::MaterialType::kSharedDriveFile;
|
||||
break;
|
||||
case (google_apis::classroom::Material::Type::kYoutubeVideo):
|
||||
material->type = mojom::MaterialType::kYoutubeVideo;
|
||||
break;
|
||||
case (google_apis::classroom::Material::Type::kLink):
|
||||
material->type = mojom::MaterialType::kLink;
|
||||
break;
|
||||
case (google_apis::classroom::Material::Type::kForm):
|
||||
material->type = mojom::MaterialType::kForm;
|
||||
break;
|
||||
case (google_apis::classroom::Material::Type::kUnknown):
|
||||
default:
|
||||
material->type = mojom::MaterialType::kUnknown;
|
||||
break;
|
||||
}
|
||||
|
||||
materials.push_back(std::move(material));
|
||||
}
|
||||
|
||||
mojom::AssignmentPtr assignment = mojom::Assignment::New();
|
||||
assignment->title = item->title();
|
||||
assignment->url = item->alternate_link();
|
||||
assignment->materials = std::move(materials);
|
||||
assignment->last_update_time = std::move(item->last_update());
|
||||
|
||||
fetched_assignments->push_back(std::move(assignment));
|
||||
}
|
||||
|
||||
if (!result.value()->next_page_token().empty()) {
|
||||
ListAssignmentsHelper(course_id, result.value()->next_page_token(),
|
||||
std::move(fetched_assignments), std::move(callback));
|
||||
} else {
|
||||
std::move(callback).Run(std::move(*fetched_assignments));
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
std::unique_ptr<google_apis::RequestSender>
|
||||
ClassroomPageHandlerImpl::CreateRequestSender() {
|
||||
|
@ -20,6 +20,8 @@ using ListCoursesCallback =
|
||||
base::OnceCallback<void(std::vector<mojom::CoursePtr>)>;
|
||||
using ListStudentsCallback =
|
||||
base::OnceCallback<void(std::vector<mojom::IdentityPtr>)>;
|
||||
using ListAssignmentsCallback =
|
||||
base::OnceCallback<void(std::vector<mojom::AssignmentPtr>)>;
|
||||
|
||||
namespace google_apis {
|
||||
class RequestSender;
|
||||
@ -27,6 +29,7 @@ class RequestSender;
|
||||
namespace classroom {
|
||||
class Courses;
|
||||
class Students;
|
||||
class CourseWork;
|
||||
} // namespace classroom
|
||||
} // namespace google_apis
|
||||
|
||||
@ -34,11 +37,13 @@ namespace ash::boca {
|
||||
|
||||
using StudentList = std::vector<mojom::IdentityPtr>;
|
||||
using CourseList = std::vector<mojom::CoursePtr>;
|
||||
using AssignmentList = std::vector<mojom::AssignmentPtr>;
|
||||
|
||||
class ClassroomPageHandlerImpl {
|
||||
public:
|
||||
ClassroomPageHandlerImpl();
|
||||
ClassroomPageHandlerImpl(std::unique_ptr<google_apis::RequestSender> sender);
|
||||
explicit ClassroomPageHandlerImpl(
|
||||
std::unique_ptr<google_apis::RequestSender> sender);
|
||||
|
||||
ClassroomPageHandlerImpl(const ClassroomPageHandlerImpl&) = delete;
|
||||
ClassroomPageHandlerImpl& operator=(const ClassroomPageHandlerImpl&) = delete;
|
||||
@ -49,6 +54,9 @@ class ClassroomPageHandlerImpl {
|
||||
|
||||
void ListStudents(const std::string& courseId, ListStudentsCallback callback);
|
||||
|
||||
void ListAssignments(const std::string& courseId,
|
||||
ListAssignmentsCallback callback);
|
||||
|
||||
private:
|
||||
void ListCoursesHelper(const std::string& teacher_id,
|
||||
const std::string& page_token,
|
||||
@ -74,6 +82,19 @@ class ClassroomPageHandlerImpl {
|
||||
base::expected<std::unique_ptr<google_apis::classroom::Students>,
|
||||
google_apis::ApiErrorCode> result);
|
||||
|
||||
void ListAssignmentsHelper(
|
||||
const std::string& course_id,
|
||||
const std::string& page_token,
|
||||
std::unique_ptr<AssignmentList> fetched_assignments,
|
||||
ListAssignmentsCallback callback);
|
||||
|
||||
void OnListAssignmentsFetched(
|
||||
const std::string& course_id,
|
||||
std::unique_ptr<AssignmentList> fetched_assignments,
|
||||
ListAssignmentsCallback callback,
|
||||
base::expected<std::unique_ptr<google_apis::classroom::CourseWork>,
|
||||
google_apis::ApiErrorCode> result);
|
||||
|
||||
static std::unique_ptr<google_apis::RequestSender> CreateRequestSender();
|
||||
|
||||
std::unique_ptr<google_apis::RequestSender> sender_;
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "content/public/test/browser_task_environment.h"
|
||||
#include "google_apis/common/dummy_auth_service.h"
|
||||
#include "google_apis/common/test_util.h"
|
||||
#include "google_apis/common/time_util.h"
|
||||
#include "google_apis/gaia/gaia_urls.h"
|
||||
#include "google_apis/gaia/gaia_urls_overrider_for_testing.h"
|
||||
#include "net/http/http_status_code.h"
|
||||
@ -19,6 +20,7 @@
|
||||
#include "services/network/test/test_shared_url_loader_factory.h"
|
||||
#include "testing/gmock/include/gmock/gmock.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "url/gurl.h"
|
||||
|
||||
namespace ash::boca {
|
||||
namespace {
|
||||
@ -511,4 +513,318 @@ TEST_F(ClassroomPageHandlerImplTest, ListStudentsWithInvalidCourseId) {
|
||||
ASSERT_EQ(response.size(), 0u);
|
||||
}
|
||||
|
||||
TEST_F(ClassroomPageHandlerImplTest, ListAllAssignments) {
|
||||
EXPECT_CALL(request_handler(), HandleRequest(Field(&HttpRequest::relative_url,
|
||||
HasSubstr("/courses?"))))
|
||||
.WillOnce(Return(ByMove(TestRequestHandler::CreateSuccessfulResponse(R"(
|
||||
{
|
||||
"courses": [
|
||||
{
|
||||
"id": "course-id-1",
|
||||
"name": "Course 1",
|
||||
"courseState": "ACTIVE"
|
||||
}
|
||||
]
|
||||
})"))));
|
||||
|
||||
std::vector<mojom::CoursePtr> course_response;
|
||||
base::MockCallback<ListCoursesCallback> course_callback;
|
||||
EXPECT_CALL(course_callback, Run(testing::_))
|
||||
.Times(1)
|
||||
.WillOnce(testing::Invoke([&](std::vector<mojom::CoursePtr> courses) {
|
||||
course_response = std::move(courses);
|
||||
}));
|
||||
|
||||
base::RunLoop course_run_loop;
|
||||
classroom_handler()->ListCourses(
|
||||
"foo", google_apis::test_util::CreateQuitCallback(&course_run_loop,
|
||||
course_callback.Get()));
|
||||
course_run_loop.Run();
|
||||
|
||||
EXPECT_CALL(request_handler(),
|
||||
HandleRequest(
|
||||
Field(&HttpRequest::relative_url, HasSubstr("/courseWork?"))))
|
||||
.WillOnce(Return(ByMove(TestRequestHandler::CreateSuccessfulResponse(R"(
|
||||
{
|
||||
"courseWork": [
|
||||
{
|
||||
"id": "assignment-multiple-materials-id",
|
||||
"title": "assignment-multiple-materials-title",
|
||||
"alternateLink": "http://assignment-multiple-materials-url.com",
|
||||
"workType": "ASSIGNMENT",
|
||||
"updateTime": "2025-01-01T00:00:00.000Z",
|
||||
"materials": [
|
||||
{
|
||||
"driveFile": {
|
||||
"driveFile": {
|
||||
"title": "drive-file-title"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"youtubeVideo": {
|
||||
"title": "youtube-video-title"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "assignment-link-materials-id",
|
||||
"title": "assignment-link-materials-title",
|
||||
"alternateLink": "http://assignment-link-materials-url.com",
|
||||
"workType": "ASSIGNMENT",
|
||||
"updateTime": "2025-01-02T01:02:03.400Z",
|
||||
"materials": [
|
||||
{
|
||||
"link": {
|
||||
"title": "link-title"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "assignment-form-materials-id",
|
||||
"title": "assignment-form-materials-title",
|
||||
"alternateLink": "http://assignment-form-materials-url.com",
|
||||
"workType": "ASSIGNMENT",
|
||||
"updateTime": "2025-02-03T02:03:04.500Z",
|
||||
"materials": [
|
||||
{
|
||||
"form": {
|
||||
"title": "form-title"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "assignment-unknown-materials-id",
|
||||
"title": "assignment-unknown-materials-title",
|
||||
"alternateLink": "http://assignment-unknown-materials-url.com",
|
||||
"workType": "ASSIGNMENT",
|
||||
"updateTime": "2025-03-04T03:04:05.600Z",
|
||||
"materials": [
|
||||
{
|
||||
"unknownType": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "question-id",
|
||||
"title": "question-title",
|
||||
"alternateLink": "http://question-url.com",
|
||||
"workType": "MULTIPLE_CHOICE_QUESTION",
|
||||
"updateTime": "2025-04-05T04:05:06.700Z"
|
||||
}
|
||||
]
|
||||
})"))));
|
||||
|
||||
std::vector<mojom::AssignmentPtr> response;
|
||||
base::MockCallback<ListAssignmentsCallback> callback;
|
||||
EXPECT_CALL(callback, Run(testing::_))
|
||||
.Times(1)
|
||||
.WillOnce(
|
||||
testing::Invoke([&](std::vector<mojom::AssignmentPtr> assignments) {
|
||||
response = std::move(assignments);
|
||||
}));
|
||||
|
||||
base::RunLoop run_loop;
|
||||
classroom_handler()->ListAssignments(
|
||||
course_response.at(0)->id,
|
||||
google_apis::test_util::CreateQuitCallback(&run_loop, callback.Get()));
|
||||
run_loop.Run();
|
||||
|
||||
ASSERT_EQ(response.size(), 4u);
|
||||
EXPECT_EQ(response.at(0)->title, "assignment-multiple-materials-title");
|
||||
EXPECT_EQ(response.at(0)->url,
|
||||
GURL("http://assignment-multiple-materials-url.com"));
|
||||
EXPECT_EQ(
|
||||
google_apis::util::FormatTimeAsString(response.at(0)->last_update_time),
|
||||
"2025-01-01T00:00:00.000Z");
|
||||
EXPECT_EQ(response.at(0)->materials.size(), 2u);
|
||||
EXPECT_EQ(response.at(0)->materials.at(0)->title, "drive-file-title");
|
||||
EXPECT_EQ(response.at(0)->materials.at(0)->type,
|
||||
mojom::MaterialType::kSharedDriveFile);
|
||||
EXPECT_EQ(response.at(0)->materials.at(1)->title, "youtube-video-title");
|
||||
EXPECT_EQ(response.at(0)->materials.at(1)->type,
|
||||
mojom::MaterialType::kYoutubeVideo);
|
||||
|
||||
EXPECT_EQ(response.at(1)->title, "assignment-link-materials-title");
|
||||
EXPECT_EQ(response.at(1)->url,
|
||||
GURL("http://assignment-link-materials-url.com"));
|
||||
EXPECT_EQ(
|
||||
google_apis::util::FormatTimeAsString(response.at(1)->last_update_time),
|
||||
"2025-01-02T01:02:03.400Z");
|
||||
|
||||
EXPECT_EQ(response.at(1)->materials.size(), 1u);
|
||||
EXPECT_EQ(response.at(1)->materials.at(0)->title, "link-title");
|
||||
EXPECT_EQ(response.at(1)->materials.at(0)->type, mojom::MaterialType::kLink);
|
||||
|
||||
EXPECT_EQ(response.at(2)->title, "assignment-form-materials-title");
|
||||
EXPECT_EQ(response.at(2)->url,
|
||||
GURL("http://assignment-form-materials-url.com"));
|
||||
EXPECT_EQ(
|
||||
google_apis::util::FormatTimeAsString(response.at(2)->last_update_time),
|
||||
"2025-02-03T02:03:04.500Z");
|
||||
EXPECT_EQ(response.at(2)->materials.size(), 1u);
|
||||
EXPECT_EQ(response.at(2)->materials.at(0)->title, "form-title");
|
||||
EXPECT_EQ(response.at(2)->materials.at(0)->type, mojom::MaterialType::kForm);
|
||||
|
||||
EXPECT_EQ(response.at(3)->title, "assignment-unknown-materials-title");
|
||||
EXPECT_EQ(response.at(3)->url,
|
||||
GURL("http://assignment-unknown-materials-url.com"));
|
||||
EXPECT_EQ(
|
||||
google_apis::util::FormatTimeAsString(response.at(3)->last_update_time),
|
||||
"2025-03-04T03:04:05.600Z");
|
||||
EXPECT_EQ(response.at(3)->materials.size(), 1u);
|
||||
EXPECT_EQ(response.at(3)->materials.at(0)->type,
|
||||
mojom::MaterialType::kUnknown);
|
||||
}
|
||||
|
||||
TEST_F(ClassroomPageHandlerImplTest, ListAssignmentsOnHttpError) {
|
||||
EXPECT_CALL(request_handler(), HandleRequest(Field(&HttpRequest::relative_url,
|
||||
HasSubstr("/courses?"))))
|
||||
.WillOnce(Return(ByMove(TestRequestHandler::CreateSuccessfulResponse(R"(
|
||||
{
|
||||
"courses": [
|
||||
{
|
||||
"id": "course-id-1",
|
||||
"name": "Course 1",
|
||||
"courseState": "ACTIVE"
|
||||
}
|
||||
]
|
||||
})"))));
|
||||
|
||||
std::vector<mojom::CoursePtr> course_response;
|
||||
base::MockCallback<ListCoursesCallback> course_callback;
|
||||
EXPECT_CALL(course_callback, Run(testing::_))
|
||||
.Times(1)
|
||||
.WillOnce(testing::Invoke([&](std::vector<mojom::CoursePtr> courses) {
|
||||
course_response = std::move(courses);
|
||||
}));
|
||||
|
||||
base::RunLoop course_run_loop;
|
||||
classroom_handler()->ListCourses(
|
||||
"foo", google_apis::test_util::CreateQuitCallback(&course_run_loop,
|
||||
course_callback.Get()));
|
||||
course_run_loop.Run();
|
||||
|
||||
EXPECT_CALL(request_handler(), HandleRequest(testing::_))
|
||||
.WillOnce(Return(ByMove(TestRequestHandler::CreateFailedResponse())));
|
||||
|
||||
std::vector<mojom::AssignmentPtr> response;
|
||||
base::MockCallback<ListAssignmentsCallback> callback;
|
||||
EXPECT_CALL(callback, Run(testing::_))
|
||||
.Times(1)
|
||||
.WillOnce(
|
||||
testing::Invoke([&](std::vector<mojom::AssignmentPtr> assignments) {
|
||||
response = std::move(assignments);
|
||||
}));
|
||||
|
||||
base::RunLoop run_loop;
|
||||
classroom_handler()->ListAssignments(
|
||||
course_response.at(0)->id,
|
||||
google_apis::test_util::CreateQuitCallback(&run_loop, callback.Get()));
|
||||
run_loop.Run();
|
||||
|
||||
ASSERT_EQ(response.size(), 0u);
|
||||
}
|
||||
|
||||
TEST_F(ClassroomPageHandlerImplTest, ListAssignmentsMultiplePages) {
|
||||
EXPECT_CALL(request_handler(), HandleRequest(Field(&HttpRequest::relative_url,
|
||||
HasSubstr("/courses?"))))
|
||||
.WillOnce(Return(ByMove(TestRequestHandler::CreateSuccessfulResponse(R"(
|
||||
{
|
||||
"courses": [
|
||||
{
|
||||
"id": "course-id-1",
|
||||
"name": "Course 1",
|
||||
"courseState": "ACTIVE"
|
||||
}
|
||||
]
|
||||
})"))));
|
||||
|
||||
std::vector<mojom::CoursePtr> course_response;
|
||||
base::MockCallback<ListCoursesCallback> course_callback;
|
||||
EXPECT_CALL(course_callback, Run(testing::_))
|
||||
.Times(1)
|
||||
.WillOnce(testing::Invoke([&](std::vector<mojom::CoursePtr> courses) {
|
||||
course_response = std::move(courses);
|
||||
}));
|
||||
|
||||
base::RunLoop course_run_loop;
|
||||
classroom_handler()->ListCourses(
|
||||
"foo", google_apis::test_util::CreateQuitCallback(&course_run_loop,
|
||||
course_callback.Get()));
|
||||
course_run_loop.Run();
|
||||
|
||||
EXPECT_CALL(request_handler(),
|
||||
HandleRequest(Field(&HttpRequest::relative_url,
|
||||
AllOf(HasSubstr("/courseWork?"),
|
||||
Not(HasSubstr("pageToken"))))))
|
||||
.WillOnce(Return(ByMove(TestRequestHandler::CreateSuccessfulResponse(R"(
|
||||
{
|
||||
"courseWork": [
|
||||
{
|
||||
"id": "id-page-1",
|
||||
"title": "title-page-1",
|
||||
"alternateLink": "http://url-page-1.com",
|
||||
"workType": "ASSIGNMENT"
|
||||
}
|
||||
],
|
||||
"nextPageToken": "page-2-token"
|
||||
})"))));
|
||||
|
||||
EXPECT_CALL(request_handler(),
|
||||
HandleRequest(Field(&HttpRequest::relative_url,
|
||||
AllOf(HasSubstr("/courseWork?"),
|
||||
HasSubstr("pageToken=page-2-token")))))
|
||||
.WillOnce(Return(ByMove(TestRequestHandler::CreateSuccessfulResponse(R"(
|
||||
{
|
||||
"courseWork": [
|
||||
{
|
||||
"id": "id-page-2",
|
||||
"title": "title-page-2",
|
||||
"alternateLink": "http://url-page-2.com",
|
||||
"workType": "ASSIGNMENT"
|
||||
}
|
||||
],
|
||||
"nextPageToken": "page-3-token"
|
||||
})"))));
|
||||
|
||||
EXPECT_CALL(request_handler(),
|
||||
HandleRequest(Field(&HttpRequest::relative_url,
|
||||
AllOf(HasSubstr("/courseWork?"),
|
||||
HasSubstr("pageToken=page-3-token")))))
|
||||
.WillOnce(Return(ByMove(TestRequestHandler::CreateSuccessfulResponse(R"(
|
||||
{
|
||||
"courseWork": [
|
||||
{
|
||||
"id": "id-page-3",
|
||||
"title": "title-page-3",
|
||||
"alternateLink": "http://url-page-3.com",
|
||||
"workType": "ASSIGNMENT"
|
||||
}
|
||||
]
|
||||
})"))));
|
||||
|
||||
std::vector<mojom::AssignmentPtr> response;
|
||||
base::MockCallback<ListAssignmentsCallback> callback;
|
||||
EXPECT_CALL(callback, Run(testing::_))
|
||||
.Times(1)
|
||||
.WillOnce(
|
||||
testing::Invoke([&](std::vector<mojom::AssignmentPtr> assignments) {
|
||||
response = std::move(assignments);
|
||||
}));
|
||||
|
||||
base::RunLoop run_loop;
|
||||
classroom_handler()->ListAssignments(
|
||||
course_response.at(0)->id,
|
||||
google_apis::test_util::CreateQuitCallback(&run_loop, callback.Get()));
|
||||
run_loop.Run();
|
||||
|
||||
ASSERT_EQ(response.size(), 3u);
|
||||
EXPECT_EQ(response.at(0)->title, "title-page-1");
|
||||
EXPECT_EQ(response.at(1)->title, "title-page-2");
|
||||
EXPECT_EQ(response.at(2)->title, "title-page-3");
|
||||
}
|
||||
} // namespace ash::boca
|
||||
|
@ -44,6 +44,24 @@ export declare interface Course {
|
||||
section: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Declare a classroom course assignment information
|
||||
*/
|
||||
export declare interface Assignment {
|
||||
title: string;
|
||||
url: string;
|
||||
lastUpdateTime: Date;
|
||||
materials: Material[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Declare an assignment material information
|
||||
*/
|
||||
export declare interface Material {
|
||||
title: string;
|
||||
type: MaterialType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Declare navigation enum type
|
||||
*/
|
||||
@ -96,6 +114,17 @@ export enum BocaValidPref {
|
||||
CAPTION_ENABLEMENT_SETTING = 1,
|
||||
}
|
||||
|
||||
/**
|
||||
* Declare course assignment material type enum type
|
||||
*/
|
||||
export enum MaterialType {
|
||||
UNKNOWN = 0,
|
||||
SHARED_DRIVE_FILE = 1,
|
||||
YOUTUBE_VIDEO = 2,
|
||||
LINK = 3,
|
||||
FORM = 4,
|
||||
}
|
||||
|
||||
/**
|
||||
* Declare controlled tab
|
||||
*/
|
||||
@ -201,6 +230,11 @@ export declare interface ClientApiDelegate {
|
||||
*/
|
||||
getStudentList(courseId: string): Promise<Identity[]>;
|
||||
|
||||
/**
|
||||
* Get list of assignments in a course.
|
||||
*/
|
||||
getAssignmentList(courseId: string): Promise<Assignment[]>;
|
||||
|
||||
/**
|
||||
* Create a new session.
|
||||
*/
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
import type {Value} from '//resources/mojo/mojo/public/mojom/base/values.mojom-webui.js'
|
||||
|
||||
import {Config, ControlledTab as ControlledTabMojom, Course, IdentifiedActivity as Activity, Identity as IdentityMojom, NetworkInfo as NetworkInfoMojom, PageHandlerRemote, TabInfo, Window} from '../mojom/boca.mojom-webui.js';
|
||||
import {Assignment as AssignmentMojom, Config, ControlledTab as ControlledTabMojom, Course, IdentifiedActivity as Activity, Identity as IdentityMojom, Material as MaterialMojom, NetworkInfo as NetworkInfoMojom, PageHandlerRemote, TabInfo, Window} from '../mojom/boca.mojom-webui.js';
|
||||
|
||||
import {BocaValidPref, CaptionConfig, ClientApiDelegate, ControlledTab, IdentifiedActivity, Identity, NetworkInfo, OnTaskConfig, SessionConfig, SubmitAccessCodeResult} from './boca_app.js';
|
||||
|
||||
@ -139,6 +139,19 @@ export class ClientDelegateFactory {
|
||||
};
|
||||
});
|
||||
},
|
||||
getAssignmentList: async (id: string) => {
|
||||
const result = await pageHandler.listAssignments(id);
|
||||
return result.assignments.map((assignment: AssignmentMojom) => {
|
||||
return {
|
||||
title: assignment.title,
|
||||
url: assignment.url.url,
|
||||
lastUpdateTime: assignment.lastUpdateTime,
|
||||
materials: assignment.materials.map((material: MaterialMojom) => {
|
||||
return {title: material.title, type: material.type.valueOf()};
|
||||
}),
|
||||
};
|
||||
});
|
||||
},
|
||||
createSession: async (sessionConfig: SessionConfig) => {
|
||||
const result = await pageHandler.createSession({
|
||||
sessionDuration: {
|
||||
|
@ -3,7 +3,7 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
import {ClientDelegateFactory, getNetworkInfoMojomToUI, getSessionConfigMojomToUI, getStudentActivityMojomToUI} from 'chrome-untrusted://boca-app/app/client_delegate.js';
|
||||
import type {BocaValidPref, CaptionConfig, Config, Course, Identity, OnTaskConfig, RemoveStudentError, SessionResult, UpdateSessionError, ViewStudentScreenError, Window} from 'chrome-untrusted://boca-app/mojom/boca.mojom-webui.js';
|
||||
import type {Assignment, BocaValidPref, CaptionConfig, Config, Course, Identity, OnTaskConfig, RemoveStudentError, SessionResult, UpdateSessionError, ViewStudentScreenError, Window} from 'chrome-untrusted://boca-app/mojom/boca.mojom-webui.js';
|
||||
import {PageHandlerRemote, SubmitAccessCodeError} from 'chrome-untrusted://boca-app/mojom/boca.mojom-webui.js';
|
||||
import {Value} from 'chrome-untrusted://resources/mojo/mojo/public/mojom/base/values.mojom-webui.js';
|
||||
import type {Url} from 'chrome-untrusted://resources/mojo/url/mojom/url.mojom-webui.js';
|
||||
@ -51,6 +51,32 @@ class MockRemoteHandler extends PageHandlerRemote {
|
||||
],
|
||||
});
|
||||
}
|
||||
override listAssignments(id: string): Promise<{assignments: Assignment[]}> {
|
||||
// Dummy action get around with unused variable check.
|
||||
id;
|
||||
return Promise.resolve({
|
||||
assignments: [
|
||||
{
|
||||
title: 'assignment-title1',
|
||||
url: {url: 'url1'},
|
||||
lastUpdateTime: new Date(1000000),
|
||||
materials: [
|
||||
{title: 'material-title-1', type: 0},
|
||||
{title: 'material-title-2', type: 1},
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'assignment-title2',
|
||||
url: {url: 'url2'},
|
||||
lastUpdateTime: new Date(2000000),
|
||||
materials: [
|
||||
{title: 'material-title-3', type: 2},
|
||||
{title: 'material-title-4', type: 3},
|
||||
]
|
||||
}
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
override createSession(config: Config): Promise<{success: boolean}> {
|
||||
assertDeepEquals(
|
||||
@ -319,6 +345,37 @@ suite('ClientDelegateTest', function() {
|
||||
result);
|
||||
});
|
||||
|
||||
test(
|
||||
'client delegate should properly translate mojom layer data for' +
|
||||
'assignment list',
|
||||
async () => {
|
||||
const result =
|
||||
await clientDelegateImpl.getInstance().getAssignmentList('1');
|
||||
|
||||
assertDeepEquals(
|
||||
[
|
||||
{
|
||||
title: 'assignment-title1',
|
||||
url: 'url1',
|
||||
lastUpdateTime: new Date(1000000),
|
||||
materials: [
|
||||
{title: 'material-title-1', type: 0},
|
||||
{title: 'material-title-2', type: 1},
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'assignment-title2',
|
||||
url: 'url2',
|
||||
lastUpdateTime: new Date(2000000),
|
||||
materials: [
|
||||
{title: 'material-title-3', type: 2},
|
||||
{title: 'material-title-4', type: 3},
|
||||
]
|
||||
}
|
||||
],
|
||||
result);
|
||||
});
|
||||
|
||||
test(
|
||||
'client delegate should translate data for creating session',
|
||||
async () => {
|
||||
|
Reference in New Issue
Block a user