Add UI for editing labels on a manifest

This commit is contained in:
Joseph Schorr 2017-03-09 19:04:35 -05:00
parent 46d1532f0e
commit cfb81c977f
7 changed files with 154 additions and 4 deletions

View file

@ -49,9 +49,9 @@ class RepositoryManifestLabels(RepositoryParamResource):
'description': 'The value for the label',
},
'media_type': {
'type': ['string'],
'type': ['string', 'null'],
'description': 'The media type for this label',
'enum': ALLOWED_LABEL_MEDIA_TYPES,
'enum': ALLOWED_LABEL_MEDIA_TYPES + [None],
},
},
},

View file

@ -20,4 +20,9 @@
list-style: none;
display: inline-block;
margin: 4px;
}
.tag-operations-dialog .label-section {
margin-bottom: 10px;
padding: 6px;
}

View file

@ -232,6 +232,10 @@
<span class="cor-option" option-click="askAddTag(tag)">
<i class="fa fa-plus"></i> Add New Tag
</span>
<span class="cor-option" option-click="showLabelEditor(tag)"
ng-if="tag.manifest_digest">
<i class="fa fa-tags"></i> Edit Labels
</span>
<span class="cor-option" option-click="askDeleteTag(tag.name)">
<i class="fa fa-times"></i> Delete Tag
</span>
@ -272,6 +276,7 @@
<div class="tag-operations-dialog" repository="repository"
image-loader="imageLoader"
action-handler="tagActionHandler"></div>
action-handler="tagActionHandler"
labels-changed="handleLabelsChanged(manifest_digest)"></div>
<div class="fetch-tag-dialog" repository="repository" action-handler="fetchTagActionHandler"></div>

View file

@ -18,6 +18,27 @@
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<!-- Edit Labels Dialog -->
<div class="cor-confirm-dialog"
dialog-context="editLabelsInfo"
dialog-action="editLabels(info, callback)"
dialog-title="Edit Manifest Labels"
dialog-action-title="Edit Labels">
<div class="cor-loader" ng-if="editLabelsInfo.loading"></div>
<div ng-if="!editLabelsInfo.loading && editLabelsInfo.labels">
<div><strong>Read-only labels:</strong></div>
<div class="label-section">
<div class="label-list" labels="editLabelsInfo.readonly_labels"></div>
</div>
<div><strong>Mutable labels:</strong></div>
<div class="label-section">
<div class="label-input" labels="editLabelsInfo.mutable_labels"
updated-labels="editLabelsInfo.updated_labels"></div>
</div>
</div>
</div>
<!-- Add Tag Dialog -->
<div class="modal fade" id="createOrMoveTagModal">

View file

@ -336,6 +336,11 @@ angular.module('quay').directive('repoPanelTags', function () {
$scope.tagActionHandler.askAddTag(tag.image_id);
};
$scope.showLabelEditor = function(tag) {
if (!tag.manifest_digest) { return; }
$scope.tagActionHandler.showLabelEditor(tag.manifest_digest);
};
$scope.orderBy = function(predicate) {
if (predicate == $scope.options.predicate) {
$scope.options.reverse = !$scope.options.reverse;
@ -393,6 +398,10 @@ angular.module('quay').directive('repoPanelTags', function () {
return names.join(',');
};
$scope.handleLabelsChanged = function(manifest_digest) {
delete $scope.labelCache[manifest_digest];
};
}
};
return directiveDefinitionObject;

View file

@ -46,6 +46,12 @@ angular.module('quay').directive('manifestLabelList', function () {
});
};
$scope.$watch('cache', function(cache) {
if (cache && $scope.manifestDigest && $scope.labels && !cache[$scope.manifestDigest]) {
loadLabels();
}
}, true);
$scope.$watch('repository', loadLabels);
$scope.$watch('manifestDigest', loadLabels);
}

View file

@ -13,7 +13,8 @@ angular.module('quay').directive('tagOperationsDialog', function () {
'repository': '=repository',
'actionHandler': '=actionHandler',
'imageLoader': '=imageLoader',
'tagChanged': '&tagChanged'
'tagChanged': '&tagChanged',
'labelsChanged': '&labelsChanged'
},
controller: function($scope, $element, $timeout, ApiService) {
$scope.addingTag = false;
@ -138,6 +139,86 @@ angular.module('quay').directive('tagOperationsDialog', function () {
}, errorHandler);
};
$scope.editLabels = function(info, callback) {
var actions = [];
var existingMutableLabels = {};
// Build the set of adds and deletes.
info['updated_labels'].forEach(function(label) {
if (label['id']) {
existingMutableLabels[label['id']] = true;
} else {
actions.push({
'action': 'add',
'label': label
});
}
});
info['mutable_labels'].forEach(function(label) {
if (!existingMutableLabels[label['id']]) {
actions.push({
'action': 'delete',
'label': label
})
}
});
// Execute the add and delete label actions.
var currentIndex = 0;
var performAction = function() {
if (currentIndex >= actions.length) {
$scope.labelsChanged({'manifest_digest': info['manifest_digest']});
callback(true);
return;
}
var currentAction = actions[currentIndex];
currentIndex++;
var errorHandler = ApiService.errorDisplay('Could not update labels', callback);
switch (currentAction.action) {
case 'add':
var params = {
'repository': $scope.repository.namespace + '/' + $scope.repository.name,
'manifestref': info['manifest_digest']
};
var pieces = currentAction['label']['keyValue'].split('=', 2);
var data = {
'key': pieces[0],
'value': pieces[1],
'media_type': null // Have backend infer the media type
};
ApiService.addManifestLabel(data, params).then(performAction, errorHandler);
break;
case 'delete':
var params = {
'repository': $scope.repository.namespace + '/' + $scope.repository.name,
'manifestref': info['manifest_digest'],
'labelid': currentAction['label']['id']
};
ApiService.deleteManifestLabel(null, params).then(performAction, errorHandler);
break;
}
};
performAction();
};
var filterLabels = function(labels, readOnly) {
if (!labels) { return []; }
return labels.filter(function(label) {
return (label['source_type'] != 'api') == readOnly;
});
};
$scope.actionHandler = {
'askDeleteTag': function(tag) {
$scope.deleteTagInfo = {
@ -159,6 +240,29 @@ angular.module('quay').directive('tagOperationsDialog', function () {
$element.find('#createOrMoveTagModal').modal('show');
},
'showLabelEditor': function(manifest_digest) {
$scope.editLabelsInfo = {
'manifest_digest': manifest_digest,
'loading': true
};
var params = {
'repository': $scope.repository.namespace + '/' + $scope.repository.name,
'manifestref': manifest_digest
};
ApiService.listManifestLabels(null, params).then(function(resp) {
var labels = resp['labels'];
$scope.editLabelsInfo['readonly_labels'] = filterLabels(labels, true);
$scope.editLabelsInfo['mutable_labels'] = filterLabels(labels, false);
$scope.editLabelsInfo['labels'] = labels;
$scope.editLabelsInfo['loading'] = false;
}, ApiService.errorDisplay('Could not load manifest labels'));
},
'askRestoreTag': function(tag, image_id, opt_manifest_digest) {
if (tag.image_id == image_id) {
bootbox.alert('This is the current image for the tag');