Skip to content

Commit e7ad16b

Browse files
author
amvanbaren
committed
Fix change namespace action
Rename and copy namespace logo Remove cached namespace details Improve namespace detail form UX
1 parent c1f83fd commit e7ad16b

13 files changed

+151
-56
lines changed

server/src/main/java/org/eclipse/openvsx/UserAPI.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,6 @@ public ResponseEntity<ResultJson> updateNamespaceDetailsLogo(
264264
) {
265265
try {
266266
return ResponseEntity.ok()
267-
.cacheControl(CacheControl.maxAge(10, TimeUnit.MINUTES).cachePublic())
268267
.body(users.updateNamespaceDetailsLogo(namespace, file));
269268
} catch (ErrorResultException exc) {
270269
return exc.toResponseEntity(ResultJson.class);

server/src/main/java/org/eclipse/openvsx/UserService.java

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838

3939
import java.io.IOException;
4040
import java.nio.file.Files;
41+
import java.util.List;
4142
import java.util.Objects;
4243
import java.util.UUID;
4344

@@ -237,6 +238,12 @@ public ResultJson updateNamespaceDetails(NamespaceDetailsJson details) {
237238
if(!Objects.equals(details.getSocialLinks(), namespace.getSocialLinks())) {
238239
namespace.setSocialLinks(details.getSocialLinks());
239240
}
241+
if(StringUtils.isEmpty(details.getLogo()) && StringUtils.isNotEmpty(namespace.getLogoName())) {
242+
storageUtil.removeNamespaceLogo(namespace);
243+
namespace.clearLogoBytes();
244+
namespace.setLogoName(null);
245+
namespace.setLogoStorageType(null);
246+
}
240247

241248
return ResultJson.success("Updated details for namespace " + details.getName());
242249
}
@@ -257,15 +264,12 @@ public ResultJson updateNamespaceDetailsLogo(String namespaceName, MultipartFile
257264
var tika = new Tika();
258265
var detectedType = tika.detect(file.getInputStream(), file.getOriginalFilename());
259266
var logoType = MimeTypes.getDefaultMimeTypes().getRegisteredMimeType(detectedType);
260-
if(logoType != null) {
261-
if(!logoType.getType().equals(MediaType.image("png")) && !logoType.getType().equals(MediaType.image("jpg"))) {
262-
throw new ErrorResultException("Namespace logo should be of png or jpg type");
263-
}
264-
265-
var logoName = "logo-" + namespace.getName() + "-" + System.currentTimeMillis() + logoType.getExtension();
266-
namespace.setLogoName(logoName);
267+
var expectedLogoTypes = List.of(MediaType.image("png"), MediaType.image("jpg"));
268+
if(logoType == null || !expectedLogoTypes.contains(logoType.getType())) {
269+
throw new ErrorResultException("Namespace logo should be a png or jpg file");
267270
}
268271

272+
namespace.setLogoName(NamingUtil.toLogoName(namespace, logoType));
269273
file.getInputStream().transferTo(out);
270274
logoFile.setNamespace(namespace);
271275
storageUtil.uploadNamespaceLogo(logoFile);

server/src/main/java/org/eclipse/openvsx/admin/ChangeNamespaceJobRequestHandler.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
* ****************************************************************************** */
1010
package org.eclipse.openvsx.admin;
1111

12+
import org.apache.commons.lang3.StringUtils;
1213
import org.eclipse.openvsx.ExtensionValidator;
1314
import org.eclipse.openvsx.entities.Extension;
1415
import org.eclipse.openvsx.entities.ExtensionVersion;
@@ -104,6 +105,11 @@ private void execute(ChangeNamespaceJobRequest jobRequest) {
104105
})
105106
.collect(Collectors.toList());
106107

108+
if(StringUtils.isNotEmpty(oldNamespace.getLogoName())) {
109+
newNamespace.setLogoName(NamingUtil.changeLogoName(oldNamespace, newNamespace));
110+
storageUtil.copyNamespaceLogo(oldNamespace, newNamespace);
111+
}
112+
107113
service.changeNamespaceInDatabase(newNamespace, oldNamespace, updatedResources, createNewNamespace, json.removeOldNamespace());
108114

109115
// remove the old resources from external storage

server/src/main/java/org/eclipse/openvsx/admin/ChangeNamespaceService.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ public void changeNamespaceInDatabase(
7272
entityManager.remove(oldNamespace);
7373
}
7474

75+
cache.evictSitemap();
76+
cache.evictNamespaceDetails(oldNamespace);
7577
search.updateSearchEntries(extensions.toList());
7678
}
7779

server/src/main/java/org/eclipse/openvsx/cache/CacheService.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,7 @@
99
* ****************************************************************************** */
1010
package org.eclipse.openvsx.cache;
1111

12-
import org.eclipse.openvsx.entities.Extension;
13-
import org.eclipse.openvsx.entities.ExtensionVersion;
14-
import org.eclipse.openvsx.entities.FileResource;
15-
import org.eclipse.openvsx.entities.UserData;
12+
import org.eclipse.openvsx.entities.*;
1613
import org.eclipse.openvsx.repositories.RepositoryService;
1714
import org.eclipse.openvsx.util.TargetPlatform;
1815
import org.eclipse.openvsx.util.VersionAlias;
@@ -59,17 +56,28 @@ public CacheService(
5956
this.filesCacheKeyGenerator = filesCacheKeyGenerator;
6057
}
6158

59+
public void evictSitemap() {
60+
invalidateCache(CACHE_SITEMAP);
61+
}
62+
6263
public void evictNamespaceDetails() {
6364
invalidateCache(CACHE_NAMESPACE_DETAILS_JSON);
6465
}
6566

67+
public void evictNamespaceDetails(Namespace namespace) {
68+
evictNamespaceDetails(namespace.getName());
69+
}
70+
6671
public void evictNamespaceDetails(Extension extension) {
72+
evictNamespaceDetails(extension.getNamespace().getName());
73+
}
74+
75+
private void evictNamespaceDetails(String namespaceName) {
6776
var cache = cacheManager.getCache(CACHE_NAMESPACE_DETAILS_JSON);
6877
if(cache == null) {
6978
return; // cache is not created
7079
}
7180

72-
var namespaceName = extension.getNamespace().getName();
7381
cache.evictIfPresent(namespaceName);
7482
}
7583

server/src/main/java/org/eclipse/openvsx/storage/AwsStorageService.java

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -222,18 +222,23 @@ public TempFile downloadFile(FileResource resource) throws IOException {
222222

223223
@Override
224224
public void copyFiles(List<Pair<FileResource, FileResource>> pairs) {
225-
for(var pair : pairs) {
226-
var oldObjectKey = getObjectKey(pair.getFirst());
227-
var newObjectKey = getObjectKey(pair.getSecond());
228-
var request = CopyObjectRequest.builder()
229-
.sourceBucket(bucket)
230-
.sourceKey(oldObjectKey)
231-
.destinationBucket(bucket)
232-
.destinationKey(newObjectKey)
233-
.build();
225+
pairs.forEach(pair -> copy(getObjectKey(pair.getFirst()), getObjectKey(pair.getSecond())));
226+
}
234227

235-
getS3Client().copyObject(request);
236-
}
228+
@Override
229+
public void copyNamespaceLogo(Namespace oldNamespace, Namespace newNamespace) {
230+
copy(getObjectKey(oldNamespace), getObjectKey(newNamespace));
231+
}
232+
233+
private void copy(String oldObjectKey, String newObjectKey) {
234+
var request = CopyObjectRequest.builder()
235+
.sourceBucket(bucket)
236+
.sourceKey(oldObjectKey)
237+
.destinationBucket(bucket)
238+
.destinationKey(newObjectKey)
239+
.build();
240+
241+
getS3Client().copyObject(request);
237242
}
238243

239244
@Override

server/src/main/java/org/eclipse/openvsx/storage/AzureBlobStorageService.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,19 @@ public void copyFiles(List<Pair<FileResource,FileResource>> pairs) {
215215
}
216216
}
217217

218+
@Override
219+
public void copyNamespaceLogo(Namespace oldNamespace, Namespace newNamespace) {
220+
var oldLocation = getNamespaceLogoLocation(oldNamespace).toString();
221+
var newBlobName = getBlobName(newNamespace);
222+
var poller = getContainerClient().getBlobClient(newBlobName)
223+
.beginCopy(oldLocation, Duration.of(1, ChronoUnit.SECONDS));
224+
225+
var response = poller.waitForCompletion();
226+
if(response.getValue().getCopyStatus() != CopyStatusType.SUCCESS) {
227+
throw new RuntimeException(response.getValue().getError());
228+
}
229+
}
230+
218231
@Override
219232
@Cacheable(value = CACHE_EXTENSION_FILES, keyGenerator = GENERATOR_FILES)
220233
public Path getCachedFile(FileResource resource) throws IOException {

server/src/main/java/org/eclipse/openvsx/storage/GoogleCloudStorageService.java

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -188,16 +188,21 @@ private String missingBucketIdMessage(String action, String name) {
188188

189189
@Override
190190
public void copyFiles(List<Pair<FileResource,FileResource>> pairs) {
191-
for(var pair : pairs) {
192-
var source = getObjectId(pair.getFirst());
193-
var target = getObjectId(pair.getSecond());
194-
var request = new Storage.CopyRequest.Builder()
195-
.setSource(BlobId.of(bucketId, source))
196-
.setTarget(BlobId.of(bucketId, target))
197-
.build();
198-
199-
getStorage().copy(request).getResult();
200-
}
191+
pairs.forEach(pair -> copy(getObjectId(pair.getFirst()), getObjectId(pair.getSecond())));
192+
}
193+
194+
@Override
195+
public void copyNamespaceLogo(Namespace oldNamespace, Namespace newNamespace) {
196+
copy(getObjectId(oldNamespace), getObjectId(newNamespace));
197+
}
198+
199+
private void copy(String source, String target) {
200+
var request = new Storage.CopyRequest.Builder()
201+
.setSource(BlobId.of(bucketId, source))
202+
.setTarget(BlobId.of(bucketId, target))
203+
.build();
204+
205+
getStorage().copy(request).getResult();
201206
}
202207

203208
@Override

server/src/main/java/org/eclipse/openvsx/storage/IStorageService.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,5 +60,7 @@ public interface IStorageService {
6060

6161
void copyFiles(List<Pair<FileResource, FileResource>> pairs);
6262

63+
void copyNamespaceLogo(Namespace oldNamespace, Namespace newNamespace);
64+
6365
Path getCachedFile(FileResource resource) throws IOException;
6466
}

server/src/main/java/org/eclipse/openvsx/storage/LocalStorageService.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,15 +138,22 @@ public TempFile downloadFile(FileResource resource) throws IOException {
138138
public void copyFiles(List<Pair<FileResource, FileResource>> pairs) {
139139
try {
140140
for (var pair : pairs) {
141-
var source = getPath(pair.getFirst());
142-
var target = getPath(pair.getSecond());
143-
Files.copy(source, target);
141+
Files.copy(getPath(pair.getFirst()), getPath(pair.getSecond()), StandardCopyOption.REPLACE_EXISTING);
144142
}
145143
} catch (IOException e) {
146144
throw new RuntimeException(e);
147145
}
148146
}
149147

148+
@Override
149+
public void copyNamespaceLogo(Namespace oldNamespace, Namespace newNamespace) {
150+
try {
151+
Files.copy(getLogoPath(oldNamespace), getLogoPath(newNamespace), StandardCopyOption.REPLACE_EXISTING);
152+
} catch (IOException e) {
153+
throw new RuntimeException(e);
154+
}
155+
}
156+
150157
private Path getPath(FileResource resource) {
151158
if(!isEnabled()) {
152159
throw new IllegalStateException("Cannot determine location of file. Configure the 'ovsx.storage.local.directory' property.");

server/src/main/java/org/eclipse/openvsx/storage/StorageUtilService.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,26 @@ public void copyFiles(List<Pair<FileResource,FileResource>> pairs) {
342342
}
343343
}
344344

345+
@Override
346+
public void copyNamespaceLogo(Namespace oldNamespace, Namespace newNamespace) {
347+
switch (oldNamespace.getLogoStorageType()) {
348+
case STORAGE_GOOGLE:
349+
googleStorage.copyNamespaceLogo(oldNamespace, newNamespace);
350+
break;
351+
case STORAGE_AZURE:
352+
azureStorage.copyNamespaceLogo(oldNamespace, newNamespace);
353+
break;
354+
case STORAGE_AWS:
355+
awsStorage.copyNamespaceLogo(oldNamespace, newNamespace);
356+
break;
357+
case STORAGE_LOCAL:
358+
localStorage.copyNamespaceLogo(oldNamespace, newNamespace);
359+
break;
360+
}
361+
362+
newNamespace.setLogoStorageType(oldNamespace.getLogoStorageType());
363+
}
364+
345365
@Override
346366
public Path getCachedFile(FileResource resource) throws IOException {
347367
return switch (resource.getStorageType()) {

server/src/main/java/org/eclipse/openvsx/util/NamingUtil.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@
1010
package org.eclipse.openvsx.util;
1111

1212
import org.apache.commons.lang3.StringUtils;
13+
import org.apache.tika.mime.MimeType;
1314
import org.eclipse.openvsx.adapter.ExtensionQueryResult;
1415
import org.eclipse.openvsx.entities.Extension;
1516
import org.eclipse.openvsx.entities.ExtensionVersion;
17+
import org.eclipse.openvsx.entities.Namespace;
1618
import org.eclipse.openvsx.json.ExtensionJson;
1719
import org.eclipse.openvsx.search.ExtensionSearch;
1820

@@ -93,4 +95,11 @@ public static ExtensionId fromExtensionId(String text) {
9395
: null;
9496
}
9597

98+
public static String toLogoName(Namespace namespace, MimeType logoType) {
99+
return "logo-" + namespace.getName() + "-" + System.currentTimeMillis() + logoType.getExtension();
100+
}
101+
102+
public static String changeLogoName(Namespace oldNamespace, Namespace newNamespace) {
103+
return oldNamespace.getLogoName().replace("-" + oldNamespace.getName() + "-", "-" + newNamespace.getName() + "-");
104+
}
96105
}

webui/src/pages/user/user-namespace-details.tsx

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* SPDX-License-Identifier: EPL-2.0
99
********************************************************************************/
1010

11-
import React, { ChangeEvent, FunctionComponent, useContext, useEffect, useRef, useState } from 'react';
11+
import React, { ChangeEvent, FunctionComponent, useContext, useEffect, useMemo, useRef, useState } from 'react';
1212
import { Box, TextField, Typography, Grid, Button, IconButton, Slider, Stack, Dialog, DialogActions, DialogTitle,
1313
DialogContent, InputAdornment, Select, MenuItem, Paper, SelectChangeEvent } from '@mui/material';
1414
import { CheckCircleOutline } from '@mui/icons-material';
@@ -101,6 +101,11 @@ export const UserNamespaceDetails: FunctionComponent<UserNamespaceDetailsProps>
101101
const [prevEditorPosition, setPrevEditorPosition] = useState<Position>();
102102
const [linkedInAccountType, setLinkedInAccountType] = useState<string>(LINKED_IN_PERSONAL);
103103

104+
const noChanges = useMemo(() => {
105+
const isFalsy = (x: unknown) => !!x === false;
106+
return _.isEqual(_.omitBy(currentDetails, isFalsy), _.omitBy(newDetails, isFalsy));
107+
}, [currentDetails, newDetails]);
108+
104109
useEffect(() => {
105110
getNamespaceDetails();
106111
return () => abortController.current.abort();
@@ -194,8 +199,15 @@ export const UserNamespaceDetails: FunctionComponent<UserNamespaceDetailsProps>
194199
throw result;
195200
}
196201

202+
if (logoPreview) {
203+
const logoFile = await (await fetch(logoPreview)).blob();
204+
await context.service.setNamespaceLogo(abortController.current, details.name, logoFile, details.logo as string);
205+
await getNamespaceDetails();
206+
} else {
207+
setCurrentDetails(copy(newDetails));
208+
}
209+
197210
setDetailsUpdated(true);
198-
setCurrentDetails(copy(details));
199211
setBannerNamespaceName(details.displayName || details.name);
200212
} catch (err) {
201213
context.handleError(err);
@@ -303,21 +315,24 @@ export const UserNamespaceDetails: FunctionComponent<UserNamespaceDetailsProps>
303315
setEditing(false);
304316
};
305317

306-
const handleSaveLogo = () => {
307-
const canvasScaled = editor.current?.getImageScaledToCanvas();
308-
if (canvasScaled) {
309-
canvasScaled.toBlob(async (blob) => {
310-
if (blob) {
311-
if (logoPreview) {
312-
URL.revokeObjectURL(logoPreview);
313-
}
314-
setLogoPreview(URL.createObjectURL(blob));
315-
await context.service.setNamespaceLogo(abortController.current, props.namespace.name, blob, dropzoneFile!.name);
316-
await getNamespaceDetails();
318+
const handleApplyLogo = () => {
319+
const avatarEditor = editor.current as AvatarEditor;
320+
const canvasScaled = avatarEditor.getImageScaledToCanvas();
321+
canvasScaled.toBlob(async (blob) => {
322+
if (blob) {
323+
if (logoPreview) {
324+
URL.revokeObjectURL(logoPreview);
317325
}
318-
});
319-
setEditing(false);
320-
}
326+
setLogoPreview(URL.createObjectURL(blob));
327+
328+
if (newDetails) {
329+
const details = copy(newDetails);
330+
details.logo = dropzoneFile!.name;
331+
setNewDetails(details);
332+
}
333+
}
334+
});
335+
setEditing(false);
321336
};
322337

323338
const adjustScale = (x: number) => {
@@ -420,8 +435,8 @@ export const UserNamespaceDetails: FunctionComponent<UserNamespaceDetailsProps>
420435
</Button>
421436
<Button
422437
autoFocus
423-
onClick={handleSaveLogo} >
424-
Save logo
438+
onClick={handleApplyLogo} >
439+
Apply logo
425440
</Button>
426441
</DialogActions>
427442
</Dialog>
@@ -603,7 +618,7 @@ export const UserNamespaceDetails: FunctionComponent<UserNamespaceDetailsProps>
603618
</Grid>
604619
</Grid>
605620
<Grid item xs={12} sx={{ display: 'flex', justifyContent: 'flex-end' }}>
606-
<Button sx={{ ml: { xs: 2, sm: 2, md: 2, lg: 0, xl: 0 } }} variant='outlined' disabled={_.isEqual(currentDetails, newDetails)} onClick={setNamespaceDetails}>
621+
<Button sx={{ ml: { xs: 2, sm: 2, md: 2, lg: 0, xl: 0 } }} variant='outlined' disabled={noChanges} onClick={setNamespaceDetails}>
607622
Save Namespace Details
608623
</Button>
609624
</Grid>

0 commit comments

Comments
 (0)