0

boca: Add access code to be part of the session payload.

This allows display access code in teacher UI so that student can use
the code to join a class session.

UX mock: http://shortn/_BpugcftImn

BocaAppBrowserProducerTest.*

Test: unit tested. BocaAppPageHandlerTest.GetSessionWithFullInputTest
Bug: b:374372602
Change-Id: Ia61582b49364e69c36b6d0949819f424f60709c4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5938901
Reviewed-by: Benjamin Zielinski <bzielinski@google.com>
Commit-Queue: April Zhou <aprilzhou@google.com>
Reviewed-by: Alex Gough <ajgo@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1372980}
This commit is contained in:
April Zhou
2024-10-23 22:08:22 +00:00
committed by Chromium LUCI CQ
parent f62c540a9f
commit ed016cd505
11 changed files with 76 additions and 8 deletions

@ -118,12 +118,18 @@ mojom::ConfigPtr SessionConfigProtoToMojom(::boca::Session* session) {
seconds +
static_cast<double>(nanos) / base::Time::kNanosecondsPerSecond);
}
std::string access_code;
if (session->has_join_code()) {
access_code = session->join_code().code();
}
return mojom::Config::New(
// Nanos are not used throughout session lifecycle so it's
// safe to only parse seconds.
base::Seconds(session->duration().seconds()), start_time,
std::move(teacher), std::move(students), std::move(on_task_config),
std::move(caption_config));
std::move(caption_config), access_code);
}
} // namespace
@ -411,6 +417,11 @@ void BocaAppHandler::OnConsumerActivityUpdated(
OnStudentActivityUpdated(std::move(result));
}
void BocaAppHandler::OnSessionStarted(const std::string& session_id,
const ::boca::UserIdentity& producer) {
UpdateSessionConfig();
}
void BocaAppHandler::OnSessionEnded(const std::string& session_id) {
OnSessionConfigUpdated(
mojom::SessionResult::NewError(mojom::GetSessionError::kEmpty));

@ -76,10 +76,9 @@ class BocaAppHandler : public mojom::PageHandler,
// BocaSessionManager::Observer
void OnConsumerActivityUpdated(
const std::map<std::string, ::boca::StudentStatus>& activities) override;
// When session start, UI is already most update to date, do
// not handle the event
void OnSessionStarted(const std::string& session_id,
const ::boca::UserIdentity& producer) override {}
const ::boca::UserIdentity& producer) override;
void OnSessionEnded(const std::string& session_id) override;
void OnBundleUpdated(const ::boca::Bundle& bundle) override;
void OnSessionCaptionConfigUpdated(

@ -257,7 +257,7 @@ TEST_F(BocaAppPageHandlerTest, CreateSessionWithFullInput) {
const auto config = mojom::Config::New(
session_duration, std::nullopt, nullptr, std::move(students),
GetCommonTestLockOnTaskConfig(), GetCommonCaptionConfig());
GetCommonTestLockOnTaskConfig(), GetCommonCaptionConfig(), "");
// Page handler callback.
base::test::TestFuture<base::expected<std::unique_ptr<::boca::Session>,
google_apis::ApiErrorCode>>
@ -391,7 +391,7 @@ TEST_F(BocaAppPageHandlerTest, CreateSessionWithCritialInputOnly) {
const auto config = mojom::Config::New(
session_duration, std::nullopt, nullptr,
std::vector<mojom::IdentityPtr>{}, mojom::OnTaskConfigPtr(nullptr),
mojom::CaptionConfigPtr(nullptr));
mojom::CaptionConfigPtr(nullptr), "");
::boca::UserIdentity teacher;
teacher.set_gaia_id(kGaiaId);
@ -450,6 +450,9 @@ TEST_F(BocaAppPageHandlerTest, GetSessionWithFullInputTest) {
teacher->set_gaia_id("000");
teacher->set_photo_url("cdn://s");
auto* access_code = session->mutable_join_code();
access_code->set_code("testCode");
auto* student_groups_1 =
session->mutable_roster()->mutable_student_groups()->Add();
student_groups_1->set_title(kMainStudentGroupName);
@ -495,6 +498,7 @@ TEST_F(BocaAppPageHandlerTest, GetSessionWithFullInputTest) {
EXPECT_EQ("000", result->teacher->id);
EXPECT_EQ("cdn://s", result->teacher->photo_url->spec());
EXPECT_EQ("testCode", result->access_code);
EXPECT_EQ(true, result->caption_config->session_caption_enabled);
EXPECT_EQ(true, result->caption_config->session_translation_enabled);
@ -1331,6 +1335,18 @@ TEST_F(BocaAppPageHandlerTest, RemoveStudentWithNonActiveSession) {
EXPECT_EQ(mojom::RemoveStudentError::kInvalid, future_1.Get().value());
}
TEST_F(BocaAppPageHandlerTest, OnSessionSessionStartedSucceed) {
auto session = GetCommonActiveSessionProto();
EXPECT_CALL(*session_manager(), GetCurrentSession())
.WillOnce(Return(&session));
base::test::TestFuture<mojom::SessionResultPtr> future;
boca_app_handler()->SetSessionConfigInterceptorCallbackForTesting(
future.GetCallback());
boca_app_handler()->OnSessionStarted(std::string(), ::boca::UserIdentity());
auto result = future.Take();
ASSERT_TRUE(result->is_config());
}
TEST_F(BocaAppPageHandlerTest, OnSessionEndedSucceed) {
base::test::TestFuture<mojom::SessionResultPtr> future;
boca_app_handler()->SetSessionConfigInterceptorCallbackForTesting(

@ -60,6 +60,9 @@ struct Config{
array<Identity> students;
OnTaskConfig on_task_config;
CaptionConfig caption_config;
// Strings allow consumer to type and join a session. Will be alphabetic
// characters.
string? access_code;
};
struct CaptionConfig {

@ -95,6 +95,7 @@ export declare interface SessionConfig {
teacher?: Identity;
onTaskConfig: OnTaskConfig;
captionConfig: CaptionConfig;
accessCode?: string;
}
/**

@ -66,6 +66,7 @@ export function getSessionConfigMojomToUI(session: Config|
}),
},
captionConfig: session.captionConfig,
accessCode: session.accessCode ? session.accessCode : ''
}
};

@ -111,6 +111,7 @@ class MockRemoteHandler extends PageHandlerRemote {
email: 'teacher@gmail.com',
photoUrl: {url: 'cdn0'},
},
accessCode: 'testCode',
students: [
{
id: '1',
@ -329,6 +330,7 @@ suite('ClientDelegateTest', function() {
{id: '1', name: 'cat', email: 'cat@gmail.com', photoUrl: 'cdn1'},
{id: '2', name: 'dog', email: 'dog@gmail.com', photoUrl: 'cdn2'},
],
accessCode: 'testCode',
onTaskConfig: {
isLocked: true,
tabs: [
@ -379,6 +381,7 @@ suite('ClientDelegateTest', function() {
email: 'teacher@gmail.com',
photoUrl: {url: 'cdn0'},
},
accessCode: null,
captionConfig: {
sessionCaptionEnabled: true,
localCaptionEnabled: true,
@ -401,6 +404,7 @@ suite('ClientDelegateTest', function() {
isLocked: false,
tabs: [],
},
accessCode: '',
captionConfig: {
sessionCaptionEnabled: true,
localCaptionEnabled: true,

@ -74,6 +74,9 @@ inline constexpr char kDeviceId[] = "deviceId";
inline constexpr char kActivity[] = "activity";
inline constexpr char kUsers[] = "users";
inline constexpr char kTachyonGroupId[] = "tachyonGroupId";
inline constexpr char kJoinCode[] = "joinCode";
inline constexpr char kJoinCodeEnabled[] = "enabled";
inline constexpr char kCode[] = "code";
inline constexpr net::NetworkTrafficAnnotationTag kTrafficAnnotation =
net::DefineNetworkTrafficAnnotation("boca_classroom_integration", R"(

@ -89,6 +89,20 @@ void ParseTeacherProtoFromJson(base::Value::Dict* session_dict,
}
}
void ParseJoinCodeProtoFromJson(base::Value::Dict* session_dict,
::boca::Session* session) {
if (!session_dict->FindDict(kJoinCode)) {
return;
}
auto* join_code = session->mutable_join_code();
join_code->set_enabled(session_dict->FindDict(kJoinCode)
->FindBool(kJoinCodeEnabled)
.value_or(false));
if (auto* ptr = session_dict->FindDict(kJoinCode)->FindString(kCode)) {
join_code->set_code(*ptr);
}
}
void ParseRosterProtoFromJson(base::Value::Dict* session_dict,
::boca::Session* session) {
auto* roster_dict = session_dict->FindDict(kRoster);
@ -300,6 +314,8 @@ std::unique_ptr<::boca::Session> GetSessionProtoFromJson(std::string json,
ParseTeacherProtoFromJson(session_dict, session.get());
ParseJoinCodeProtoFromJson(session_dict, session.get());
ParseRosterProtoFromJson(session_dict, session.get());
ParseSessionConfigProtoFromJson(session_dict, session.get(), is_producer);

@ -24,6 +24,8 @@ namespace ash::boca {
// Proto to Json
void ParseTeacherProtoFromJson(base::Value::Dict* session_dict,
::boca::Session* session);
void ParseJoinCodeProtoFromJson(base::Value::Dict* session_dict,
::boca::Session* session);
void ParseRosterProtoFromJson(base::Value::Dict* session_dict,
::boca::Session* session);
void ParseSessionConfigProtoFromJson(base::Value::Dict* session_dict,

@ -14,8 +14,9 @@
namespace ash::boca {
namespace {
// Unit test cases for proto2json conversion is covered in
// create_session_request_unittest.cc, not duplicating here.
// TODO(crbug.com/374364083):Refactor existing get session unit test.
// Currently this file doesn't have full coverage for session_parser, the reset
// is covered in get_session_request_unittest, we should move those here.
constexpr char kFullSessionResponse[] = R"(
{
"startTime":{
@ -25,6 +26,10 @@ constexpr char kFullSessionResponse[] = R"(
"duration": {
"seconds": 120
},
"joinCode":{
"enabled":true,
"code":"testCode"
},
"studentStatuses": {
"2": {
"state": "ADDED"
@ -161,6 +166,13 @@ TEST_F(SessionParserTest, TestParseTeacherProtoFromJson) {
EXPECT_EQ("1", session_partial->teacher().gaia_id());
}
TEST_F(SessionParserTest, TestParseJoinCodeProtoFromJson) {
ParseJoinCodeProtoFromJson(session_dict_full->GetIfDict(),
session_full.get());
EXPECT_TRUE(session_full->join_code().enabled());
EXPECT_EQ("testCode", session_full->join_code().code());
}
TEST_F(SessionParserTest, TestParseRosterProtoFromJson) {
ParseRosterProtoFromJson(session_dict_full->GetIfDict(), session_full.get());
ASSERT_EQ(2, session_full->roster().student_groups()[0].students().size());