OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "sync/test/fake_server/fake_server.h" | |
6 | |
7 #include <limits> | |
8 #include <string> | |
9 #include <vector> | |
10 | |
11 #include "base/basictypes.h" | |
12 #include "base/logging.h" | |
13 #include "base/memory/scoped_ptr.h" | |
14 #include "base/strings/string_number_conversions.h" | |
15 #include "sync/internal_api/public/base/model_type.h" | |
16 #include "sync/protocol/sync.pb.h" | |
17 | |
18 using std::string; | |
19 | |
20 // The parent tag for childen of the root node. | |
21 static const std::string kRootParentTag = "0"; | |
22 | |
23 namespace syncer { | |
24 namespace { | |
25 | |
26 // A filter used during GetUpdates calls to determine what information to | |
27 // send back to the client. There is a 1:1 correspondence between any given | |
28 // GetUpdates call and an UpdateSieve instance. | |
29 class UpdateSieve { | |
30 public: | |
31 // Factory method for creating an UpdateSieve. | |
32 static scoped_ptr<UpdateSieve> Create( | |
rlarocque
2014/01/14 01:53:38
nit that I should have caught earlier:
You should
pval...(no longer on Chromium)
2014/01/14 02:14:45
Thanks. Done.
| |
33 const sync_pb::GetUpdatesMessage& get_updates_message) { | |
34 DCHECK_GT(get_updates_message.from_progress_marker_size(), 0); | |
35 | |
36 ModelTypeToVersionMap request_from_version; | |
37 int64 min_version = std::numeric_limits<int64>::max(); | |
38 for (int i = 0; i < get_updates_message.from_progress_marker_size(); i++) { | |
39 sync_pb::DataTypeProgressMarker marker = | |
40 get_updates_message.from_progress_marker(i); | |
41 | |
42 int64 version; | |
43 if (!marker.has_token() || marker.token().empty()) { | |
44 // Initialize the version on the first request for this type. | |
45 version = 0; | |
46 } else { | |
47 DCHECK(base::StringToInt64(marker.token(), &version)); | |
48 } | |
49 | |
50 ModelType model_type = GetModelTypeFromSpecificsFieldNumber( | |
51 marker.data_type_id()); | |
52 request_from_version[model_type] = version; | |
53 | |
54 if (version < min_version) | |
55 min_version = version; | |
56 } | |
57 | |
58 return scoped_ptr<UpdateSieve>( | |
59 new UpdateSieve(request_from_version, min_version)); | |
60 } | |
61 | |
62 ~UpdateSieve() { } | |
63 | |
64 // Sets the progress markers in |get_updates_response| given the progress | |
65 // markers from the original GetUpdatesMessage and |new_version| (the latest | |
66 // version in the entries sent back). | |
67 void UpdateProgressMarkers( | |
68 int64 new_version, | |
69 sync_pb::GetUpdatesResponse* get_updates_response) const { | |
70 ModelTypeToVersionMap::const_iterator it; | |
71 for (it = request_from_version_.begin(); it != request_from_version_.end(); | |
72 ++it) { | |
73 sync_pb::DataTypeProgressMarker* new_marker = | |
74 get_updates_response->add_new_progress_marker(); | |
75 new_marker->set_data_type_id( | |
76 GetSpecificsFieldNumberFromModelType(it->first)); | |
77 | |
78 int64 version = std::max(new_version, it->second); | |
79 new_marker->set_token(base::Int64ToString(version)); | |
80 } | |
81 } | |
82 | |
83 // Determines whether the server should send |entity| to the client based | |
84 // on its type and version. | |
85 bool ClientWantsItem(const sync_pb::SyncEntity& entity) const { | |
86 ModelTypeToVersionMap::const_iterator it = | |
87 request_from_version_.find(GetModelType(entity)); | |
88 | |
89 return it == request_from_version_.end() ? | |
90 false : it->second < entity.version(); | |
91 } | |
92 | |
93 // Returns the mininum version seen across all types. | |
94 int64 GetMinVersion() const { | |
95 return min_version_; | |
96 } | |
97 | |
98 // Returns the data type IDs of types being synced for the first time. | |
99 std::vector<ModelType> GetFirstTimeTypes() const { | |
100 std::vector<ModelType> types; | |
101 | |
102 ModelTypeToVersionMap::const_iterator it; | |
103 for (it = request_from_version_.begin(); it != request_from_version_.end(); | |
104 ++it) { | |
105 if (it->second == 0) | |
106 types.push_back(it->first); | |
107 } | |
108 | |
109 return types; | |
110 } | |
111 | |
112 private: | |
113 typedef std::map<ModelType, int64> ModelTypeToVersionMap; | |
114 | |
115 // Creates an UpdateSieve. | |
116 UpdateSieve(const ModelTypeToVersionMap request_from_version, | |
117 const int64 min_version) | |
118 : request_from_version_(request_from_version), | |
119 min_version_(min_version) { } | |
120 | |
121 // Maps data type IDs to the latest version seen for that type. | |
122 const ModelTypeToVersionMap request_from_version_; | |
123 | |
124 // The minimum version seen among all data types. | |
125 const int min_version_; | |
126 }; | |
127 | |
128 } // namespace | |
129 | |
130 FakeServer::FakeServer() : version_(0), birthday_("1234567890") { | |
131 keystore_keys_.push_back("1111111111111111"); | |
132 } | |
133 | |
134 FakeServer::~FakeServer() { } | |
135 | |
136 void FakeServer::CreateDefaultPermanentItems( | |
137 const std::vector<ModelType>& first_time_types) { | |
138 for (std::vector<ModelType>::const_iterator it = first_time_types.begin(); | |
139 it != first_time_types.end(); ++it) { | |
140 if (!ModelTypeSet::All().Has(*it)) { | |
141 NOTREACHED() << "An unexpected ModelType was encountered."; | |
142 } | |
143 | |
144 ModelType model_type = *it; | |
145 CreateSyncEntity(model_type, | |
146 ModelTypeToRootTag(model_type), | |
147 ModelTypeToString(model_type), | |
148 kRootParentTag); | |
149 | |
150 if (model_type == BOOKMARKS) { | |
151 CreateSyncEntity(BOOKMARKS, | |
152 "bookmark_bar", | |
153 "Bookmark Bar", | |
154 ModelTypeToRootTag(BOOKMARKS)); | |
155 CreateSyncEntity(BOOKMARKS, | |
156 "other_bookmarks", | |
157 "Other Bookmarks", | |
158 ModelTypeToRootTag(BOOKMARKS)); | |
159 } | |
160 } | |
161 | |
162 | |
163 // TODO(pvalenzuela): Create the mobile bookmarks folder when the fake server | |
164 // is used by mobile tests. | |
165 } | |
166 | |
167 void FakeServer::CreateSyncEntity(ModelType model_type, | |
168 const std::string& id, | |
169 const std::string& name, | |
170 const std::string& parent_tag) { | |
171 DCHECK(!id.empty()); | |
172 DCHECK(!name.empty()); | |
173 DCHECK(!parent_tag.empty()); | |
174 | |
175 sync_pb::SyncEntity entity; | |
176 entity.set_id_string(id); | |
177 entity.set_non_unique_name(name); | |
178 entity.set_name(name); | |
179 entity.set_server_defined_unique_tag(id); | |
180 entity.set_folder(true); | |
181 entity.set_deleted(false); | |
182 | |
183 entity.set_parent_id_string(parent_tag); | |
184 | |
185 if (parent_tag != kRootParentTag && model_type == BOOKMARKS) { | |
186 // Use a dummy value here. | |
187 entity.set_position_in_parent(1337); | |
188 } | |
189 | |
190 sync_pb::EntitySpecifics* specifics = entity.mutable_specifics(); | |
191 AddDefaultFieldValue(model_type, specifics); | |
192 | |
193 SaveEntity(entity); | |
194 } | |
195 | |
196 void FakeServer::SaveEntity(sync_pb::SyncEntity entity) { | |
197 version_++; | |
198 entity.set_version(version_); | |
199 entity.set_sync_timestamp(version_); | |
200 | |
201 sync_pb::SyncEntity original_entity = entities_[entity.id_string()]; | |
202 entity.set_originator_cache_guid(original_entity.originator_cache_guid()); | |
203 entity.set_originator_client_item_id( | |
204 original_entity.originator_client_item_id()); | |
205 | |
206 entities_[entity.id_string()] = entity; | |
207 } | |
208 | |
209 int FakeServer::HandleCommand(string request, | |
210 int* response_code, | |
211 string* response) { | |
212 sync_pb::ClientToServerMessage message; | |
213 DCHECK(message.ParseFromString(request)); | |
214 | |
215 sync_pb::ClientToServerResponse response_proto; | |
216 switch (message.message_contents()) { | |
217 case sync_pb::ClientToServerMessage::GET_UPDATES: | |
218 response_proto = HandleGetUpdatesRequest(message); | |
219 break; | |
220 case sync_pb::ClientToServerMessage::COMMIT: | |
221 response_proto = HandleCommitRequest(message); | |
222 break; | |
223 default: | |
224 // Any nonzero int will indicate a failure here. This one happens to | |
225 // be net::ERR_NOT_IMPLEMENTED. | |
226 return -11; | |
227 } | |
228 | |
229 *response_code = 200; | |
230 *response = response_proto.SerializeAsString(); | |
231 return 0; | |
232 } | |
233 | |
234 bool SyncEntityVersionComparator(const sync_pb::SyncEntity& first, | |
235 const sync_pb::SyncEntity& second) { | |
236 return first.version() < second.version(); | |
237 } | |
238 | |
239 sync_pb::ClientToServerResponse FakeServer::HandleGetUpdatesRequest( | |
240 const sync_pb::ClientToServerMessage& message) { | |
241 sync_pb::ClientToServerResponse response; | |
242 response.set_error_code(sync_pb::SyncEnums::SUCCESS); | |
243 response.set_store_birthday(birthday_); | |
244 | |
245 sync_pb::GetUpdatesResponse* get_updates_response = | |
246 response.mutable_get_updates(); | |
247 // TODO(pvalenzuela): Implement batching instead of sending all information | |
248 // at once. | |
249 get_updates_response->set_changes_remaining(0); | |
250 | |
251 scoped_ptr<UpdateSieve> sieve = UpdateSieve::Create(message.get_updates()); | |
252 CreateDefaultPermanentItems(sieve->GetFirstTimeTypes()); | |
253 | |
254 int64 min_version = sieve->GetMinVersion(); | |
255 | |
256 bool send_encryption_keys_based_on_nigori = false; | |
257 int64 max_response_version = 0; | |
258 for (EntityMap::iterator it = entities_.begin(); it != entities_.end(); | |
259 ++it) { | |
260 sync_pb::SyncEntity entity = it->second; | |
261 if (entity.version() > min_version && sieve->ClientWantsItem(entity)) { | |
262 sync_pb::SyncEntity* response_entity = | |
263 get_updates_response->add_entries(); | |
264 response_entity->CopyFrom(entity); | |
265 max_response_version = std::max(max_response_version, | |
266 response_entity->version()); | |
267 | |
268 if (response_entity->name() == ModelTypeToString(NIGORI)) { | |
269 send_encryption_keys_based_on_nigori = | |
270 response_entity->specifics().nigori().passphrase_type() == | |
271 sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE; | |
272 } | |
273 } | |
274 } | |
275 | |
276 if (send_encryption_keys_based_on_nigori || | |
277 message.get_updates().need_encryption_key()) { | |
278 for (std::vector<std::string>::iterator it = keystore_keys_.begin(); | |
279 it != keystore_keys_.end(); ++it) { | |
280 get_updates_response->add_encryption_keys(*it); | |
281 } | |
282 } | |
283 | |
284 sieve->UpdateProgressMarkers(max_response_version, get_updates_response); | |
285 | |
286 return response; | |
287 } | |
288 | |
289 sync_pb::SyncEntity FakeServer::CommitEntity(sync_pb::SyncEntity entity, | |
290 string guid) { | |
291 // TODO(pvalenzuela): Implement this. Right now this method cheats and | |
292 // doesn't actually commit. | |
293 return entity; | |
294 } | |
295 | |
296 sync_pb::ClientToServerResponse FakeServer::HandleCommitRequest( | |
297 const sync_pb::ClientToServerMessage& message) { | |
298 sync_pb::ClientToServerResponse response; | |
299 response.set_error_code(sync_pb::SyncEnums::SUCCESS); | |
300 response.set_store_birthday(birthday_); | |
301 | |
302 sync_pb::CommitMessage commit = message.commit(); | |
303 string guid = commit.cache_guid(); | |
304 | |
305 sync_pb::CommitResponse* commit_response = response.mutable_commit(); | |
306 | |
307 ::google::protobuf::RepeatedPtrField<sync_pb::SyncEntity>::const_iterator it; | |
308 for (it = commit.entries().begin(); it != commit.entries().end(); ++it) { | |
309 sync_pb::CommitResponse_EntryResponse* entry_response = | |
310 commit_response->add_entryresponse(); | |
311 | |
312 sync_pb::SyncEntity server_entity = CommitEntity(*it, guid); | |
313 | |
314 entry_response->set_id_string(server_entity.id_string()); | |
315 entry_response->set_response_type(sync_pb::CommitResponse::SUCCESS); | |
316 entry_response->set_version(it->version() + 1); | |
317 } | |
318 | |
319 return response; | |
320 } | |
321 | |
322 } // namespace syncer | |
OLD | NEW |