Skip to content

Commit e686120

Browse files
authored
Merge pull request #8309 from mook-as/rdx/upgrade
RDX: Marketplace: support upgrade
2 parents bcd49c3 + 2217948 commit e686120

File tree

5 files changed

+211
-93
lines changed

5 files changed

+211
-93
lines changed

pkg/rancher-desktop/assets/translations/en-us.yaml

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -190,13 +190,14 @@ marketplace:
190190
banners:
191191
install: Extension {name} has been installed.
192192
uninstall: Extension {name} has been removed.
193-
sidebar:
194-
installButton:
195-
label: Install
196-
loading: Installing...
197-
uninstallButton:
198-
label: Remove
199-
loading: Removing...
193+
labels:
194+
install: Install
195+
uninstall: Remove
196+
upgrade: Upgrade
197+
loading:
198+
install: Installing...
199+
uninstall: Removing...
200+
upgrade: Upgrading...
200201
moreInfo: More information
201202
containers:
202203
title: Containers
@@ -4864,6 +4865,7 @@ extensions:
48644865
icon: icon-extension
48654866
installed:
48664867
list:
4868+
upgrade: Upgrade
48674869
uninstall: Remove
48684870
emptyState:
48694871
icon: icon-extension

pkg/rancher-desktop/components/MarketplaceCard.vue

Lines changed: 76 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -33,35 +33,58 @@
3333
>
3434
{{ error }}
3535
</Banner>
36+
<!-- install button -->
3637
<button
37-
v-if="!error"
38+
v-if="!error && !currentAction && !installedVersion"
3839
data-test="button-install"
39-
:class="isInstalled ? 'role-danger': 'role-primary'"
40-
class="btn btn-xs"
41-
:disabled="loading"
42-
@click="appInstallation(installationAction)"
40+
class="role-primary btn btn-xs"
41+
@click="appInstallation('install')"
4342
>
44-
<span
45-
v-if="loading"
46-
name="loading"
47-
:is-loading="loading"
48-
>
49-
<loading-indicator>{{ buttonLabel }}</loading-indicator>
43+
{{ t('marketplace.labels.install') }}
44+
</button>
45+
<!-- upgrade button -->
46+
<button
47+
v-if="!error && !currentAction && canUpgrade"
48+
class="role-primary btn btn-xs"
49+
@click="appInstallation('upgrade')"
50+
>
51+
{{ t('marketplace.labels.upgrade') }}
52+
</button>
53+
<!-- uninstall button -->
54+
<button
55+
v-if="!error && !currentAction && installedVersion"
56+
data-test="button-uninstall"
57+
class="role-danger btn btn-xs"
58+
@click="appInstallation('uninstall')"
59+
>
60+
{{ t('marketplace.labels.uninstall') }}
61+
</button>
62+
<!-- "loading" fake button -->
63+
<button
64+
v-if="!error && currentAction"
65+
data-test="button-loading"
66+
class="role-primary btn btn-xs"
67+
disabled="true"
68+
>
69+
<span name="loading" is-loading="true">
70+
<loading-indicator>{{ loadingLabel }}</loading-indicator>
5071
</span>
51-
<span v-if="!loading">{{ buttonLabel }}</span>
5272
</button>
5373
</div>
5474
</div>
5575
</template>
5676

