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
3 // found in the LICENSE file.
5 #include "sync/test/fake_server/fake_server.h"
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 "net/base/net_errors.h"
16 #include "net/http/http_status_code.h"
17 #include "sync/internal_api/public/base/model_type.h"
18 #include "sync/protocol/sync.pb.h"
22 // The parent tag for childen of the root node.
23 static const char kRootParentTag
[] = "0";
25 // The default birthday value.
26 static const char kDefaultBirthday
[] = "1234567890";
28 // The default keystore key.
29 static const char kDefaultKeystoreKey
[] = "1111111111111111";
31 // A dummy value to use for the position_in_parent field of SyncEntity.
32 static const int64 kDummyBookmarkPositionInParent
= 1337;
37 // A filter used during GetUpdates calls to determine what information to
38 // send back to the client. There is a 1:1 correspondence between any given
39 // GetUpdates call and an UpdateSieve instance.
44 // Factory method for creating an UpdateSieve.
45 static scoped_ptr
<UpdateSieve
> Create(
46 const sync_pb::GetUpdatesMessage
& get_updates_message
);
48 // Sets the progress markers in |get_updates_response| given the progress
49 // markers from the original GetUpdatesMessage and |new_version| (the latest
50 // version in the entries sent back).
51 void UpdateProgressMarkers(
53 sync_pb::GetUpdatesResponse
* get_updates_response
) const {
54 ModelTypeToVersionMap::const_iterator it
;
55 for (it
= request_from_version_
.begin(); it
!= request_from_version_
.end();
57 sync_pb::DataTypeProgressMarker
* new_marker
=
58 get_updates_response
->add_new_progress_marker();
59 new_marker
->set_data_type_id(
60 GetSpecificsFieldNumberFromModelType(it
->first
));
62 int64 version
= std::max(new_version
, it
->second
);
63 new_marker
->set_token(base::Int64ToString(version
));
67 // Determines whether the server should send |entity| to the client based
68 // on its type and version.
69 bool ClientWantsItem(const sync_pb::SyncEntity
& entity
) const {
70 ModelTypeToVersionMap::const_iterator it
=
71 request_from_version_
.find(GetModelType(entity
));
73 return it
== request_from_version_
.end() ?
74 false : it
->second
< entity
.version();
77 // Returns the mininum version seen across all types.
78 int64
GetMinVersion() const {
82 // Returns the data type IDs of types being synced for the first time.
83 std::vector
<ModelType
> GetFirstTimeTypes() const {
84 std::vector
<ModelType
> types
;
86 ModelTypeToVersionMap::const_iterator it
;
87 for (it
= request_from_version_
.begin(); it
!= request_from_version_
.end();
90 types
.push_back(it
->first
);
97 typedef std::map
<ModelType
, int64
> ModelTypeToVersionMap
;
99 // Creates an UpdateSieve.
100 UpdateSieve(const ModelTypeToVersionMap request_from_version
,
101 const int64 min_version
)
102 : request_from_version_(request_from_version
),
103 min_version_(min_version
) { }
105 // Maps data type IDs to the latest version seen for that type.
106 const ModelTypeToVersionMap request_from_version_
;
108 // The minimum version seen among all data types.
109 const int min_version_
;
112 scoped_ptr
<UpdateSieve
> UpdateSieve::Create(
113 const sync_pb::GetUpdatesMessage
& get_updates_message
) {
114 DCHECK_GT(get_updates_message
.from_progress_marker_size(), 0);
116 UpdateSieve::ModelTypeToVersionMap request_from_version
;
117 int64 min_version
= std::numeric_limits
<int64
>::max();
118 for (int i
= 0; i
< get_updates_message
.from_progress_marker_size(); i
++) {
119 sync_pb::DataTypeProgressMarker marker
=
120 get_updates_message
.from_progress_marker(i
);
123 // Let the version remain zero if there is no token or an empty token (the
124 // first request for this type).
125 if (marker
.has_token() && !marker
.token().empty()) {
126 bool parsed
= base::StringToInt64(marker
.token(), &version
);
130 ModelType model_type
= GetModelTypeFromSpecificsFieldNumber(
131 marker
.data_type_id());
132 request_from_version
[model_type
] = version
;
134 if (version
< min_version
)
135 min_version
= version
;
138 return scoped_ptr
<UpdateSieve
>(
139 new UpdateSieve(request_from_version
, min_version
));
144 FakeServer::FakeServer() : version_(0), birthday_(kDefaultBirthday
) {
145 keystore_keys_
.push_back(kDefaultKeystoreKey
);
148 FakeServer::~FakeServer() { }
150 void FakeServer::CreateDefaultPermanentItems(
151 const std::vector
<ModelType
>& first_time_types
) {
152 for (std::vector
<ModelType
>::const_iterator it
= first_time_types
.begin();
153 it
!= first_time_types
.end(); ++it
) {
154 if (!ModelTypeSet::All().Has(*it
)) {
155 NOTREACHED() << "An unexpected ModelType was encountered.";
158 ModelType model_type
= *it
;
159 CreateSyncEntity(model_type
,
160 ModelTypeToRootTag(model_type
),
161 ModelTypeToString(model_type
),
164 if (model_type
== BOOKMARKS
) {
165 CreateSyncEntity(BOOKMARKS
,
168 ModelTypeToRootTag(BOOKMARKS
));
169 CreateSyncEntity(BOOKMARKS
,
172 ModelTypeToRootTag(BOOKMARKS
));
177 // TODO(pvalenzuela): Create the mobile bookmarks folder when the fake server
178 // is used by mobile tests.
181 void FakeServer::CreateSyncEntity(ModelType model_type
,
182 const std::string
& id
,
183 const std::string
& name
,
184 const std::string
& parent_tag
) {
186 DCHECK(!name
.empty());
187 DCHECK(!parent_tag
.empty());
189 sync_pb::SyncEntity entity
;
190 entity
.set_id_string(id
);
191 entity
.set_non_unique_name(name
);
192 entity
.set_name(name
);
193 entity
.set_server_defined_unique_tag(id
);
194 entity
.set_folder(true);
195 entity
.set_deleted(false);
197 entity
.set_parent_id_string(parent_tag
);
199 if (parent_tag
!= kRootParentTag
&& model_type
== BOOKMARKS
) {
200 // Use a dummy value here.
201 entity
.set_position_in_parent(kDummyBookmarkPositionInParent
);
204 sync_pb::EntitySpecifics
* specifics
= entity
.mutable_specifics();
205 AddDefaultFieldValue(model_type
, specifics
);
210 void FakeServer::SaveEntity(sync_pb::SyncEntity
* entity
) {
212 entity
->set_version(version_
);
213 entity
->set_sync_timestamp(version_
);
215 sync_pb::SyncEntity original_entity
= entities_
[entity
->id_string()];
216 entity
->set_originator_cache_guid(original_entity
.originator_cache_guid());
217 entity
->set_originator_client_item_id(
218 original_entity
.originator_client_item_id());
220 entities_
[entity
->id_string()] = *entity
;
223 int FakeServer::HandleCommand(const string
& request
,
226 sync_pb::ClientToServerMessage message
;
227 bool parsed
= message
.ParseFromString(request
);
230 sync_pb::ClientToServerResponse response_proto
;
231 switch (message
.message_contents()) {
232 case sync_pb::ClientToServerMessage::GET_UPDATES
:
233 response_proto
= HandleGetUpdatesRequest(message
);
235 case sync_pb::ClientToServerMessage::COMMIT
:
236 response_proto
= HandleCommitRequest(message
);
239 return net::ERR_NOT_IMPLEMENTED
;
242 *response_code
= net::HTTP_OK
;
243 *response
= response_proto
.SerializeAsString();
247 bool SyncEntityVersionComparator(const sync_pb::SyncEntity
& first
,
248 const sync_pb::SyncEntity
& second
) {
249 return first
.version() < second
.version();
252 sync_pb::ClientToServerResponse
FakeServer::HandleGetUpdatesRequest(
253 const sync_pb::ClientToServerMessage
& message
) {
254 sync_pb::ClientToServerResponse response
;
255 response
.set_error_code(sync_pb::SyncEnums::SUCCESS
);
256 response
.set_store_birthday(birthday_
);
258 sync_pb::GetUpdatesResponse
* get_updates_response
=
259 response
.mutable_get_updates();
260 // TODO(pvalenzuela): Implement batching instead of sending all information
262 get_updates_response
->set_changes_remaining(0);
264 scoped_ptr
<UpdateSieve
> sieve
= UpdateSieve::Create(message
.get_updates());
265 CreateDefaultPermanentItems(sieve
->GetFirstTimeTypes());
267 int64 min_version
= sieve
->GetMinVersion();
269 bool send_encryption_keys_based_on_nigori
= false;
270 int64 max_response_version
= 0;
271 for (EntityMap::iterator it
= entities_
.begin(); it
!= entities_
.end();
273 sync_pb::SyncEntity entity
= it
->second
;
274 if (entity
.version() > min_version
&& sieve
->ClientWantsItem(entity
)) {
275 sync_pb::SyncEntity
* response_entity
=
276 get_updates_response
->add_entries();
277 response_entity
->CopyFrom(entity
);
278 max_response_version
= std::max(max_response_version
,
279 response_entity
->version());
281 if (response_entity
->name() == ModelTypeToString(NIGORI
)) {
282 send_encryption_keys_based_on_nigori
=
283 response_entity
->specifics().nigori().passphrase_type() ==
284 sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE
;
289 if (send_encryption_keys_based_on_nigori
||
290 message
.get_updates().need_encryption_key()) {
291 for (std::vector
<std::string
>::iterator it
= keystore_keys_
.begin();
292 it
!= keystore_keys_
.end(); ++it
) {
293 get_updates_response
->add_encryption_keys(*it
);
297 sieve
->UpdateProgressMarkers(max_response_version
, get_updates_response
);
302 sync_pb::SyncEntity
FakeServer::CommitEntity(const sync_pb::SyncEntity
& entity
,
304 // TODO(pvalenzuela): Implement this. Right now this method cheats and
305 // doesn't actually commit.
309 sync_pb::ClientToServerResponse
FakeServer::HandleCommitRequest(
310 const sync_pb::ClientToServerMessage
& message
) {
311 sync_pb::ClientToServerResponse response
;
312 response
.set_error_code(sync_pb::SyncEnums::SUCCESS
);
313 response
.set_store_birthday(birthday_
);
315 sync_pb::CommitMessage commit
= message
.commit();
316 string guid
= commit
.cache_guid();
318 sync_pb::CommitResponse
* commit_response
= response
.mutable_commit();
320 ::google::protobuf::RepeatedPtrField
<sync_pb::SyncEntity
>::const_iterator it
;
321 for (it
= commit
.entries().begin(); it
!= commit
.entries().end(); ++it
) {
322 sync_pb::CommitResponse_EntryResponse
* entry_response
=
323 commit_response
->add_entryresponse();
325 sync_pb::SyncEntity server_entity
= CommitEntity(*it
, guid
);
327 entry_response
->set_id_string(server_entity
.id_string());
328 entry_response
->set_response_type(sync_pb::CommitResponse::SUCCESS
);
329 entry_response
->set_version(it
->version() + 1);
335 } // namespace syncer