OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "components/sync/test/fake_server/fake_server.h" | 5 #include "components/sync/test/fake_server/fake_server.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <limits> | 8 #include <limits> |
9 #include <set> | 9 #include <set> |
10 #include <utility> | 10 #include <utility> |
11 | 11 |
12 #include "base/guid.h" | 12 #include "base/guid.h" |
13 #include "base/logging.h" | 13 #include "base/logging.h" |
14 #include "base/memory/ptr_util.h" | 14 #include "base/memory/ptr_util.h" |
15 #include "base/stl_util.h" | 15 #include "base/stl_util.h" |
16 #include "base/strings/string_number_conversions.h" | 16 #include "base/strings/string_number_conversions.h" |
17 #include "base/strings/string_util.h" | 17 #include "base/strings/string_util.h" |
18 #include "base/strings/stringprintf.h" | 18 #include "base/strings/stringprintf.h" |
19 #include "base/synchronization/lock.h" | 19 #include "base/synchronization/lock.h" |
20 #include "components/sync/test/fake_server/bookmark_entity.h" | 20 #include "base/threading/thread_restrictions.h" |
21 #include "components/sync/test/fake_server/permanent_entity.h" | 21 #include "components/sync/engine_impl/net/server_connection_manager.h" |
22 #include "components/sync/test/fake_server/tombstone_entity.h" | |
23 #include "components/sync/test/fake_server/unique_client_entity.h" | |
24 #include "net/base/net_errors.h" | 22 #include "net/base/net_errors.h" |
25 #include "net/http/http_status_code.h" | 23 #include "net/http/http_status_code.h" |
26 | 24 |
27 using std::string; | 25 using std::string; |
28 using std::vector; | 26 using std::vector; |
29 using syncer::GetModelType; | 27 using syncer::GetModelType; |
30 using syncer::GetModelTypeFromSpecifics; | 28 using syncer::GetModelTypeFromSpecifics; |
| 29 using syncer::LoopbackServerEntity; |
31 using syncer::ModelType; | 30 using syncer::ModelType; |
32 using syncer::ModelTypeSet; | 31 using syncer::ModelTypeSet; |
33 | 32 |
34 namespace fake_server { | 33 namespace fake_server { |
35 | 34 |
36 class FakeServerEntity; | |
37 | |
38 namespace { | |
39 | |
40 // The default keystore key. | |
41 static const char kDefaultKeystoreKey[] = "1111111111111111"; | |
42 | |
43 // Properties of the bookmark bar permanent folder. | |
44 static const char kBookmarkBarFolderServerTag[] = "bookmark_bar"; | |
45 static const char kBookmarkBarFolderName[] = "Bookmark Bar"; | |
46 | |
47 // Properties of the other bookmarks permanent folder. | |
48 static const char kOtherBookmarksFolderServerTag[] = "other_bookmarks"; | |
49 static const char kOtherBookmarksFolderName[] = "Other Bookmarks"; | |
50 | |
51 // Properties of the synced bookmarks permanent folder. | |
52 static const char kSyncedBookmarksFolderServerTag[] = "synced_bookmarks"; | |
53 static const char kSyncedBookmarksFolderName[] = "Synced Bookmarks"; | |
54 | |
55 // A filter used during GetUpdates calls to determine what information to | |
56 // send back to the client; filtering out old entities and tracking versions to | |
57 // use in response progress markers. Note that only the GetUpdatesMessage's | |
58 // from_progress_marker is used to determine this; legacy fields are ignored. | |
59 class UpdateSieve { | |
60 public: | |
61 explicit UpdateSieve(const sync_pb::GetUpdatesMessage& message) | |
62 : UpdateSieve(MessageToVersionMap(message)) {} | |
63 ~UpdateSieve() {} | |
64 | |
65 // Sets the progress markers in |get_updates_response| based on the highest | |
66 // version between request progress markers and response entities. | |
67 void SetProgressMarkers( | |
68 sync_pb::GetUpdatesResponse* get_updates_response) const { | |
69 for (const auto& kv : response_version_map_) { | |
70 sync_pb::DataTypeProgressMarker* new_marker = | |
71 get_updates_response->add_new_progress_marker(); | |
72 new_marker->set_data_type_id( | |
73 GetSpecificsFieldNumberFromModelType(kv.first)); | |
74 new_marker->set_token(base::Int64ToString(kv.second)); | |
75 } | |
76 } | |
77 | |
78 // Determines whether the server should send an |entity| to the client as | |
79 // part of a GetUpdatesResponse. Update internal tracking of max versions as a | |
80 // side effect which will later be used to set response progress markers. | |
81 bool ClientWantsItem(const FakeServerEntity& entity) { | |
82 int64_t version = entity.GetVersion(); | |
83 ModelType type = entity.model_type(); | |
84 response_version_map_[type] = | |
85 std::max(response_version_map_[type], version); | |
86 auto it = request_version_map_.find(type); | |
87 return it == request_version_map_.end() ? false : it->second < version; | |
88 } | |
89 | |
90 private: | |
91 using ModelTypeToVersionMap = std::map<ModelType, int64_t>; | |
92 | |
93 static UpdateSieve::ModelTypeToVersionMap MessageToVersionMap( | |
94 const sync_pb::GetUpdatesMessage& get_updates_message) { | |
95 CHECK_GT(get_updates_message.from_progress_marker_size(), 0) | |
96 << "A GetUpdates request must have at least one progress marker."; | |
97 ModelTypeToVersionMap request_version_map; | |
98 | |
99 for (int i = 0; i < get_updates_message.from_progress_marker_size(); i++) { | |
100 sync_pb::DataTypeProgressMarker marker = | |
101 get_updates_message.from_progress_marker(i); | |
102 | |
103 int64_t version = 0; | |
104 // Let the version remain zero if there is no token or an empty token (the | |
105 // first request for this type). | |
106 if (marker.has_token() && !marker.token().empty()) { | |
107 bool parsed = base::StringToInt64(marker.token(), &version); | |
108 CHECK(parsed) << "Unable to parse progress marker token."; | |
109 } | |
110 | |
111 ModelType model_type = | |
112 syncer::GetModelTypeFromSpecificsFieldNumber(marker.data_type_id()); | |
113 DCHECK(request_version_map.find(model_type) == request_version_map.end()); | |
114 request_version_map[model_type] = version; | |
115 } | |
116 return request_version_map; | |
117 } | |
118 | |
119 explicit UpdateSieve(const ModelTypeToVersionMap request_version_map) | |
120 : request_version_map_(request_version_map), | |
121 response_version_map_(request_version_map) {} | |
122 | |
123 // The largest versions the client has seen before this request, and is used | |
124 // to filter entities to send back to clients. The values in this map are not | |
125 // updated after being initially set. The presence of a type in this map is a | |
126 // proxy for the desire to receive results about this type. | |
127 const ModelTypeToVersionMap request_version_map_; | |
128 | |
129 // The largest versions seen between client and server, ultimately used to | |
130 // send progress markers back to the client. | |
131 ModelTypeToVersionMap response_version_map_; | |
132 }; | |
133 | |
134 // Returns whether |entity| is deleted or permanent. | |
135 bool IsDeletedOrPermanent(const FakeServerEntity& entity) { | |
136 return entity.IsDeleted() || entity.IsPermanent(); | |
137 } | |
138 | |
139 } // namespace | |
140 | |
141 FakeServer::FakeServer() | 35 FakeServer::FakeServer() |
142 : version_(0), | 36 : authenticated_(true), |
143 store_birthday_(0), | |
144 authenticated_(true), | |
145 error_type_(sync_pb::SyncEnums::SUCCESS), | 37 error_type_(sync_pb::SyncEnums::SUCCESS), |
146 alternate_triggered_errors_(false), | 38 alternate_triggered_errors_(false), |
147 request_counter_(0), | 39 request_counter_(0), |
148 network_enabled_(true), | 40 network_enabled_(true), |
149 weak_ptr_factory_(this) { | 41 weak_ptr_factory_(this) { |
150 Init(); | 42 loopback_server_storage_ = base::MakeUnique<base::ScopedTempDir>(); |
| 43 DCHECK(loopback_server_storage_->CreateUniqueTempDir()); |
| 44 base::ThreadRestrictions::SetIOAllowed(true); |
| 45 loopback_server_ = base::MakeUnique<syncer::LoopbackServer>( |
| 46 loopback_server_storage_->GetPath().AppendASCII("profile.pb")); |
| 47 loopback_server_->set_observer_for_tests(this); |
151 } | 48 } |
152 | 49 |
153 FakeServer::~FakeServer() {} | 50 FakeServer::~FakeServer() {} |
154 | 51 |
155 void FakeServer::Init() { | |
156 keystore_keys_.push_back(kDefaultKeystoreKey); | |
157 | |
158 const bool create_result = CreateDefaultPermanentItems(); | |
159 DCHECK(create_result) << "Permanent items were not created successfully."; | |
160 } | |
161 | |
162 bool FakeServer::CreatePermanentBookmarkFolder(const std::string& server_tag, | |
163 const std::string& name) { | |
164 DCHECK(thread_checker_.CalledOnValidThread()); | |
165 std::unique_ptr<FakeServerEntity> entity = | |
166 PermanentEntity::Create(syncer::BOOKMARKS, server_tag, name, | |
167 ModelTypeToRootTag(syncer::BOOKMARKS)); | |
168 if (!entity) | |
169 return false; | |
170 | |
171 SaveEntity(std::move(entity)); | |
172 return true; | |
173 } | |
174 | |
175 bool FakeServer::CreateDefaultPermanentItems() { | |
176 // Permanent folders are always required for Bookmarks (hierarchical | |
177 // structure) and Nigori (data stored in permanent root folder). | |
178 ModelTypeSet permanent_folder_types = | |
179 ModelTypeSet(syncer::BOOKMARKS, syncer::NIGORI); | |
180 | |
181 for (ModelTypeSet::Iterator it = permanent_folder_types.First(); it.Good(); | |
182 it.Inc()) { | |
183 ModelType model_type = it.Get(); | |
184 | |
185 std::unique_ptr<FakeServerEntity> top_level_entity = | |
186 PermanentEntity::CreateTopLevel(model_type); | |
187 if (!top_level_entity) { | |
188 return false; | |
189 } | |
190 SaveEntity(std::move(top_level_entity)); | |
191 | |
192 if (model_type == syncer::BOOKMARKS) { | |
193 if (!CreatePermanentBookmarkFolder(kBookmarkBarFolderServerTag, | |
194 kBookmarkBarFolderName)) | |
195 return false; | |
196 if (!CreatePermanentBookmarkFolder(kOtherBookmarksFolderServerTag, | |
197 kOtherBookmarksFolderName)) | |
198 return false; | |
199 } | |
200 } | |
201 | |
202 return true; | |
203 } | |
204 | |
205 void FakeServer::UpdateEntityVersion(FakeServerEntity* entity) { | |
206 entity->SetVersion(++version_); | |
207 } | |
208 | |
209 void FakeServer::SaveEntity(std::unique_ptr<FakeServerEntity> entity) { | |
210 UpdateEntityVersion(entity.get()); | |
211 entities_[entity->id()] = std::move(entity); | |
212 } | |
213 | |
214 void FakeServer::HandleCommand(const string& request, | 52 void FakeServer::HandleCommand(const string& request, |
215 const base::Closure& completion_closure, | 53 const base::Closure& completion_closure, |
216 int* error_code, | 54 int* error_code, |
217 int* response_code, | 55 int* response_code, |
218 std::string* response) { | 56 std::string* response) { |
219 DCHECK(thread_checker_.CalledOnValidThread()); | 57 DCHECK(thread_checker_.CalledOnValidThread()); |
| 58 |
220 if (!network_enabled_) { | 59 if (!network_enabled_) { |
221 *error_code = net::ERR_FAILED; | 60 *error_code = net::ERR_FAILED; |
222 *response_code = net::ERR_FAILED; | 61 *response_code = net::ERR_FAILED; |
223 *response = string(); | 62 *response = string(); |
224 completion_closure.Run(); | 63 completion_closure.Run(); |
225 return; | 64 return; |
226 } | 65 } |
227 request_counter_++; | 66 request_counter_++; |
228 | 67 |
229 if (!authenticated_) { | 68 if (!authenticated_) { |
230 *error_code = 0; | 69 *error_code = 0; |
231 *response_code = net::HTTP_UNAUTHORIZED; | 70 *response_code = net::HTTP_UNAUTHORIZED; |
232 *response = string(); | 71 *response = string(); |
233 completion_closure.Run(); | 72 completion_closure.Run(); |
234 return; | 73 return; |
235 } | 74 } |
236 | 75 |
237 sync_pb::ClientToServerMessage message; | |
238 bool parsed = message.ParseFromString(request); | |
239 CHECK(parsed) << "Unable to parse the ClientToServerMessage."; | |
240 | |
241 sync_pb::ClientToServerResponse response_proto; | 76 sync_pb::ClientToServerResponse response_proto; |
242 | 77 *response_code = 200; |
243 if (message.has_store_birthday() && | 78 *error_code = 0; |
244 message.store_birthday() != GetStoreBirthday()) { | 79 if (error_type_ != sync_pb::SyncEnums::SUCCESS && |
245 response_proto.set_error_code(sync_pb::SyncEnums::NOT_MY_BIRTHDAY); | 80 ShouldSendTriggeredError()) { |
246 } else if (error_type_ != sync_pb::SyncEnums::SUCCESS && | |
247 ShouldSendTriggeredError()) { | |
248 response_proto.set_error_code(error_type_); | 81 response_proto.set_error_code(error_type_); |
249 } else if (triggered_actionable_error_.get() && ShouldSendTriggeredError()) { | 82 } else if (triggered_actionable_error_.get() && ShouldSendTriggeredError()) { |
250 sync_pb::ClientToServerResponse_Error* error = | 83 sync_pb::ClientToServerResponse_Error* error = |
251 response_proto.mutable_error(); | 84 response_proto.mutable_error(); |
252 error->CopyFrom(*(triggered_actionable_error_.get())); | 85 error->CopyFrom(*(triggered_actionable_error_.get())); |
253 } else { | 86 } else { |
254 bool success = false; | 87 sync_pb::ClientToServerMessage message; |
| 88 bool parsed = message.ParseFromString(request); |
| 89 CHECK(parsed) << "Unable to parse the ClientToServerMessage."; |
255 switch (message.message_contents()) { | 90 switch (message.message_contents()) { |
256 case sync_pb::ClientToServerMessage::GET_UPDATES: | 91 case sync_pb::ClientToServerMessage::GET_UPDATES: |
257 last_getupdates_message_ = message; | 92 last_getupdates_message_ = message; |
258 success = HandleGetUpdatesRequest(message.get_updates(), | |
259 response_proto.mutable_get_updates()); | |
260 break; | 93 break; |
261 case sync_pb::ClientToServerMessage::COMMIT: | 94 case sync_pb::ClientToServerMessage::COMMIT: |
262 last_commit_message_ = message; | 95 last_commit_message_ = message; |
263 success = HandleCommitRequest(message.commit(), | |
264 message.invalidator_client_id(), | |
265 response_proto.mutable_commit()); | |
266 break; | |
267 case sync_pb::ClientToServerMessage::CLEAR_SERVER_DATA: | |
268 ClearServerData(); | |
269 response_proto.mutable_clear_server_data(); | |
270 success = true; | |
271 break; | 96 break; |
272 default: | 97 default: |
273 *error_code = net::ERR_NOT_IMPLEMENTED; | 98 break; |
274 *response_code = 0; | 99 // Don't care. |
275 *response = string(); | |
276 completion_closure.Run(); | |
277 return; | |
278 } | 100 } |
279 | 101 |
280 if (!success) { | 102 int64_t response_code_large; |
281 // TODO(pvalenzuela): Add logging here so that tests have more info about | 103 syncer::HttpResponse::ServerConnectionCode server_status; |
282 // the failure. | 104 base::ThreadRestrictions::SetIOAllowed(true); |
283 *error_code = net::ERR_FAILED; | 105 loopback_server_->HandleCommand(request, &server_status, |
284 *response_code = 0; | 106 &response_code_large, response); |
285 *response = string(); | 107 *response_code = static_cast<int>(response_code_large); |
286 completion_closure.Run(); | 108 completion_closure.Run(); |
287 return; | 109 return; |
288 } | |
289 | |
290 response_proto.set_error_code(sync_pb::SyncEnums::SUCCESS); | |
291 } | 110 } |
292 | 111 |
293 response_proto.set_store_birthday(GetStoreBirthday()); | 112 response_proto.set_store_birthday(loopback_server_->GetStoreBirthday()); |
294 | |
295 *error_code = 0; | |
296 *response_code = net::HTTP_OK; | |
297 *response = response_proto.SerializeAsString(); | 113 *response = response_proto.SerializeAsString(); |
298 completion_closure.Run(); | 114 completion_closure.Run(); |
299 } | 115 } |
300 | 116 |
301 bool FakeServer::GetLastCommitMessage(sync_pb::ClientToServerMessage* message) { | 117 bool FakeServer::GetLastCommitMessage(sync_pb::ClientToServerMessage* message) { |
302 if (!last_commit_message_.has_commit()) | 118 if (!last_commit_message_.has_commit()) |
303 return false; | 119 return false; |
304 | 120 |
305 message->CopyFrom(last_commit_message_); | 121 message->CopyFrom(last_commit_message_); |
306 return true; | 122 return true; |
307 } | 123 } |
308 | 124 |
309 bool FakeServer::GetLastGetUpdatesMessage( | 125 bool FakeServer::GetLastGetUpdatesMessage( |
310 sync_pb::ClientToServerMessage* message) { | 126 sync_pb::ClientToServerMessage* message) { |
311 if (!last_getupdates_message_.has_get_updates()) | 127 if (!last_getupdates_message_.has_get_updates()) |
312 return false; | 128 return false; |
313 | 129 |
314 message->CopyFrom(last_getupdates_message_); | 130 message->CopyFrom(last_getupdates_message_); |
315 return true; | 131 return true; |
316 } | 132 } |
317 | 133 |
318 bool FakeServer::HandleGetUpdatesRequest( | |
319 const sync_pb::GetUpdatesMessage& get_updates, | |
320 sync_pb::GetUpdatesResponse* response) { | |
321 // TODO(pvalenzuela): Implement batching instead of sending all information | |
322 // at once. | |
323 response->set_changes_remaining(0); | |
324 | |
325 auto sieve = base::MakeUnique<UpdateSieve>(get_updates); | |
326 | |
327 // This folder is called "Synced Bookmarks" by sync and is renamed | |
328 // "Mobile Bookmarks" by the mobile client UIs. | |
329 if (get_updates.create_mobile_bookmarks_folder() && | |
330 !CreatePermanentBookmarkFolder(kSyncedBookmarksFolderServerTag, | |
331 kSyncedBookmarksFolderName)) { | |
332 return false; | |
333 } | |
334 | |
335 bool send_encryption_keys_based_on_nigori = false; | |
336 for (EntityMap::const_iterator it = entities_.begin(); it != entities_.end(); | |
337 ++it) { | |
338 const FakeServerEntity& entity = *it->second; | |
339 if (sieve->ClientWantsItem(entity)) { | |
340 sync_pb::SyncEntity* response_entity = response->add_entries(); | |
341 entity.SerializeAsProto(response_entity); | |
342 | |
343 if (entity.model_type() == syncer::NIGORI) { | |
344 send_encryption_keys_based_on_nigori = | |
345 response_entity->specifics().nigori().passphrase_type() == | |
346 sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE; | |
347 } | |
348 } | |
349 } | |
350 | |
351 if (send_encryption_keys_based_on_nigori || | |
352 get_updates.need_encryption_key()) { | |
353 for (vector<string>::iterator it = keystore_keys_.begin(); | |
354 it != keystore_keys_.end(); ++it) { | |
355 response->add_encryption_keys(*it); | |
356 } | |
357 } | |
358 | |
359 sieve->SetProgressMarkers(response); | |
360 return true; | |
361 } | |
362 | |
363 string FakeServer::CommitEntity( | |
364 const sync_pb::SyncEntity& client_entity, | |
365 sync_pb::CommitResponse_EntryResponse* entry_response, | |
366 const string& client_guid, | |
367 const string& parent_id) { | |
368 if (client_entity.version() == 0 && client_entity.deleted()) { | |
369 return string(); | |
370 } | |
371 | |
372 std::unique_ptr<FakeServerEntity> entity; | |
373 if (client_entity.deleted()) { | |
374 entity = TombstoneEntity::Create(client_entity.id_string(), | |
375 client_entity.client_defined_unique_tag()); | |
376 DeleteChildren(client_entity.id_string()); | |
377 } else if (GetModelType(client_entity) == syncer::NIGORI) { | |
378 // NIGORI is the only permanent item type that should be updated by the | |
379 // client. | |
380 EntityMap::const_iterator iter = entities_.find(client_entity.id_string()); | |
381 CHECK(iter != entities_.end()); | |
382 entity = PermanentEntity::CreateUpdatedNigoriEntity(client_entity, | |
383 *iter->second); | |
384 } else if (client_entity.has_client_defined_unique_tag()) { | |
385 entity = UniqueClientEntity::Create(client_entity); | |
386 } else { | |
387 // TODO(pvalenzuela): Validate entity's parent ID. | |
388 EntityMap::const_iterator iter = entities_.find(client_entity.id_string()); | |
389 if (iter != entities_.end()) { | |
390 entity = BookmarkEntity::CreateUpdatedVersion(client_entity, | |
391 *iter->second, parent_id); | |
392 } else { | |
393 entity = BookmarkEntity::CreateNew(client_entity, parent_id, client_guid); | |
394 } | |
395 } | |
396 | |
397 if (!entity) { | |
398 // TODO(pvalenzuela): Add logging so that it is easier to determine why | |
399 // creation failed. | |
400 return string(); | |
401 } | |
402 | |
403 const std::string id = entity->id(); | |
404 SaveEntity(std::move(entity)); | |
405 BuildEntryResponseForSuccessfulCommit(id, entry_response); | |
406 return id; | |
407 } | |
408 | |
409 void FakeServer::BuildEntryResponseForSuccessfulCommit( | |
410 const std::string& entity_id, | |
411 sync_pb::CommitResponse_EntryResponse* entry_response) { | |
412 EntityMap::const_iterator iter = entities_.find(entity_id); | |
413 CHECK(iter != entities_.end()); | |
414 const FakeServerEntity& entity = *iter->second; | |
415 entry_response->set_response_type(sync_pb::CommitResponse::SUCCESS); | |
416 entry_response->set_id_string(entity.id()); | |
417 | |
418 if (entity.IsDeleted()) { | |
419 entry_response->set_version(entity.GetVersion() + 1); | |
420 } else { | |
421 entry_response->set_version(entity.GetVersion()); | |
422 entry_response->set_name(entity.GetName()); | |
423 } | |
424 } | |
425 | |
426 bool FakeServer::IsChild(const string& id, const string& potential_parent_id) { | |
427 EntityMap::const_iterator iter = entities_.find(id); | |
428 if (iter == entities_.end()) { | |
429 // We've hit an ID (probably the imaginary root entity) that isn't stored | |
430 // by the server, so it can't be a child. | |
431 return false; | |
432 } | |
433 | |
434 const FakeServerEntity& entity = *iter->second; | |
435 if (entity.GetParentId() == potential_parent_id) | |
436 return true; | |
437 | |
438 // Recursively look up the tree. | |
439 return IsChild(entity.GetParentId(), potential_parent_id); | |
440 } | |
441 | |
442 void FakeServer::DeleteChildren(const string& id) { | |
443 std::vector<std::unique_ptr<FakeServerEntity>> tombstones; | |
444 // Find all the children of id. | |
445 for (const auto& entity : entities_) { | |
446 if (IsChild(entity.first, id)) { | |
447 tombstones.push_back(TombstoneEntity::Create( | |
448 entity.first, entity.second->client_defined_unique_tag())); | |
449 } | |
450 } | |
451 | |
452 for (auto& tombstone : tombstones) { | |
453 SaveEntity(std::move(tombstone)); | |
454 } | |
455 } | |
456 | |
457 bool FakeServer::HandleCommitRequest(const sync_pb::CommitMessage& commit, | |
458 const std::string& invalidator_client_id, | |
459 sync_pb::CommitResponse* response) { | |
460 std::map<string, string> client_to_server_ids; | |
461 string guid = commit.cache_guid(); | |
462 ModelTypeSet committed_model_types; | |
463 | |
464 // TODO(pvalenzuela): Add validation of CommitMessage.entries. | |
465 ::google::protobuf::RepeatedPtrField<sync_pb::SyncEntity>::const_iterator it; | |
466 for (it = commit.entries().begin(); it != commit.entries().end(); ++it) { | |
467 sync_pb::CommitResponse_EntryResponse* entry_response = | |
468 response->add_entryresponse(); | |
469 | |
470 sync_pb::SyncEntity client_entity = *it; | |
471 string parent_id = client_entity.parent_id_string(); | |
472 if (client_to_server_ids.find(parent_id) != client_to_server_ids.end()) { | |
473 parent_id = client_to_server_ids[parent_id]; | |
474 } | |
475 | |
476 const string entity_id = | |
477 CommitEntity(client_entity, entry_response, guid, parent_id); | |
478 if (entity_id.empty()) { | |
479 return false; | |
480 } | |
481 | |
482 // Record the ID if it was renamed. | |
483 if (entity_id != client_entity.id_string()) { | |
484 client_to_server_ids[client_entity.id_string()] = entity_id; | |
485 } | |
486 | |
487 EntityMap::const_iterator iter = entities_.find(entity_id); | |
488 CHECK(iter != entities_.end()); | |
489 committed_model_types.Put(iter->second->model_type()); | |
490 } | |
491 | |
492 for (auto& observer : observers_) | |
493 observer.OnCommit(invalidator_client_id, committed_model_types); | |
494 | |
495 return true; | |
496 } | |
497 | |
498 std::unique_ptr<base::DictionaryValue> | 134 std::unique_ptr<base::DictionaryValue> |
499 FakeServer::GetEntitiesAsDictionaryValue() { | 135 FakeServer::GetEntitiesAsDictionaryValue() { |
500 DCHECK(thread_checker_.CalledOnValidThread()); | 136 DCHECK(thread_checker_.CalledOnValidThread()); |
501 std::unique_ptr<base::DictionaryValue> dictionary( | 137 return loopback_server_->GetEntitiesAsDictionaryValue(); |
502 new base::DictionaryValue()); | |
503 | |
504 // Initialize an empty ListValue for all ModelTypes. | |
505 ModelTypeSet all_types = ModelTypeSet::All(); | |
506 for (ModelTypeSet::Iterator it = all_types.First(); it.Good(); it.Inc()) { | |
507 dictionary->Set(ModelTypeToString(it.Get()), new base::ListValue()); | |
508 } | |
509 | |
510 for (EntityMap::const_iterator it = entities_.begin(); it != entities_.end(); | |
511 ++it) { | |
512 const FakeServerEntity& entity = *it->second; | |
513 if (IsDeletedOrPermanent(entity)) { | |
514 // Tombstones are ignored as they don't represent current data. Folders | |
515 // are also ignored as current verification infrastructure does not | |
516 // consider them. | |
517 continue; | |
518 } | |
519 base::ListValue* list_value; | |
520 if (!dictionary->GetList(ModelTypeToString(entity.model_type()), | |
521 &list_value)) { | |
522 return std::unique_ptr<base::DictionaryValue>(); | |
523 } | |
524 // TODO(pvalenzuela): Store more data for each entity so additional | |
525 // verification can be performed. One example of additional verification | |
526 // is checking the correctness of the bookmark hierarchy. | |
527 list_value->AppendString(entity.GetName()); | |
528 } | |
529 | |
530 return dictionary; | |
531 } | 138 } |
532 | 139 |
533 std::vector<sync_pb::SyncEntity> FakeServer::GetSyncEntitiesByModelType( | 140 std::vector<sync_pb::SyncEntity> FakeServer::GetSyncEntitiesByModelType( |
534 ModelType model_type) { | 141 ModelType model_type) { |
535 std::vector<sync_pb::SyncEntity> sync_entities; | |
536 DCHECK(thread_checker_.CalledOnValidThread()); | 142 DCHECK(thread_checker_.CalledOnValidThread()); |
537 for (EntityMap::const_iterator it = entities_.begin(); it != entities_.end(); | 143 return loopback_server_->GetSyncEntitiesByModelType(model_type); |
538 ++it) { | |
539 const FakeServerEntity& entity = *it->second; | |
540 if (!IsDeletedOrPermanent(entity) && entity.model_type() == model_type) { | |
541 sync_pb::SyncEntity sync_entity; | |
542 entity.SerializeAsProto(&sync_entity); | |
543 sync_entities.push_back(sync_entity); | |
544 } | |
545 } | |
546 return sync_entities; | |
547 } | 144 } |
548 | 145 |
549 void FakeServer::InjectEntity(std::unique_ptr<FakeServerEntity> entity) { | 146 void FakeServer::InjectEntity(std::unique_ptr<LoopbackServerEntity> entity) { |
550 DCHECK(thread_checker_.CalledOnValidThread()); | 147 DCHECK(thread_checker_.CalledOnValidThread()); |
551 SaveEntity(std::move(entity)); | 148 loopback_server_->SaveEntity(std::move(entity)); |
552 } | 149 } |
553 | 150 |
554 bool FakeServer::ModifyEntitySpecifics( | 151 bool FakeServer::ModifyEntitySpecifics( |
555 const std::string& id, | 152 const std::string& id, |
556 const sync_pb::EntitySpecifics& updated_specifics) { | 153 const sync_pb::EntitySpecifics& updated_specifics) { |
557 EntityMap::const_iterator iter = entities_.find(id); | 154 return loopback_server_->ModifyEntitySpecifics(id, updated_specifics); |
558 if (iter == entities_.end() || | |
559 iter->second->model_type() != | |
560 GetModelTypeFromSpecifics(updated_specifics)) { | |
561 return false; | |
562 } | |
563 | |
564 FakeServerEntity* entity = iter->second.get(); | |
565 entity->SetSpecifics(updated_specifics); | |
566 UpdateEntityVersion(entity); | |
567 return true; | |
568 } | |
569 | |
570 bool FakeServer::ModifyBookmarkEntity( | |
571 const std::string& id, | |
572 const std::string& parent_id, | |
573 const sync_pb::EntitySpecifics& updated_specifics) { | |
574 EntityMap::const_iterator iter = entities_.find(id); | |
575 if (iter == entities_.end() || | |
576 iter->second->model_type() != syncer::BOOKMARKS || | |
577 GetModelTypeFromSpecifics(updated_specifics) != syncer::BOOKMARKS) { | |
578 return false; | |
579 } | |
580 | |
581 BookmarkEntity* entity = static_cast<BookmarkEntity*>(iter->second.get()); | |
582 | |
583 entity->SetParentId(parent_id); | |
584 entity->SetSpecifics(updated_specifics); | |
585 if (updated_specifics.has_bookmark()) { | |
586 entity->SetName(updated_specifics.bookmark().title()); | |
587 } | |
588 UpdateEntityVersion(entity); | |
589 return true; | |
590 } | 155 } |
591 | 156 |
592 void FakeServer::ClearServerData() { | 157 void FakeServer::ClearServerData() { |
593 DCHECK(thread_checker_.CalledOnValidThread()); | 158 DCHECK(thread_checker_.CalledOnValidThread()); |
594 entities_.clear(); | 159 loopback_server_->ClearServerData(); |
595 keystore_keys_.clear(); | |
596 ++store_birthday_; | |
597 Init(); | |
598 } | 160 } |
599 | 161 |
600 void FakeServer::SetAuthenticated() { | 162 void FakeServer::SetAuthenticated() { |
601 DCHECK(thread_checker_.CalledOnValidThread()); | 163 DCHECK(thread_checker_.CalledOnValidThread()); |
602 authenticated_ = true; | 164 authenticated_ = true; |
603 } | 165 } |
604 | 166 |
605 void FakeServer::SetUnauthenticated() { | 167 void FakeServer::SetUnauthenticated() { |
606 DCHECK(thread_checker_.CalledOnValidThread()); | 168 DCHECK(thread_checker_.CalledOnValidThread()); |
607 authenticated_ = false; | 169 authenticated_ = false; |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
665 void FakeServer::AddObserver(Observer* observer) { | 227 void FakeServer::AddObserver(Observer* observer) { |
666 DCHECK(thread_checker_.CalledOnValidThread()); | 228 DCHECK(thread_checker_.CalledOnValidThread()); |
667 observers_.AddObserver(observer); | 229 observers_.AddObserver(observer); |
668 } | 230 } |
669 | 231 |
670 void FakeServer::RemoveObserver(Observer* observer) { | 232 void FakeServer::RemoveObserver(Observer* observer) { |
671 DCHECK(thread_checker_.CalledOnValidThread()); | 233 DCHECK(thread_checker_.CalledOnValidThread()); |
672 observers_.RemoveObserver(observer); | 234 observers_.RemoveObserver(observer); |
673 } | 235 } |
674 | 236 |
| 237 void FakeServer::OnCommit(const std::string& committer_id, |
| 238 syncer::ModelTypeSet committed_model_types) { |
| 239 for (auto& observer : observers_) |
| 240 observer.OnCommit(committer_id, committed_model_types); |
| 241 } |
| 242 |
675 void FakeServer::EnableNetwork() { | 243 void FakeServer::EnableNetwork() { |
676 DCHECK(thread_checker_.CalledOnValidThread()); | 244 DCHECK(thread_checker_.CalledOnValidThread()); |
677 network_enabled_ = true; | 245 network_enabled_ = true; |
678 } | 246 } |
679 | 247 |
680 void FakeServer::DisableNetwork() { | 248 void FakeServer::DisableNetwork() { |
681 DCHECK(thread_checker_.CalledOnValidThread()); | 249 DCHECK(thread_checker_.CalledOnValidThread()); |
682 network_enabled_ = false; | 250 network_enabled_ = false; |
683 } | 251 } |
684 | 252 |
685 std::string FakeServer::GetBookmarkBarFolderId() const { | |
686 DCHECK(thread_checker_.CalledOnValidThread()); | |
687 for (EntityMap::const_iterator it = entities_.begin(); it != entities_.end(); | |
688 ++it) { | |
689 FakeServerEntity* entity = it->second.get(); | |
690 if (entity->GetName() == kBookmarkBarFolderName && entity->IsFolder() && | |
691 entity->model_type() == syncer::BOOKMARKS) { | |
692 return entity->id(); | |
693 } | |
694 } | |
695 NOTREACHED() << "Bookmark Bar entity not found."; | |
696 return ""; | |
697 } | |
698 | |
699 base::WeakPtr<FakeServer> FakeServer::AsWeakPtr() { | 253 base::WeakPtr<FakeServer> FakeServer::AsWeakPtr() { |
700 DCHECK(thread_checker_.CalledOnValidThread()); | 254 DCHECK(thread_checker_.CalledOnValidThread()); |
701 return weak_ptr_factory_.GetWeakPtr(); | 255 return weak_ptr_factory_.GetWeakPtr(); |
702 } | 256 } |
703 | 257 |
704 std::string FakeServer::GetStoreBirthday() const { | |
705 DCHECK(thread_checker_.CalledOnValidThread()); | |
706 return base::Int64ToString(store_birthday_); | |
707 } | |
708 | |
709 } // namespace fake_server | 258 } // namespace fake_server |
OLD | NEW |