This is an automated email from the ASF dual-hosted git repository.
joao pushed a commit to branch 4.19
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/4.19 by this push:
new 49cd5ba64ad Fix link to removed volumes being shown in info card and
list view (#8833)
49cd5ba64ad is described below
commit 49cd5ba64ad6f3a145f03592903d07a8dffa4578
Author: Fabricio Duarte <[email protected]>
AuthorDate: Wed Jul 24 09:09:07 2024 -0300
Fix link to removed volumes being shown in info card and list view (#8833)
* Framework for validating links in the front-end
* Rename valid links map in the list view
---
.../org/apache/cloudstack/api/ApiConstants.java | 1 +
.../cloudstack/api/response/SnapshotResponse.java | 8 +++++
.../META-INF/db/views/cloud.snapshot_view.sql | 1 +
.../main/java/com/cloud/api/ApiResponseHelper.java | 1 +
.../cloud/api/query/dao/SnapshotJoinDaoImpl.java | 1 +
.../com/cloud/api/query/vo/SnapshotJoinVO.java | 8 +++++
ui/src/components/view/InfoCard.vue | 8 +++--
ui/src/components/view/ListView.vue | 16 +++++++++-
ui/src/utils/links.js | 36 ++++++++++++++++++++++
9 files changed, 77 insertions(+), 3 deletions(-)
diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
index 2324b861830..d6099ac4717 100644
--- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
+++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
@@ -576,6 +576,7 @@ public class ApiConstants {
public static final String AGGREGATE_NAME = "aggregatename";
public static final String POOL_NAME = "poolname";
public static final String VOLUME_NAME = "volumename";
+ public static final String VOLUME_STATE = "volumestate";
public static final String SNAPSHOT_POLICY = "snapshotpolicy";
public static final String SNAPSHOT_RESERVATION = "snapshotreservation";
public static final String IP_NETWORK_LIST = "iptonetworklist";
diff --git
a/api/src/main/java/org/apache/cloudstack/api/response/SnapshotResponse.java
b/api/src/main/java/org/apache/cloudstack/api/response/SnapshotResponse.java
index e160f64ebe9..02132416b8a 100644
--- a/api/src/main/java/org/apache/cloudstack/api/response/SnapshotResponse.java
+++ b/api/src/main/java/org/apache/cloudstack/api/response/SnapshotResponse.java
@@ -71,6 +71,10 @@ public class SnapshotResponse extends
BaseResponseWithTagInformation implements
@Param(description = "type of the disk volume")
private String volumeType;
+ @SerializedName(ApiConstants.VOLUME_STATE)
+ @Param(description = "state of the disk volume")
+ private String volumeState;
+
@SerializedName(ApiConstants.CREATED)
@Param(description = " the date the snapshot was created")
private Date created;
@@ -199,6 +203,10 @@ public class SnapshotResponse extends
BaseResponseWithTagInformation implements
this.volumeType = volumeType;
}
+ public void setVolumeState(String volumeState) {
+ this.volumeState = volumeState;
+ }
+
public void setCreated(Date created) {
this.created = created;
}
diff --git
a/engine/schema/src/main/resources/META-INF/db/views/cloud.snapshot_view.sql
b/engine/schema/src/main/resources/META-INF/db/views/cloud.snapshot_view.sql
index c6b8d6b4d05..d0eddc1fc4b 100644
--- a/engine/schema/src/main/resources/META-INF/db/views/cloud.snapshot_view.sql
+++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.snapshot_view.sql
@@ -48,6 +48,7 @@ SELECT
`volumes`.`uuid` AS `volume_uuid`,
`volumes`.`name` AS `volume_name`,
`volumes`.`volume_type` AS `volume_type`,
+ `volumes`.`state` AS `volume_state`,
`volumes`.`size` AS `volume_size`,
`data_center`.`id` AS `data_center_id`,
`data_center`.`uuid` AS `data_center_uuid`,
diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java
b/server/src/main/java/com/cloud/api/ApiResponseHelper.java
index e801d1f9b31..eec767d7b5e 100644
--- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java
+++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java
@@ -659,6 +659,7 @@ public class ApiResponseHelper implements ResponseGenerator
{
snapshotResponse.setVolumeId(volume.getUuid());
snapshotResponse.setVolumeName(volume.getName());
snapshotResponse.setVolumeType(volume.getVolumeType().name());
+ snapshotResponse.setVolumeState(volume.getState().name());
snapshotResponse.setVirtualSize(volume.getSize());
DataCenter zone =
ApiDBUtils.findZoneById(volume.getDataCenterId());
if (zone != null) {
diff --git
a/server/src/main/java/com/cloud/api/query/dao/SnapshotJoinDaoImpl.java
b/server/src/main/java/com/cloud/api/query/dao/SnapshotJoinDaoImpl.java
index b08fb4529f4..8b951c174f4 100644
--- a/server/src/main/java/com/cloud/api/query/dao/SnapshotJoinDaoImpl.java
+++ b/server/src/main/java/com/cloud/api/query/dao/SnapshotJoinDaoImpl.java
@@ -128,6 +128,7 @@ public class SnapshotJoinDaoImpl extends
GenericDaoBaseWithTagInformation<Snapsh
snapshotResponse.setVolumeId(snapshot.getVolumeUuid());
snapshotResponse.setVolumeName(snapshot.getVolumeName());
snapshotResponse.setVolumeType(snapshot.getVolumeType().name());
+ snapshotResponse.setVolumeState(snapshot.getVolumeState().name());
snapshotResponse.setVirtualSize(snapshot.getVolumeSize());
VolumeVO volume =
ApiDBUtils.findVolumeById(snapshot.getVolumeId());
if (volume != null && volume.getVolumeType() == Type.ROOT &&
volume.getInstanceId() != null) {
diff --git a/server/src/main/java/com/cloud/api/query/vo/SnapshotJoinVO.java
b/server/src/main/java/com/cloud/api/query/vo/SnapshotJoinVO.java
index 9ec74dac128..2dc2f7a810f 100644
--- a/server/src/main/java/com/cloud/api/query/vo/SnapshotJoinVO.java
+++ b/server/src/main/java/com/cloud/api/query/vo/SnapshotJoinVO.java
@@ -130,6 +130,10 @@ public class SnapshotJoinVO extends
BaseViewWithTagInformationVO implements Cont
@Enumerated(EnumType.STRING)
Volume.Type volumeType = Volume.Type.UNKNOWN;
+ @Column(name = "volume_state")
+ @Enumerated(EnumType.STRING)
+ Volume.State volumeState;
+
@Column(name = "volume_size")
Long volumeSize;
@@ -297,6 +301,10 @@ public class SnapshotJoinVO extends
BaseViewWithTagInformationVO implements Cont
return volumeType;
}
+ public Volume.State getVolumeState() {
+ return volumeState;
+ }
+
public Long getVolumeSize() {
return volumeSize;
}
diff --git a/ui/src/components/view/InfoCard.vue
b/ui/src/components/view/InfoCard.vue
index bb6726d75f1..98c26bf5565 100644
--- a/ui/src/components/view/InfoCard.vue
+++ b/ui/src/components/view/InfoCard.vue
@@ -442,7 +442,8 @@
<div class="resource-detail-item__label">{{ $t('label.volume')
}}</div>
<div class="resource-detail-item__details">
<hdd-outlined />
- <router-link :to="{ path: '/volume/' + resource.volumeid }">{{
resource.volumename || resource.volume || resource.volumeid }} </router-link>
+ <router-link v-if="validLinks.volume" :to="{ path: '/volume/' +
resource.volumeid }">{{ resource.volumename || resource.volume ||
resource.volumeid }} </router-link>
+ <span v-else>{{ resource.volumename || resource.volume ||
resource.volumeid }}</span>
</div>
</div>
<div class="resource-detail-item" v-if="resource.associatednetworkid">
@@ -783,6 +784,7 @@
<script>
import { api } from '@/api'
import { createPathBasedOnVmType } from '@/utils/plugins'
+import { validateLinks } from '@/utils/links'
import Console from '@/components/widgets/Console'
import OsLogo from '@/components/widgets/OsLogo'
import Status from '@/components/widgets/Status'
@@ -848,7 +850,8 @@ export default {
vpc: '',
network: ''
},
- newResource: {}
+ newResource: {},
+ validLinks: {}
}
},
watch: {
@@ -865,6 +868,7 @@ export default {
this.newResource = newData
this.showKeys = false
this.setData()
+ this.validLinks = validateLinks(this.$router, this.isStatic,
this.resource)
if ('apikey' in this.resource) {
this.getUserKeys()
diff --git a/ui/src/components/view/ListView.vue
b/ui/src/components/view/ListView.vue
index cf3d9361638..8512dab7c05 100644
--- a/ui/src/components/view/ListView.vue
+++ b/ui/src/components/view/ListView.vue
@@ -167,7 +167,8 @@
<router-link :to="{ path: getVmRouteUsingType(record) +
record.virtualmachineid }">{{ text }}</router-link>
</template>
<template v-if="column.key === 'volumename'">
- <router-link :to="{ path: '/volume/' + record.volumeid }">{{ text
}}</router-link>
+ <router-link v-if="resourceIdToValidLinksMap[record.id]?.volume"
:to="{ path: '/volume/' + record.volumeid }">{{ text }}</router-link>
+ <span v-else>{{ text }}</span>
</template>
<template v-if="column.key === 'size'">
<span v-if="text && $route.path === '/kubernetes'">
@@ -488,6 +489,7 @@ import TooltipButton from
'@/components/widgets/TooltipButton'
import ResourceIcon from '@/components/view/ResourceIcon'
import ResourceLabel from '@/components/widgets/ResourceLabel'
import { createPathBasedOnVmType } from '@/utils/plugins'
+import { validateLinks } from '@/utils/links'
import cronstrue from 'cronstrue/i18n'
import moment from 'moment-timezone'
@@ -576,6 +578,18 @@ export default {
notification: 'storageallocatedthreshold',
disable: 'storageallocateddisablethreshold'
}
+ },
+ resourceIdToValidLinksMap: {}
+ }
+ },
+ watch: {
+ items: {
+ deep: true,
+ handler (newData, oldData) {
+ if (newData === oldData) return
+ this.items.forEach(record => {
+ this.resourceIdToValidLinksMap[record.id] =
validateLinks(this.$router, false, record)
+ })
}
}
},
diff --git a/ui/src/utils/links.js b/ui/src/utils/links.js
new file mode 100644
index 00000000000..fa650cdd7b8
--- /dev/null
+++ b/ui/src/utils/links.js
@@ -0,0 +1,36 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+export function validateLinks (router, isStatic, resource) {
+ const validLinks = {
+ volume: false
+ }
+
+ if (isStatic) {
+ return validLinks
+ }
+
+ if (resource.volumeid && router.resolve('/volume/' +
resource.volumeid).matched[0].redirect !== '/exception/404') {
+ if (resource.volumestate) {
+ validLinks.volume = resource.volumestate !== 'Expunged'
+ } else {
+ validLinks.volume = true
+ }
+ }
+
+ return validLinks
+}