5777
<script lang="ts">
5878
import { Banner } from '@rancher/components';
79+
import semver from 'semver';
5980
6081
import LoadingIndicator from '@pkg/components/LoadingIndicator.vue';
6182
import { MarketplaceData } from '@pkg/store/extensions.js';
6283
6384
import type { PropType } from 'vue';
6485
86+
type action = 'install' | 'uninstall' | 'upgrade';
87+
6588
export default {
6689
components: { LoadingIndicator, Banner },
6790
props: {
@@ -77,19 +100,21 @@ export default {
77100
type: Boolean,
78101
required: true,
79102
},
103+
installedVersion: {
104+
type: String,
105+
required: false,
106+
default: undefined,
107+
},
80108
},
81109
data() {
82110
return {
83-
loading: false,
84-
error: null as string | null,
85-
response: null,
86-
bannerActive: false,
111+
currentAction: null as null | action,
112+
error: null as string | null,
113+
response: null,
114+
bannerActive: false,
87115
};
88116
},
89117
computed: {
90-
installationAction() {
91-
return this.isInstalled ? 'uninstall' : 'install';
92-
},
93118
versionedExtension() {
94119
return `${ this.extensionWithoutVersion }:${ this.extension.version }`;
95120
},
@@ -101,52 +126,45 @@ export default {
101126
extensionLink() {
102127
return this.extension.slug.startsWith('ghcr.io/') ? `https://${ this.extension.slug }` : `https://hub.docker.com/extensions/${ this.extension.slug }`;
103128
},
104-
buttonLabel() {
105-
if (this.loading) {
106-
return this.isInstalled ? this.t('marketplace.sidebar.uninstallButton.loading') : this.t('marketplace.sidebar.installButton.loading');
107-
} else {
108-
return this.isInstalled ? this.t('marketplace.sidebar.uninstallButton.label') : this.t('marketplace.sidebar.installButton.label');
129+
canUpgrade() {
130+
try {
131+
return this.installedVersion && semver.gt(this.extension.version, this.installedVersion);
132+
} catch {
133+
// If installed or available version is not semver.
134+
return false;
109135
}
110136
},
137+
loadingLabel() {
138+
return this.t(`marketplace.loading.${ this.currentAction }`);
139+
},
111140
},
112141
113142
methods: {
114143
resetBanners() {
115144
this.error = null;
116145
},
117-
appInstallation(action: 'uninstall' | 'install') {
118-
this.loading = true;
146+
async appInstallation(action: action) {
147+
this.currentAction = action;
119148
this.resetBanners();
120-
const extensionId = action === 'uninstall' ? this.extensionWithoutVersion : this.versionedExtension;
121-
122-
fetch(
123-
`http://localhost:${ this.credentials?.port }/v1/extensions/${ action }?id=${ extensionId }`,
124-
{
125-
method: 'POST',
126-
headers: new Headers({
127-
Authorization: `Basic ${ window.btoa(
128-
`${ this.credentials?.user }:${ this.credentials?.password }`,
129-
) }`,
130-
'Content-Type': 'application/x-www-form-urlencoded',
131-
}),
132-
},
133-
).then((r) => {
134-
if (!r.ok) {
135-
this.error = r.statusText;
136-
this.loading = false;
137-
}
149+
const id = action === 'uninstall' ? this.extensionWithoutVersion : this.versionedExtension;
150+
const verb = action === 'uninstall' ? 'uninstall' : 'install'; // upgrades are installs
151+
152+
try {
153+
const result = await this.$store.dispatch(`extensions/${ verb }`, { id });
138154
139-
if (r.status === 201) {
140-
this.loading = false;
155+
if (typeof result === 'string') {
156+
this.error = result;
157+
this.currentAction = null;
158+
} else if (result) {
159+
this.currentAction = null;
141160
}
142-
})
143-
.finally(() => {
144-
this.$emit('update:extension');
145-
146-
setTimeout(() => {
147-
this.resetBanners();
148-
}, 3000);
149-
});
161+
} finally {
162+
this.$emit('update:extension');
163+
164+
setTimeout(() => {
165+
this.resetBanners();
166+
}, 3_000);
167+
}
150168
},
151169
},
152170
};
@@ -206,6 +224,10 @@ export default {
206224
.banner {
207225
margin: 0;
208226
}
227+
228+
button:not(:first-of-type) {
229+
margin-left: 10px;
230+
}
209231
}
210232
211233
&-more-info {

pkg/rancher-desktop/components/MarketplaceCatalog.vue

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ export default (Vue as VueConstructor<Vue & VuexBindings>).extend({
9393
9494
return available && installed && semver.gt(available.version, installed.version);
9595
},
96+
installedVersion(slug: string) {
97+
return this.installedExtensions.find(item => item.id === slug)?.version;
98+
},
9699
isAllowed(slug: string) {
97100
return !this.allowedListEnabled || this.allowedExtensions.includes(slug);
98101
},
@@ -126,6 +129,7 @@ export default (Vue as VueConstructor<Vue & VuexBindings>).extend({
126129
:extension="item"
127130
:data-test="`extension-card-${item.title.toLowerCase()}`"
128131
:is-installed="item.installed"
132+
:installed-version="installedVersion(item.slug)"
129133
:credentials="credentials"
130134
@update:extension="isInstalled"
131135
/>

0 commit comments

Comments
 (0)