Add UI for editing labels on a manifest
This commit is contained in:
parent
46d1532f0e
commit
cfb81c977f
7 changed files with 154 additions and 4 deletions
|
@ -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],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -20,4 +20,9 @@
|
|||
list-style: none;
|
||||
display: inline-block;
|
||||
margin: 4px;
|
||||
}
|
||||
|
||||
.tag-operations-dialog .label-section {
|
||||
margin-bottom: 10px;
|
||||
padding: 6px;
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
|
|
Reference in a new issue