From 571bdd7c46f1171896d07963455f228a352ecd90 Mon Sep 17 00:00:00 2001 From: nscuro Date: Sat, 6 Jul 2024 19:50:31 +0200 Subject: [PATCH 1/2] Fix usage of deprecated policy tagging endpoints Replaces usages of `/api/v1/policy/{uuid}/tag/{name}` endpoints with `/api/v1/tag/{name}/policy`, in accordance with https://github.com/DependencyTrack/dependency-track/pull/3924. Also fixes redundant toasts when removing or adding tags from/to policies. Signed-off-by: nscuro --- src/views/policy/PolicyList.vue | 37 +++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/src/views/policy/PolicyList.vue b/src/views/policy/PolicyList.vue index 9b430c7b7..eaf57a03f 100644 --- a/src/views/policy/PolicyList.vue +++ b/src/views/policy/PolicyList.vue @@ -306,10 +306,10 @@ export default { }); }, deleteTagLimiter: function (tagName) { - let url = `${this.$api.BASE_URL}/${this.$api.URL_POLICY}/${this.policy.uuid}/tag/${tagName}`; + let url = `${this.$api.BASE_URL}/${this.$api.URL_TAG}/${encodeURIComponent(tagName)}/policy`; this.axios - .delete(url) - .then((response) => { + .delete(url, { data: [this.policy.uuid] }) + .then(() => { let p = []; for (let i = 0; i < this.tags.length; i++) { if (this.tags[i].name !== tagName) { @@ -318,9 +318,6 @@ export default { } this.tags = p; this.$toastr.s(this.$t('message.updated')); - }) - .catch((error) => { - this.$toastr.w(this.$t('condition.unsuccessful_action')); }); }, updateProjectSelection: function (selections) { @@ -347,19 +344,27 @@ export default { }, updateTagSelection: function (selections) { this.$root.$emit('bv::hide::modal', 'selectTagModal'); + + let promises = []; for (let i = 0; i < selections.length; i++) { let selection = selections[i]; - let url = `${this.$api.BASE_URL}/${this.$api.URL_POLICY}/${this.policy.uuid}/tag/${selection.name}`; - this.axios - .post(url) - .then((response) => { - this.tags.push(selection); - this.$toastr.s(this.$t('message.updated')); - }) - .catch((error) => { - this.$toastr.w(this.$t('condition.unsuccessful_action')); - }); + let url = `${this.$api.BASE_URL}/${this.$api.URL_TAG}/${encodeURIComponent(selection.name)}/policy`; + promises.push( + this.axios + .post(url, [this.policy.uuid]) + .then(() => Promise.resolve(selection.name)), + ); } + + Promise.all(promises).then((addedTagNames) => { + for (const tagName of addedTagNames) { + if (!this.tags.some((tag) => tag.name === tagName)) { + this.tags.push({ name: tagName }); + } + } + + this.$toastr.s(this.$t('message.updated')); + }); }, updateIncludeChildren: function () { let url = `${this.$api.BASE_URL}/${this.$api.URL_POLICY}`; From db3945d705c188046397e3002463426dac0bd938 Mon Sep 17 00:00:00 2001 From: nscuro Date: Sat, 6 Jul 2024 19:52:02 +0200 Subject: [PATCH 2/2] Add ability to untag policies from tags view Signed-off-by: nscuro --- .../tags/TaggedPoliciesListModal.vue | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/views/portfolio/tags/TaggedPoliciesListModal.vue b/src/views/portfolio/tags/TaggedPoliciesListModal.vue index 54987280b..ec71a4210 100644 --- a/src/views/portfolio/tags/TaggedPoliciesListModal.vue +++ b/src/views/portfolio/tags/TaggedPoliciesListModal.vue @@ -37,6 +37,11 @@ export default { apiUrl: function () { return `${this.$api.BASE_URL}/${this.$api.URL_TAG}/${this.tag}/policy`; }, + untag: function (policyUuids) { + return this.axios.delete(this.apiUrl(), { + data: policyUuids, + }); + }, refreshTable: function () { this.$refs.table.refresh({ url: this.apiUrl(), @@ -45,6 +50,21 @@ export default { }); }, }, + mounted() { + // NB: Because this modal is loaded dynamically from TagList, + // this.$refs.table may still be undefined when mounted() is called. + // https://jefrydco.id/en/blog/safe-access-vue-refs-undefined + const interval = setInterval(() => { + if (this.$refs.table) { + this.$refs.table.refreshOptions({ + showBtnDeleteSelected: this.isPermitted( + this.PERMISSIONS.POLICY_MANAGEMENT, + ), + }); + clearInterval(interval); + } + }, 50); + }, data() { return { labelIcon: { @@ -52,6 +72,11 @@ export default { dataOff: '\u2715', }, columns: [ + { + field: 'state', + checkbox: true, + align: 'center', + }, { title: this.$t('message.name'), field: 'name', @@ -63,6 +88,31 @@ export default { ], data: [], options: { + buttons: { + btnDeleteSelected: { + icon: 'fa fa-minus', + attributes: { + title: this.$t('message.unassign_tag_from_selection'), + }, + event: () => { + let selected = this.$refs.table.getSelections(); + if ( + !selected || + (Array.isArray(selected) && selected.length === 0) + ) { + this.$toastr.w(this.$t('message.empty_selection')); + return; + } + + this.untag(selected.map((row) => row.uuid)).then(() => { + this.$toastr.s(this.$t('message.tag_unassigned_successfully')); + this.refreshTable(); + }); + }, + }, + }, + buttonsOrder: ['btnDeleteSelected', 'refresh', 'columns'], + clickToSelect: true, search: true, showColumns: true, showRefresh: true,