Commit f4b1e88b by Samuel Padgett

Support PartialObjectMetadataList in DataService.list()

Let clients optionally request only object metadata when calling
DataService.list().

See https://bugzilla.redhat.com/show_bug.cgi?id=1471033
parent 7f91b5a8
......@@ -1210,6 +1210,15 @@ angular.module('openshiftCommonServices')
angular.module('openshiftCommonServices')
.factory('DataService', function($cacheFactory, $http, $ws, $rootScope, $q, API_CFG, APIService, Logger, $timeout, base64, base64util) {
// Accept PartialObjectMetadataList. Unfortunately we can't use the Accept
// header to fallback to JSON due to an API server content negotiation bug.
// https://github.com/kubernetes/kubernetes/issues/50519
//
// This is a potential version skew issue for when the web console runs in
// a pod where we potentially need to support different server versions.
// https://trello.com/c/9oaUh8xP
var ACCEPT_PARTIAL_OBJECT_METADATA_LIST = 'application/json;as=PartialObjectMetadataList;v=v1alpha1;g=meta.k8s.io';
function Data(array) {
this._data = {};
this._objectsByAttribute(array, "metadata.name", this._data);
......@@ -1356,11 +1365,12 @@ angular.module('openshiftCommonServices')
// which includes a helper method for returning a map indexed
// by attribute (e.g. data.by('metadata.name'))
// opts: http - options to pass to the inner $http call
// partialObjectMetadataList - if true, request only the metadata for each object
//
// returns a promise
DataService.prototype.list = function(resource, context, callback, opts) {
resource = APIService.toResourceGroupVersion(resource);
var key = this._uniqueKey(resource, null, context, _.get(opts, 'http.params'));
var key = this._uniqueKey(resource, null, context, opts);
var deferred = this._listDeferred(key);
if (callback) {
deferred.promise.then(callback);
......@@ -1622,7 +1632,7 @@ angular.module('openshiftCommonServices')
DataService.prototype.get = function(resource, name, context, opts) {
resource = APIService.toResourceGroupVersion(resource);
opts = opts || {};
var key = this._uniqueKey(resource, name, context, _.get(opts, 'http.params'));
var key = this._uniqueKey(resource, name, context, opts);
var force = !!opts.force;
delete opts.force;
......@@ -1819,7 +1829,7 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
resource = APIService.toResourceGroupVersion(resource);
opts = opts || {};
var invokeApply = !opts.skipDigest;
var key = this._uniqueKey(resource, null, context, _.get(opts, 'http.params'));
var key = this._uniqueKey(resource, null, context, opts);
if (callback) {
// If we were given a callback, add it
this._watchCallbacks(key).add(callback);
......@@ -1898,7 +1908,7 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
DataService.prototype.watchObject = function(resource, name, context, callback, opts) {
resource = APIService.toResourceGroupVersion(resource);
opts = opts || {};
var key = this._uniqueKey(resource, name, context, _.get(opts, 'http.params'));
var key = this._uniqueKey(resource, name, context, opts);
var wrapperCallback;
if (callback) {
// If we were given a callback, add it
......@@ -1939,10 +1949,10 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
var callback = handle.callback;
var objectCallback = handle.objectCallback;
var opts = handle.opts;
var key = this._uniqueKey(resource, null, context, _.get(opts, 'http.params'));
var key = this._uniqueKey(resource, null, context, opts);
if (objectCallback && objectName) {
var objectKey = this._uniqueKey(resource, objectName, context, _.get(opts, 'http.params'));
var objectKey = this._uniqueKey(resource, objectName, context, opts);
var objCallbacks = this._watchObjectCallbacks(objectKey);
objCallbacks.remove(objectCallback);
}
......@@ -2160,29 +2170,43 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
// - ensure namespace if available
// - ensure only witelisted url params used for keys (fieldSelector, labelSelector) via paramsForKey
// and that these are consistently ordered
// - ensure that requests with different Accept request headers have different keys
// - NOTE: Do not use the key as your url for API requests. This function does not use the 'isWebsocket'
// bool. Both websocket & http operations should respond with the same data from cache if key matches
// so the unique key will always include http://
DataService.prototype._uniqueKey = function(resource, name, context, params) {
DataService.prototype._uniqueKey = function(resource, name, context, opts) {
var ns = context && context.namespace ||
_.get(context, 'project.metadata.name') ||
context.projectName;
return this._urlForResource(resource, name, context, null, angular.extend({}, {}, {namespace: ns})).toString() + paramsForKey(params || {});
var params = _.get(opts, 'http.params');
var url = this._urlForResource(resource, name, context, null, angular.extend({}, {}, {namespace: ns})).toString() + paramsForKey(params || {});
if (_.get(opts, 'partialObjectMetadataList')) {
// Make sure partial objects get a different cache key.
return url + '#' + ACCEPT_PARTIAL_OBJECT_METADATA_LIST;
}
return url;
};
DataService.prototype._startListOp = function(resource, context, opts) {
opts = opts || {};
var key = this._uniqueKey(resource, null, context, _.get(opts, 'http.params'));
var key = this._uniqueKey(resource, null, context, opts);
// mark the operation as in progress
this._listInFlight(key, true);
var headers = {};
if (opts.partialObjectMetadataList) {
headers.Accept = ACCEPT_PARTIAL_OBJECT_METADATA_LIST;
}
var self = this;
if (context.projectPromise && !resource.equals("projects")) {
context.projectPromise.done(function(project) {
$http(angular.extend({
method: 'GET',
auth: {},
headers: headers,
url: self._urlForResource(resource, null, context, false, {namespace: project.metadata.name})
}, opts.http || {}))
.success(function(data, status, headerFunc, config, statusText) {
......@@ -2206,6 +2230,7 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
$http({
method: 'GET',
auth: {},
headers: headers,
url: this._urlForResource(resource, null, context),
}).success(function(data, status, headerFunc, config, statusText) {
self._listOpComplete(key, resource, context, opts, data);
......@@ -2250,7 +2275,9 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
var deferred = this._listDeferred(key);
delete this._listDeferredMap[key];
this._resourceVersion(key, data.resourceVersion || data.metadata.resourceVersion);
// Some responses might not have `data.metadata` (for instance, PartialObjectMetadataList).
var resourceVersion = _.get(data, 'resourceVersion') || _.get(data, 'metadata.resourceVersion');
this._resourceVersion(key, resourceVersion);
this._data(key, items);
deferred.resolve(this._data(key));
this._watchCallbacks(key).fire(this._data(key));
......@@ -2315,12 +2342,12 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
DataService.prototype._watchOpOnOpen = function(resource, context, opts, event) {
Logger.log('Websocket opened for resource/context', resource, context);
var key = this._uniqueKey(resource, null, context, _.get(opts, 'http.params'));
var key = this._uniqueKey(resource, null, context, opts);
this._addWebsocketEvent(key, 'open');
};
DataService.prototype._watchOpOnMessage = function(resource, context, opts, event) {
var key = this._uniqueKey(resource, null, context, _.get(opts, 'http.params'));
var key = this._uniqueKey(resource, null, context, opts);
opts = opts || {};
var invokeApply = !opts.skipDigest;
try {
......@@ -2362,7 +2389,7 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
DataService.prototype._watchOpOnClose = function(resource, context, opts, event) {
var eventWS = event.target;
var key = this._uniqueKey(resource, null, context, _.get(opts, 'http.params'));
var key = this._uniqueKey(resource, null, context, opts);
if (!eventWS) {
Logger.log("Skipping reopen, no eventWS in event", event);
......
......@@ -3089,6 +3089,15 @@ angular.module('openshiftCommonServices')
angular.module('openshiftCommonServices')
.factory('DataService', ["$cacheFactory", "$http", "$ws", "$rootScope", "$q", "API_CFG", "APIService", "Logger", "$timeout", "base64", "base64util", function($cacheFactory, $http, $ws, $rootScope, $q, API_CFG, APIService, Logger, $timeout, base64, base64util) {
// Accept PartialObjectMetadataList. Unfortunately we can't use the Accept
// header to fallback to JSON due to an API server content negotiation bug.
// https://github.com/kubernetes/kubernetes/issues/50519
//
// This is a potential version skew issue for when the web console runs in
// a pod where we potentially need to support different server versions.
// https://trello.com/c/9oaUh8xP
var ACCEPT_PARTIAL_OBJECT_METADATA_LIST = 'application/json;as=PartialObjectMetadataList;v=v1alpha1;g=meta.k8s.io';
function Data(array) {
this._data = {};
this._objectsByAttribute(array, "metadata.name", this._data);
......@@ -3235,11 +3244,12 @@ angular.module('openshiftCommonServices')
// which includes a helper method for returning a map indexed
// by attribute (e.g. data.by('metadata.name'))
// opts: http - options to pass to the inner $http call
// partialObjectMetadataList - if true, request only the metadata for each object
//
// returns a promise
DataService.prototype.list = function(resource, context, callback, opts) {
resource = APIService.toResourceGroupVersion(resource);
var key = this._uniqueKey(resource, null, context, _.get(opts, 'http.params'));
var key = this._uniqueKey(resource, null, context, opts);
var deferred = this._listDeferred(key);
if (callback) {
deferred.promise.then(callback);
......@@ -3501,7 +3511,7 @@ angular.module('openshiftCommonServices')
DataService.prototype.get = function(resource, name, context, opts) {
resource = APIService.toResourceGroupVersion(resource);
opts = opts || {};
var key = this._uniqueKey(resource, name, context, _.get(opts, 'http.params'));
var key = this._uniqueKey(resource, name, context, opts);
var force = !!opts.force;
delete opts.force;
......@@ -3698,7 +3708,7 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
resource = APIService.toResourceGroupVersion(resource);
opts = opts || {};
var invokeApply = !opts.skipDigest;
var key = this._uniqueKey(resource, null, context, _.get(opts, 'http.params'));
var key = this._uniqueKey(resource, null, context, opts);
if (callback) {
// If we were given a callback, add it
this._watchCallbacks(key).add(callback);
......@@ -3777,7 +3787,7 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
DataService.prototype.watchObject = function(resource, name, context, callback, opts) {
resource = APIService.toResourceGroupVersion(resource);
opts = opts || {};
var key = this._uniqueKey(resource, name, context, _.get(opts, 'http.params'));
var key = this._uniqueKey(resource, name, context, opts);
var wrapperCallback;
if (callback) {
// If we were given a callback, add it
......@@ -3818,10 +3828,10 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
var callback = handle.callback;
var objectCallback = handle.objectCallback;
var opts = handle.opts;
var key = this._uniqueKey(resource, null, context, _.get(opts, 'http.params'));
var key = this._uniqueKey(resource, null, context, opts);
if (objectCallback && objectName) {
var objectKey = this._uniqueKey(resource, objectName, context, _.get(opts, 'http.params'));
var objectKey = this._uniqueKey(resource, objectName, context, opts);
var objCallbacks = this._watchObjectCallbacks(objectKey);
objCallbacks.remove(objectCallback);
}
......@@ -4039,29 +4049,43 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
// - ensure namespace if available
// - ensure only witelisted url params used for keys (fieldSelector, labelSelector) via paramsForKey
// and that these are consistently ordered
// - ensure that requests with different Accept request headers have different keys
// - NOTE: Do not use the key as your url for API requests. This function does not use the 'isWebsocket'
// bool. Both websocket & http operations should respond with the same data from cache if key matches
// so the unique key will always include http://
DataService.prototype._uniqueKey = function(resource, name, context, params) {
DataService.prototype._uniqueKey = function(resource, name, context, opts) {
var ns = context && context.namespace ||
_.get(context, 'project.metadata.name') ||
context.projectName;
return this._urlForResource(resource, name, context, null, angular.extend({}, {}, {namespace: ns})).toString() + paramsForKey(params || {});
var params = _.get(opts, 'http.params');
var url = this._urlForResource(resource, name, context, null, angular.extend({}, {}, {namespace: ns})).toString() + paramsForKey(params || {});
if (_.get(opts, 'partialObjectMetadataList')) {
// Make sure partial objects get a different cache key.
return url + '#' + ACCEPT_PARTIAL_OBJECT_METADATA_LIST;
}
return url;
};
DataService.prototype._startListOp = function(resource, context, opts) {
opts = opts || {};
var key = this._uniqueKey(resource, null, context, _.get(opts, 'http.params'));
var key = this._uniqueKey(resource, null, context, opts);
// mark the operation as in progress
this._listInFlight(key, true);
var headers = {};
if (opts.partialObjectMetadataList) {
headers.Accept = ACCEPT_PARTIAL_OBJECT_METADATA_LIST;
}
var self = this;
if (context.projectPromise && !resource.equals("projects")) {
context.projectPromise.done(function(project) {
$http(angular.extend({
method: 'GET',
auth: {},
headers: headers,
url: self._urlForResource(resource, null, context, false, {namespace: project.metadata.name})
}, opts.http || {}))
.success(function(data, status, headerFunc, config, statusText) {
......@@ -4085,6 +4109,7 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
$http({
method: 'GET',
auth: {},
headers: headers,
url: this._urlForResource(resource, null, context),
}).success(function(data, status, headerFunc, config, statusText) {
self._listOpComplete(key, resource, context, opts, data);
......@@ -4129,7 +4154,9 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
var deferred = this._listDeferred(key);
delete this._listDeferredMap[key];
this._resourceVersion(key, data.resourceVersion || data.metadata.resourceVersion);
// Some responses might not have `data.metadata` (for instance, PartialObjectMetadataList).
var resourceVersion = _.get(data, 'resourceVersion') || _.get(data, 'metadata.resourceVersion');
this._resourceVersion(key, resourceVersion);
this._data(key, items);
deferred.resolve(this._data(key));
this._watchCallbacks(key).fire(this._data(key));
......@@ -4194,12 +4221,12 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
DataService.prototype._watchOpOnOpen = function(resource, context, opts, event) {
Logger.log('Websocket opened for resource/context', resource, context);
var key = this._uniqueKey(resource, null, context, _.get(opts, 'http.params'));
var key = this._uniqueKey(resource, null, context, opts);
this._addWebsocketEvent(key, 'open');
};
DataService.prototype._watchOpOnMessage = function(resource, context, opts, event) {
var key = this._uniqueKey(resource, null, context, _.get(opts, 'http.params'));
var key = this._uniqueKey(resource, null, context, opts);
opts = opts || {};
var invokeApply = !opts.skipDigest;
try {
......@@ -4241,7 +4268,7 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
DataService.prototype._watchOpOnClose = function(resource, context, opts, event) {
var eventWS = event.target;
var key = this._uniqueKey(resource, null, context, _.get(opts, 'http.params'));
var key = this._uniqueKey(resource, null, context, opts);
if (!eventWS) {
Logger.log("Skipping reopen, no eventWS in event", event);
......
......@@ -1279,6 +1279,7 @@ if (events.length < maxConsecutiveCloseEvents) return !1;
for (var i = events.length - maxConsecutiveCloseEvents; i < events.length; i++) if ("close" !== events[i].type) return !1;
return !0;
}
var ACCEPT_PARTIAL_OBJECT_METADATA_LIST = "application/json;as=PartialObjectMetadataList;v=v1alpha1;g=meta.k8s.io";
Data.prototype.by = function(attr) {
if ("metadata.name" === attr) return this._data;
var map = {};
......@@ -1313,7 +1314,7 @@ status && (message += " (status " + status + ")"), queuedErrors.push(message), a
};
DataService.prototype.list = function(resource, context, callback, opts) {
resource = APIService.toResourceGroupVersion(resource);
var key = this._uniqueKey(resource, null, context, _.get(opts, "http.params")), deferred = this._listDeferred(key);
var key = this._uniqueKey(resource, null, context, opts), deferred = this._listDeferred(key);
return callback && deferred.promise.then(callback), this._isCached(key) ? deferred.resolve(this._data(key)) :this._listInFlight(key) || this._startListOp(resource, context, opts), deferred.promise;
}, DataService.prototype["delete"] = function(resource, name, context, opts) {
resource = APIService.toResourceGroupVersion(resource), opts = opts || {};
......@@ -1433,7 +1434,7 @@ object:object
}), deferred.promise;
}, DataService.prototype.get = function(resource, name, context, opts) {
resource = APIService.toResourceGroupVersion(resource), opts = opts || {};
var key = this._uniqueKey(resource, name, context, _.get(opts, "http.params"));
var key = this._uniqueKey(resource, name, context, opts);
!!opts.force;
delete opts.force;
var deferred = $q.defer(), existingImmutableData = this._immutableData(key);
......@@ -1535,7 +1536,7 @@ ws.close();
};
}, DataService.prototype.watch = function(resource, context, callback, opts) {
resource = APIService.toResourceGroupVersion(resource), opts = opts || {};
var invokeApply = !opts.skipDigest, key = this._uniqueKey(resource, null, context, _.get(opts, "http.params"));
var invokeApply = !opts.skipDigest, key = this._uniqueKey(resource, null, context, opts);
if (callback) this._watchCallbacks(key).add(callback); else if (!this._watchCallbacks(key).has()) return {};
var existingWatchOpts = this._watchOptions(key);
if (existingWatchOpts) {
......@@ -1561,7 +1562,7 @@ opts:opts
};
}, DataService.prototype.watchObject = function(resource, name, context, callback, opts) {
resource = APIService.toResourceGroupVersion(resource), opts = opts || {};
var wrapperCallback, key = this._uniqueKey(resource, name, context, _.get(opts, "http.params"));
var wrapperCallback, key = this._uniqueKey(resource, name, context, opts);
if (callback) {
this._watchObjectCallbacks(key).add(callback);
var self = this;
......@@ -1575,9 +1576,9 @@ itemsByName[name] && self._watchObjectCallbacks(key).fire(itemsByName[name]);
var handle = this.watch(resource, context, wrapperCallback, opts);
return handle.objectCallback = callback, handle.objectName = name, handle;
}, DataService.prototype.unwatch = function(handle) {
var resource = handle.resource, objectName = handle.objectName, context = handle.context, callback = handle.callback, objectCallback = handle.objectCallback, opts = handle.opts, key = this._uniqueKey(resource, null, context, _.get(opts, "http.params"));
var resource = handle.resource, objectName = handle.objectName, context = handle.context, callback = handle.callback, objectCallback = handle.objectCallback, opts = handle.opts, key = this._uniqueKey(resource, null, context, opts);
if (objectCallback && objectName) {
var objectKey = this._uniqueKey(resource, objectName, context, _.get(opts, "http.params")), objCallbacks = this._watchObjectCallbacks(objectKey);
var objectKey = this._uniqueKey(resource, objectName, context, opts), objCallbacks = this._watchObjectCallbacks(objectKey);
objCallbacks.remove(objectCallback);
}
var callbacks = this._watchCallbacks(key);
......@@ -1632,20 +1633,23 @@ return _.reduce(keys, function(result, key, i) {
return result + key + "=" + encodeURIComponent(params[key]) + (i < keys.length - 1 ? "&" :"");
}, "?");
};
DataService.prototype._uniqueKey = function(resource, name, context, params) {
var ns = context && context.namespace || _.get(context, "project.metadata.name") || context.projectName;
return this._urlForResource(resource, name, context, null, angular.extend({}, {}, {
DataService.prototype._uniqueKey = function(resource, name, context, opts) {
var ns = context && context.namespace || _.get(context, "project.metadata.name") || context.projectName, params = _.get(opts, "http.params"), url = this._urlForResource(resource, name, context, null, angular.extend({}, {}, {
namespace:ns
})).toString() + paramsForKey(params || {});
return _.get(opts, "partialObjectMetadataList") ? url + "#" + ACCEPT_PARTIAL_OBJECT_METADATA_LIST :url;
}, DataService.prototype._startListOp = function(resource, context, opts) {
opts = opts || {};
var key = this._uniqueKey(resource, null, context, _.get(opts, "http.params"));
var key = this._uniqueKey(resource, null, context, opts);
this._listInFlight(key, !0);
var headers = {};
opts.partialObjectMetadataList && (headers.Accept = ACCEPT_PARTIAL_OBJECT_METADATA_LIST);
var self = this;
context.projectPromise && !resource.equals("projects") ? context.projectPromise.done(function(project) {
$http(angular.extend({
method:"GET",
auth:{},
headers:headers,
url:self._urlForResource(resource, null, context, !1, {
namespace:project.metadata.name
})
......@@ -1659,6 +1663,7 @@ delete self._listDeferredMap[key], deferred.reject(data, status, headers, config
}) :$http({
method:"GET",
auth:{},
headers:headers,
url:this._urlForResource(resource, null, context)
}).success(function(data, status, headerFunc, config, statusText) {
self._listOpComplete(key, resource, context, opts, data);
......@@ -1674,7 +1679,9 @@ data.kind && data.kind.indexOf("List") === data.kind.length - 4 && angular.forEa
item.kind || (item.kind = data.kind.slice(0, -4)), item.apiVersion || (item.apiVersion = data.apiVersion);
}), this._listInFlight(key, !1);
var deferred = this._listDeferred(key);
if (delete this._listDeferredMap[key], this._resourceVersion(key, data.resourceVersion || data.metadata.resourceVersion), this._data(key, items), deferred.resolve(this._data(key)), this._watchCallbacks(key).fire(this._data(key)), this._watchCallbacks(key).has()) {
delete this._listDeferredMap[key];
var resourceVersion = _.get(data, "resourceVersion") || _.get(data, "metadata.resourceVersion");
if (this._resourceVersion(key, resourceVersion), this._data(key, items), deferred.resolve(this._data(key)), this._watchCallbacks(key).fire(this._data(key)), this._watchCallbacks(key).has()) {
var watchOpts = this._watchOptions(key) || {};
watchOpts.poll ? (this._watchInFlight(key, !0), this._watchPollTimeouts(key, setTimeout($.proxy(this, "_startListOp", resource, context), watchOpts.pollInterval || 5e3))) :this._watchInFlight(key) || this._startWatchOp(key, resource, context, opts, this._resourceVersion(key));
}
......@@ -1705,10 +1712,10 @@ Logger.log("Watching", ws), self._watchWebsockets(key, ws);
}
}, DataService.prototype._watchOpOnOpen = function(resource, context, opts, event) {
Logger.log("Websocket opened for resource/context", resource, context);
var key = this._uniqueKey(resource, null, context, _.get(opts, "http.params"));
var key = this._uniqueKey(resource, null, context, opts);
this._addWebsocketEvent(key, "open");
}, DataService.prototype._watchOpOnMessage = function(resource, context, opts, event) {
var key = this._uniqueKey(resource, null, context, _.get(opts, "http.params"));
var key = this._uniqueKey(resource, null, context, opts);
opts = opts || {};
var invokeApply = !opts.skipDigest;
try {
......@@ -1723,7 +1730,7 @@ self._watchCallbacks(key).fire(self._data(key), eventData.type, eventData.object
Logger.error("Error processing message", resource, event.data);
}
}, DataService.prototype._watchOpOnClose = function(resource, context, opts, event) {
var eventWS = event.target, key = this._uniqueKey(resource, null, context, _.get(opts, "http.params"));
var eventWS = event.target, key = this._uniqueKey(resource, null, context, opts);
if (!eventWS) return void Logger.log("Skipping reopen, no eventWS in event", event);
var registeredWS = this._watchWebsockets(key);
if (!registeredWS) return void Logger.log("Skipping reopen, no registeredWS for resource/context", resource, context);
......
......@@ -4,6 +4,15 @@
angular.module('openshiftCommonServices')
.factory('DataService', function($cacheFactory, $http, $ws, $rootScope, $q, API_CFG, APIService, Logger, $timeout, base64, base64util) {
// Accept PartialObjectMetadataList. Unfortunately we can't use the Accept
// header to fallback to JSON due to an API server content negotiation bug.
// https://github.com/kubernetes/kubernetes/issues/50519
//
// This is a potential version skew issue for when the web console runs in
// a pod where we potentially need to support different server versions.
// https://trello.com/c/9oaUh8xP
var ACCEPT_PARTIAL_OBJECT_METADATA_LIST = 'application/json;as=PartialObjectMetadataList;v=v1alpha1;g=meta.k8s.io';
function Data(array) {
this._data = {};
this._objectsByAttribute(array, "metadata.name", this._data);
......@@ -150,11 +159,12 @@ angular.module('openshiftCommonServices')
// which includes a helper method for returning a map indexed
// by attribute (e.g. data.by('metadata.name'))
// opts: http - options to pass to the inner $http call
// partialObjectMetadataList - if true, request only the metadata for each object
//
// returns a promise
DataService.prototype.list = function(resource, context, callback, opts) {
resource = APIService.toResourceGroupVersion(resource);
var key = this._uniqueKey(resource, null, context, _.get(opts, 'http.params'));
var key = this._uniqueKey(resource, null, context, opts);
var deferred = this._listDeferred(key);
if (callback) {
deferred.promise.then(callback);
......@@ -416,7 +426,7 @@ angular.module('openshiftCommonServices')
DataService.prototype.get = function(resource, name, context, opts) {
resource = APIService.toResourceGroupVersion(resource);
opts = opts || {};
var key = this._uniqueKey(resource, name, context, _.get(opts, 'http.params'));
var key = this._uniqueKey(resource, name, context, opts);
var force = !!opts.force;
delete opts.force;
......@@ -613,7 +623,7 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
resource = APIService.toResourceGroupVersion(resource);
opts = opts || {};
var invokeApply = !opts.skipDigest;
var key = this._uniqueKey(resource, null, context, _.get(opts, 'http.params'));
var key = this._uniqueKey(resource, null, context, opts);
if (callback) {
// If we were given a callback, add it
this._watchCallbacks(key).add(callback);
......@@ -692,7 +702,7 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
DataService.prototype.watchObject = function(resource, name, context, callback, opts) {
resource = APIService.toResourceGroupVersion(resource);
opts = opts || {};
var key = this._uniqueKey(resource, name, context, _.get(opts, 'http.params'));
var key = this._uniqueKey(resource, name, context, opts);
var wrapperCallback;
if (callback) {
// If we were given a callback, add it
......@@ -733,10 +743,10 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
var callback = handle.callback;
var objectCallback = handle.objectCallback;
var opts = handle.opts;
var key = this._uniqueKey(resource, null, context, _.get(opts, 'http.params'));
var key = this._uniqueKey(resource, null, context, opts);
if (objectCallback && objectName) {
var objectKey = this._uniqueKey(resource, objectName, context, _.get(opts, 'http.params'));
var objectKey = this._uniqueKey(resource, objectName, context, opts);
var objCallbacks = this._watchObjectCallbacks(objectKey);
objCallbacks.remove(objectCallback);
}
......@@ -954,29 +964,43 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
// - ensure namespace if available
// - ensure only witelisted url params used for keys (fieldSelector, labelSelector) via paramsForKey
// and that these are consistently ordered
// - ensure that requests with different Accept request headers have different keys
// - NOTE: Do not use the key as your url for API requests. This function does not use the 'isWebsocket'
// bool. Both websocket & http operations should respond with the same data from cache if key matches
// so the unique key will always include http://
DataService.prototype._uniqueKey = function(resource, name, context, params) {
DataService.prototype._uniqueKey = function(resource, name, context, opts) {
var ns = context && context.namespace ||
_.get(context, 'project.metadata.name') ||
context.projectName;
return this._urlForResource(resource, name, context, null, angular.extend({}, {}, {namespace: ns})).toString() + paramsForKey(params || {});
var params = _.get(opts, 'http.params');
var url = this._urlForResource(resource, name, context, null, angular.extend({}, {}, {namespace: ns})).toString() + paramsForKey(params || {});
if (_.get(opts, 'partialObjectMetadataList')) {
// Make sure partial objects get a different cache key.
return url + '#' + ACCEPT_PARTIAL_OBJECT_METADATA_LIST;
}
return url;
};
DataService.prototype._startListOp = function(resource, context, opts) {
opts = opts || {};
var key = this._uniqueKey(resource, null, context, _.get(opts, 'http.params'));
var key = this._uniqueKey(resource, null, context, opts);
// mark the operation as in progress
this._listInFlight(key, true);
var headers = {};
if (opts.partialObjectMetadataList) {
headers.Accept = ACCEPT_PARTIAL_OBJECT_METADATA_LIST;
}
var self = this;
if (context.projectPromise && !resource.equals("projects")) {
context.projectPromise.done(function(project) {
$http(angular.extend({
method: 'GET',
auth: {},
headers: headers,
url: self._urlForResource(resource, null, context, false, {namespace: project.metadata.name})
}, opts.http || {}))
.success(function(data, status, headerFunc, config, statusText) {
......@@ -1000,6 +1024,7 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
$http({
method: 'GET',
auth: {},
headers: headers,
url: this._urlForResource(resource, null, context),
}).success(function(data, status, headerFunc, config, statusText) {
self._listOpComplete(key, resource, context, opts, data);
......@@ -1044,7 +1069,9 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
var deferred = this._listDeferred(key);
delete this._listDeferredMap[key];
this._resourceVersion(key, data.resourceVersion || data.metadata.resourceVersion);
// Some responses might not have `data.metadata` (for instance, PartialObjectMetadataList).
var resourceVersion = _.get(data, 'resourceVersion') || _.get(data, 'metadata.resourceVersion');
this._resourceVersion(key, resourceVersion);
this._data(key, items);
deferred.resolve(this._data(key));
this._watchCallbacks(key).fire(this._data(key));
......@@ -1109,12 +1136,12 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
DataService.prototype._watchOpOnOpen = function(resource, context, opts, event) {
Logger.log('Websocket opened for resource/context', resource, context);
var key = this._uniqueKey(resource, null, context, _.get(opts, 'http.params'));
var key = this._uniqueKey(resource, null, context, opts);
this._addWebsocketEvent(key, 'open');
};
DataService.prototype._watchOpOnMessage = function(resource, context, opts, event) {
var key = this._uniqueKey(resource, null, context, _.get(opts, 'http.params'));
var key = this._uniqueKey(resource, null, context, opts);
opts = opts || {};
var invokeApply = !opts.skipDigest;
try {
......@@ -1156,7 +1183,7 @@ DataService.prototype.createStream = function(resource, name, context, opts, isR
DataService.prototype._watchOpOnClose = function(resource, context, opts, event) {
var eventWS = event.target;
var key = this._uniqueKey(resource, null, context, _.get(opts, 'http.params'));
var key = this._uniqueKey(resource, null, context, opts);
if (!eventWS) {
Logger.log("Skipping reopen, no eventWS in event", event);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment