Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / remoting / webapp / crd / js / combined_host_list_api.js
blob681a4dac087fe8b193d7dbfe66fb106a84b6e119
1 // Copyright 2015 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 /**
6  * @fileoverview
7  * API implementation that combines two other implementations.
8  */
10 /** @suppress {duplicate} */
11 var remoting = remoting || {};
13 (function() {
15 'use strict';
17 /**
18  * Amount of time to wait for GCD results after legacy registry has
19  * returned.
20  */
21 var GCD_TIMEOUT_MS = 1000;
23 /**
24  * @constructor
25  * @param {!remoting.HostListApi} legacyImpl
26  * @param {!remoting.HostListApi} gcdImpl
27  * @implements {remoting.HostListApi}
28  */
29 remoting.CombinedHostListApi = function(legacyImpl, gcdImpl) {
30   /** @const {!remoting.HostListApi} */
31   this.legacyImpl_ = legacyImpl;
33   /** @const {!remoting.HostListApi} */
34   this.gcdImpl_ = gcdImpl;
36   /**
37    * List of host IDs most recently retrieved from |legacyImpl_|.
38    * @type {!Set<string>}
39    */
40   this.legacyIds_ = new Set();
42   /**
43    * List of host IDs most recently retrieved |gcdImpl_|.
44    * @type {!Set<string>}
45    */
46   this.gcdIds_ = new Set();
49 /** @override */
50 remoting.CombinedHostListApi.prototype.register = function(
51     hostName, publicKey, hostClientId) {
52   var that = this;
53   // First, register the new host with GCD, which will create a
54   // service account and generate a host ID.
55   return this.gcdImpl_.register(hostName, publicKey, hostClientId).then(
56       function(gcdRegResult) {
57         // After the GCD registration has been created, copy the
58         // registration to the legacy directory so that clients not yet
59         // upgraded to use GCD can see the new host.
60         //
61         // This is an ugly hack for multiple reasons:
62         //
63         // 1. It completely ignores |this.legacyImpl_|, complicating
64         //    unit tests.
65         //
66         // 2. It relies on the fact that, when |hostClientId| is null,
67         //    the legacy directory will "register" a host without
68         //    creating a service account.  This is an obsolete feature
69         //    of the legacy directory that is being revived for a new
70         //    purpose.
71         //
72         // 3. It assumes the device ID generated by GCD is usable as a
73         //    host ID by the legacy directory.  Fortunately both systems
74         //    use UUIDs.
75         return remoting.LegacyHostListApi.registerWithHostId(
76             gcdRegResult.hostId, hostName, publicKey, null).then(
77                 function() {
78                   // On success, return the result from GCD, ignoring
79                   // the result returned by the legacy directory.
80                   that.gcdIds_.add(gcdRegResult.hostId);
81                   that.legacyIds_.add(gcdRegResult.hostId);
82                   return gcdRegResult;
83                 },
84                 function(error) {
85                   console.warn(
86                       'Error copying host GCD host registration ' +
87                       'to legacy directory: ' + error);
88                   throw error;
89                 }
90             );
91       });
94 /** @override */
95 remoting.CombinedHostListApi.prototype.get = function() {
96   // Fetch the host list from both directories and merge hosts that
97   // have the same ID.
98   var that = this;
99   var legacyPromise = this.legacyImpl_.get();
100   var gcdPromise = this.gcdImpl_.get();
101   return legacyPromise.then(function(legacyHosts) {
102     // If GCD is too slow, just act as if it had returned an empty
103     // result set.
104     var timeoutPromise = base.Promise.withTimeout(
105         gcdPromise, GCD_TIMEOUT_MS, []);
107     // Combine host information from both directories.  In the case of
108     // conflicting information, prefer information from whichever
109     // directory claims to have newer information.
110     return timeoutPromise.then(function(gcdHosts) {
111       // Update |that.gcdIds_| and |that.legacyIds_|.
112       that.gcdIds_ = new Set();
113       that.legacyIds_ = new Set();
114       gcdHosts.forEach(function(host) {
115         that.gcdIds_.add(host.hostId);
116       });
117       legacyHosts.forEach(function(host) {
118         that.legacyIds_.add(host.hostId);
119       });
121       /**
122        * A mapping from host IDs to the host data that will be
123        * returned from this method.
124        * @type {!Map<string,!remoting.Host>}
125        */
126       var hostMap = new Map();
128       // Add legacy hosts to the output; some of these may be replaced
129       // by GCD hosts.
130       legacyHosts.forEach(function(host) {
131         hostMap.set(host.hostId, host);
132       });
134       // Add GCD hosts to the output, possibly replacing some legacy
135       // host data with newer data from GCD.
136       gcdHosts.forEach(function(gcdHost) {
137         var hostId = gcdHost.hostId;
138         var legacyHost = hostMap.get(hostId);
139         if (!legacyHost || legacyHost.updatedTime <= gcdHost.updatedTime) {
140           hostMap.set(hostId, gcdHost);
141         }
142       });
144       // Convert the result to an Array.
145       // TODO(jrw): Use Array.from once it becomes available.
146       var hosts = [];
147       hostMap.forEach(function(host) {
148         hosts.push(host);
149       });
150       return hosts;
151     });
152   });
155 /** @override */
156 remoting.CombinedHostListApi.prototype.put =
157     function(hostId, hostName, hostPublicKey) {
158   var legacyPromise = Promise.resolve();
159   if (this.legacyIds_.has(hostId)) {
160     legacyPromise = this.legacyImpl_.put(hostId, hostName, hostPublicKey);
161   }
162   var gcdPromise = Promise.resolve();
163   if (this.gcdIds_.has(hostId)) {
164     gcdPromise = this.gcdImpl_.put(hostId, hostName, hostPublicKey);
165   }
166   return legacyPromise.then(function() {
167     // If GCD is too slow, just ignore it and return result from the
168     // legacy directory.
169     return base.Promise.withTimeout(
170         gcdPromise, GCD_TIMEOUT_MS);
171   });
174 /** @override */
175 remoting.CombinedHostListApi.prototype.remove = function(hostId) {
176   var legacyPromise = Promise.resolve();
177   if (this.legacyIds_.has(hostId)) {
178     legacyPromise = this.legacyImpl_.remove(hostId);
179   }
180   var gcdPromise = Promise.resolve();
181   if (this.gcdIds_.has(hostId)) {
182     gcdPromise = this.gcdImpl_.remove(hostId);
183   }
184   return legacyPromise.then(function() {
185     // If GCD is too slow, just ignore it and return result from the
186     // legacy directory.
187     return base.Promise.withTimeout(
188         gcdPromise, GCD_TIMEOUT_MS);
189   });
192 /** @override */
193 remoting.CombinedHostListApi.prototype.getSupportHost = function(supportId) {
194   return this.legacyImpl_.getSupportHost(supportId);
197 })();