| // 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/ui/ash/assistant/conversation_starters_parser.h" |
| |
| #include <memory> |
| |
| #include "ash/public/cpp/assistant/conversation_starter.h" |
| #include "base/json/json_string_value_serializer.h" |
| #include "base/logging.h" |
| #include "base/strings/string_util.h" |
| #include "base/values.h" |
| |
| namespace { |
| |
| // Keys. |
| constexpr char kActionUrlKey[] = "actionUrl"; |
| constexpr char kConversationStarterKey[] = "conversationStarter"; |
| constexpr char kIconUrlKey[] = "iconUrl"; |
| constexpr char kLabelKey[] = "label"; |
| constexpr char kRequiredPermissionKey[] = "requiredPermission"; |
| |
| // Permissions. |
| constexpr char kRelatedInfoPermission[] = "RELATED_INFO"; |
| |
| // Helpers --------------------------------------------------------------------- |
| |
| std::unique_ptr<base::Value> Deserialize(const std::string& safe_json_response, |
| std::string* error_message) { |
| constexpr char kPrefix[] = ")]}'"; |
| if (!base::StartsWith(safe_json_response, kPrefix, |
| base::CompareCase::SENSITIVE)) { |
| return nullptr; |
| } |
| int error_code = 0; |
| return JSONStringValueDeserializer( |
| base::BasicStringPiece<std::string>( |
| safe_json_response.begin() + base::size(kPrefix), |
| safe_json_response.end())) |
| .Deserialize(&error_code, error_message); |
| } |
| |
| base::Optional<std::string> FindLabel(const base::Value& conversation_starter) { |
| const std::string* label = conversation_starter.FindStringKey(kLabelKey); |
| return label ? base::Optional<std::string>(*label) : base::nullopt; |
| } |
| |
| base::Optional<GURL> FindActionUrl(const base::Value& conversation_starter) { |
| const std::string* action_url = |
| conversation_starter.FindStringKey(kActionUrlKey); |
| return action_url ? base::Optional<GURL>(*action_url) : base::nullopt; |
| } |
| |
| base::Optional<GURL> FindIconUrl(const base::Value& conversation_starter) { |
| const std::string* icon_url = conversation_starter.FindStringKey(kIconUrlKey); |
| return icon_url ? base::Optional<GURL>(*icon_url) : base::nullopt; |
| } |
| |
| uint32_t FindRequiredPermissions(const base::Value& conversation_starter) { |
| using Permission = ash::ConversationStarter::Permission; |
| |
| uint32_t required_permissions = 0u; |
| |
| const base::Value* required_permissions_list = |
| conversation_starter.FindListKey(kRequiredPermissionKey); |
| if (!required_permissions_list) |
| return required_permissions; |
| |
| for (auto& required_permission : required_permissions_list->GetList()) { |
| if (required_permission.GetString() == kRelatedInfoPermission) { |
| required_permissions |= Permission::kRelatedInfo; |
| continue; |
| } |
| required_permissions |= Permission::kUnknown; |
| } |
| |
| return required_permissions; |
| } |
| |
| } // namespace |
| |
| // ConversationStartersParser -------------------------------------------------- |
| |
| // static |
| std::vector<ash::ConversationStarter> ConversationStartersParser::Parse( |
| const std::string& safe_json_response) { |
| std::vector<ash::ConversationStarter> conversation_starters; |
| |
| // Deserialize |safe_json_response|. |
| std::string error_message; |
| auto response = Deserialize(safe_json_response, &error_message); |
| if (!response || !response->is_dict() || !error_message.empty()) { |
| LOG(ERROR) << (error_message.empty() ? "Error deserializing response." |
| : error_message); |
| return conversation_starters; |
| } |
| |
| // Parse |response|. |
| base::Value* conversation_starters_list = |
| response->FindListKey(kConversationStarterKey); |
| if (!conversation_starters_list) { |
| LOG(ERROR) << "Error parsing response."; |
| return conversation_starters; |
| } |
| |
| // Parse each |conversation_starter|. |
| for (auto& conversation_starter : conversation_starters_list->GetList()) { |
| // Parse |label|. |
| base::Optional<std::string> label = FindLabel(conversation_starter); |
| if (!label.has_value()) { |
| LOG(ERROR) << "Omitting conversation starter due to missing label."; |
| continue; |
| } |
| |
| // Parse |action_url|, |icon_url|, and |required_permissions|. |
| base::Optional<GURL> action_url = FindActionUrl(conversation_starter); |
| base::Optional<GURL> icon_url = FindIconUrl(conversation_starter); |
| uint32_t required_permissions = |
| FindRequiredPermissions(conversation_starter); |
| |
| // Add well formed conversation starters to our collection. |
| conversation_starters.emplace_back(label.value(), action_url, icon_url, |
| required_permissions); |
| } |
| |
| // Return our |conversation_starters| collection. |
| // Note that we have already confirmed that all elements are well formed. |
| return conversation_starters; |
| } |