From 9c30d682a727659fed0aeb976afe3ed04d4f8158 Mon Sep 17 00:00:00 2001 From: NANTCHOUANG Cyrille Date: Wed, 24 Jan 2018 14:55:15 +0100 Subject: [PATCH] Job for calculating the size and the assset count of each hosted repository --- .../SizeAssetCountAttributesFacet.java | 28 ++ .../SizeAssetCountAttributesFacetImpl.java | 84 +++++ ...zeAssetCountAttributesCalculatingTask.java | 66 ++++ ...ntAttributesCalculatingTaskDescriptor.java | 51 +++ ...SizeAssetCountAttributesFacetImplTest.java | 295 ++++++++++++++++++ ...setCountAttributesCalculatingTaskTest.java | 95 ++++++ .../nexus/coreui/RepositoryComponent.groovy | 44 +-- .../nexus/coreui/RepositoryReferenceXO.groovy | 4 + .../sonatype/nexus/coreui/RepositoryXO.groovy | 17 +- .../rapture/NX/coreui/app/PluginStrings.js | 2 + .../NX/coreui/controller/Repositories.js | 2 + .../rapture/NX/coreui/model/Repository.js | 2 + .../NX/coreui/model/RepositoryReference.js | 4 +- .../view/repository/RepositoryListTemplate.js | 12 + .../recipes/Maven2HostedRecipe.groovy | 24 +- .../raw/internal/RawHostedRecipe.groovy | 6 + 16 files changed, 696 insertions(+), 40 deletions(-) create mode 100644 components/nexus-repository/src/main/java/org/sonatype/nexus/repository/sizeassetcount/SizeAssetCountAttributesFacet.java create mode 100644 components/nexus-repository/src/main/java/org/sonatype/nexus/repository/sizeassetcount/SizeAssetCountAttributesFacetImpl.java create mode 100644 components/nexus-repository/src/main/java/org/sonatype/nexus/repository/sizeassetcount/internal/SizeAssetCountAttributesCalculatingTask.java create mode 100644 components/nexus-repository/src/main/java/org/sonatype/nexus/repository/sizeassetcount/internal/SizeAssetCountAttributesCalculatingTaskDescriptor.java create mode 100644 components/nexus-repository/src/test/java/org/sonatype/nexus/repository/sizeassetcount/SizeAssetCountAttributesFacetImplTest.java create mode 100644 components/nexus-repository/src/test/java/org/sonatype/nexus/repository/sizeassetcount/internal/SizeAssetCountAttributesCalculatingTaskTest.java diff --git a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/sizeassetcount/SizeAssetCountAttributesFacet.java b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/sizeassetcount/SizeAssetCountAttributesFacet.java new file mode 100644 index 0000000000..0756c23e32 --- /dev/null +++ b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/sizeassetcount/SizeAssetCountAttributesFacet.java @@ -0,0 +1,28 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.sizeassetcount; + +import org.sonatype.nexus.repository.Facet; + +/** + * Facet used for calculating the size and the blobcount of repositories + * @since 3.7.0 + */ +@Facet.Exposed +public interface SizeAssetCountAttributesFacet extends Facet{ + + /** + * Calculate the size and the asset count of a repository + */ + void calculateSizeAssetCount(); +} diff --git a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/sizeassetcount/SizeAssetCountAttributesFacetImpl.java b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/sizeassetcount/SizeAssetCountAttributesFacetImpl.java new file mode 100644 index 0000000000..49ff2aef17 --- /dev/null +++ b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/sizeassetcount/SizeAssetCountAttributesFacetImpl.java @@ -0,0 +1,84 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.sizeassetcount; + +import com.google.common.collect.Streams; +import org.sonatype.nexus.repository.FacetSupport; +import org.sonatype.nexus.repository.config.Configuration; +import org.sonatype.nexus.repository.manager.RepositoryManager; +import org.sonatype.nexus.repository.storage.*; +import org.sonatype.nexus.repository.transaction.TransactionalStoreMetadata; +import org.sonatype.nexus.transaction.UnitOfWork; + +import javax.inject.Inject; +import javax.inject.Named; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * @since 3.7.0 + */ +@Named +public class SizeAssetCountAttributesFacetImpl extends FacetSupport implements SizeAssetCountAttributesFacet { + + @Inject + private RepositoryManager repositoryManager; + + + + // Key for the attributes for Size and Blob count + public static final String SIZE_ASSET_COUNT_KEY_ATTRIBUTES = "sizeAssetCount"; + // Key for the data assetcount + public static final String ASSET_COUNT_KEY = "assetCount"; + // Key for the data size + public static final String SIZE_KEY = "size"; + + + public void calculateSizeAssetCount() { + String repositoryName = getRepository().getName(); + log.debug("Repository name {} ", repositoryName); + Map attributesMap = new HashMap<>(); + attributesMap.put(ASSET_COUNT_KEY,0L); + attributesMap.put(SIZE_KEY,0L); + if (optionalFacet(StorageFacet.class).isPresent()) { + TransactionalStoreMetadata.operation.withDb(facet(StorageFacet.class).txSupplier()).call(() -> { + StorageTx storageTx = UnitOfWork.currentTx(); + + //First Get the bucket + Bucket bucket = storageTx.findBucket(getRepository()); + + //Assets of the bucket + Iterable assets = storageTx.browseAssets(bucket); + if (assets != null) { + attributesMap.put(ASSET_COUNT_KEY, storageTx.countAssets(Query.builder().where("1").eq(1).build(), Collections.singletonList(getRepository()))); + attributesMap.put(SIZE_KEY, Streams.stream(assets).mapToLong(Asset::size).sum()); + } + + return null; + }); + } + Configuration configuration = getRepository().getConfiguration(); + Objects.requireNonNull(configuration.getAttributes()) + .put(SIZE_ASSET_COUNT_KEY_ATTRIBUTES, attributesMap); + try { + repositoryManager.update(configuration); + log.debug("The attributes sizeAssetCount of the repository {} have been updated", repositoryName); + } catch (Exception e) { + log.error("Error occuring during the update of rhe repository {} ", repositoryName, e); + } + + + } +} \ No newline at end of file diff --git a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/sizeassetcount/internal/SizeAssetCountAttributesCalculatingTask.java b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/sizeassetcount/internal/SizeAssetCountAttributesCalculatingTask.java new file mode 100644 index 0000000000..65a242735b --- /dev/null +++ b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/sizeassetcount/internal/SizeAssetCountAttributesCalculatingTask.java @@ -0,0 +1,66 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.sizeassetcount.internal; + + +import org.sonatype.nexus.logging.task.TaskLogging; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.RepositoryTaskSupport; +import org.sonatype.nexus.repository.Type; +import org.sonatype.nexus.repository.sizeassetcount.SizeAssetCountAttributesFacet; +import org.sonatype.nexus.repository.types.HostedType; +import org.sonatype.nexus.scheduling.Cancelable; + +import javax.inject.Inject; +import javax.inject.Named; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.logging.task.TaskLogType.NEXUS_LOG_ONLY; + +/** + * Task that calculates the size and the asset count of a repository + * + * @since 3.7.0 + */ +@Named +@TaskLogging(NEXUS_LOG_ONLY) +public class SizeAssetCountAttributesCalculatingTask extends RepositoryTaskSupport + implements Cancelable +{ + + public static final String PREFIX_MESSAGE = "Calculate the size and the asset count of the repository "; + private final Type hostedType; + + @Inject + public SizeAssetCountAttributesCalculatingTask(@Named(HostedType.NAME) final Type hostedType) { + this.hostedType = checkNotNull(hostedType); + } + + + @Override + public String getMessage() { + return PREFIX_MESSAGE + getRepositoryField(); + } + + + @Override + protected void execute(Repository repository) { + repository.facet(SizeAssetCountAttributesFacet.class).calculateSizeAssetCount(); + + } + + @Override + protected boolean appliesTo(Repository repository) { + return hostedType.equals(repository.getType()); + } +} diff --git a/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/sizeassetcount/internal/SizeAssetCountAttributesCalculatingTaskDescriptor.java b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/sizeassetcount/internal/SizeAssetCountAttributesCalculatingTaskDescriptor.java new file mode 100644 index 0000000000..e54ebe69c6 --- /dev/null +++ b/components/nexus-repository/src/main/java/org/sonatype/nexus/repository/sizeassetcount/internal/SizeAssetCountAttributesCalculatingTaskDescriptor.java @@ -0,0 +1,51 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.sizeassetcount.internal; + +import org.sonatype.nexus.formfields.FormField; +import org.sonatype.nexus.formfields.RepositoryCombobox; +import org.sonatype.nexus.repository.sizeassetcount.SizeAssetCountAttributesFacet; +import org.sonatype.nexus.repository.types.HostedType; +import org.sonatype.nexus.scheduling.TaskDescriptorSupport; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import static org.sonatype.nexus.repository.RepositoryTaskSupport.REPOSITORY_NAME_FIELD_ID; + +/** + * @since 3.7.0 + */ +@Named +@Singleton +public class SizeAssetCountAttributesCalculatingTaskDescriptor extends TaskDescriptorSupport +{ + public static final String TYPE_ID = "repository.calculate-size-assetcount"; + + @Inject + public SizeAssetCountAttributesCalculatingTaskDescriptor() { + super(TYPE_ID, + SizeAssetCountAttributesCalculatingTask.class, + " Calculate the size and the asset count of a repository", + VISIBLE, + EXPOSED, + new RepositoryCombobox( + REPOSITORY_NAME_FIELD_ID, + "Repository", + "Select the repository which you will calculate the size and the asset count", + FormField.MANDATORY + ).includingAnyOfFacets(SizeAssetCountAttributesFacet.class).includingAnyOfTypes(HostedType.NAME).includeAnEntryForAllRepositories() + ); + } +} diff --git a/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/sizeassetcount/SizeAssetCountAttributesFacetImplTest.java b/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/sizeassetcount/SizeAssetCountAttributesFacetImplTest.java new file mode 100644 index 0000000000..557011e4b5 --- /dev/null +++ b/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/sizeassetcount/SizeAssetCountAttributesFacetImplTest.java @@ -0,0 +1,295 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.sizeassetcount; + +import com.google.common.base.Supplier; +import com.google.common.collect.Lists; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Matchers; +import org.mockito.Mock; +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.common.collect.NestedAttributesMap; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.config.Configuration; +import org.sonatype.nexus.repository.group.GroupFacet; +import org.sonatype.nexus.repository.manager.RepositoryManager; +import org.sonatype.nexus.repository.storage.*; +import org.sonatype.nexus.repository.types.GroupType; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import static com.google.common.collect.ImmutableList.copyOf; +import static org.fest.assertions.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.sonatype.nexus.repository.sizeassetcount.SizeAssetCountAttributesFacetImpl.*; + +public class SizeAssetCountAttributesFacetImplTest extends TestSupport{ + + + @Mock + private RepositoryManager repositoryManager; + + @Mock + private Configuration configuration; + + @Mock + private StorageFacet storageFacet; + + @Mock + private Bucket bucket; + + @Mock + private StorageTx storageTx; + + @Mock + private Supplier supplier; + + @Mock + private GroupType groupType; + + private Repository initRepository() throws Exception { + Repository repository = mock(Repository.class); + + repository.attach(storageFacet); + + when(repository.facet(StorageFacet.class)).thenReturn(storageFacet); + when(repository.optionalFacet(StorageFacet.class)).thenReturn(Optional.of(storageFacet)); + when(storageFacet.txSupplier()).thenReturn(supplier); + when(storageFacet.txSupplier().get()).thenReturn(storageTx); + when(repository.getName()).thenReturn("MY-MAVEN-REPO"); + + + return repository; + } + + private void initConfiguration(Repository repository) throws Exception { + Map> attributes = new HashMap<>(); + Map valueOfFirstKey = new HashMap<>(); + valueOfFirstKey.put("blobStoreName", "blobstoreTest"); + attributes.put("storage", valueOfFirstKey); + when(configuration.getRepositoryName()).thenReturn("MY-MAVEN-REPO"); + when(configuration.getRecipeName()).thenReturn("mavenRecipeTest"); + when(configuration.getAttributes()).thenReturn(attributes); + + when(repository.getConfiguration()).thenReturn(configuration); + } + + private Repository initRepositoryWithoutStorageFacet() { + Repository repository = mock(Repository.class); + Map> attributes = new HashMap<>(); + Map valueOfFirstKey = new HashMap<>(); + valueOfFirstKey.put("blobStoreName", "blobstoreTest"); + attributes.put("storage", valueOfFirstKey); + when(configuration.getRepositoryName()).thenReturn("MY-MAVEN-REPO"); + when(configuration.getRecipeName()).thenReturn("mavenRecipeTest"); + when(configuration.getAttributes()).thenReturn(attributes); + when(repository.getName()).thenReturn("MY-MAVEN-REPO"); + when(repository.optionalFacet(StorageFacet.class)).thenReturn(Optional.empty()); + when(repository.getConfiguration()).thenReturn(configuration); + + + return repository; + } + + private Repository initRepositoryWithAssets(Asset... assets) throws Exception { + Repository repository = mock(Repository.class); + Map> attributes = new HashMap<>(); + Map valueOfFirstKey = new HashMap<>(); + valueOfFirstKey.put("blobStoreName", "blobstoreTest"); + attributes.put("storage", valueOfFirstKey); + when(configuration.getRepositoryName()).thenReturn("MY-MAVEN-REPO"); + when(configuration.getRecipeName()).thenReturn("mavenRecipeTest"); + when(configuration.getAttributes()).thenReturn(attributes); + + when(repository.getName()).thenReturn("MY-MAVEN-REPO"); + when(repository.facet(StorageFacet.class)).thenReturn(storageFacet); + when(repository.optionalFacet(StorageFacet.class)).thenReturn(Optional.of(storageFacet)); + when(storageFacet.txSupplier()).thenReturn(supplier); + when(storageFacet.txSupplier().get()).thenReturn(storageTx); + + repository.init(configuration); + repository.attach(storageFacet); + + when(storageTx.findBucket(repository)).thenReturn(bucket); + when(storageTx.browseAssets(bucket)).thenReturn(Lists.newArrayList(assets)); + + when(storageTx.countAssets(Matchers.any(Query.class), Matchers.any(Iterable.class))).thenReturn(2L); + + + + return repository; + } + + private Repository groupRepository(final String name, final Repository... repositories) { + Repository groupRepository = mock(Repository.class); + when(groupRepository.getType()).thenReturn(groupType); + when(groupRepository.getName()).thenReturn(name); + when(repositoryManager.get(name)).thenReturn(groupRepository); + GroupFacet groupFacet = mock(GroupFacet.class); + when(groupRepository.facet(GroupFacet.class)).thenReturn(groupFacet); + when(groupRepository.optionalFacet(GroupFacet.class)).thenReturn(Optional.of(groupFacet)); + when(groupRepository.facet(StorageFacet.class)).thenReturn(storageFacet); + when(groupRepository.optionalFacet(StorageFacet.class)).thenReturn(Optional.of(storageFacet)); + + when(groupFacet.members()).thenReturn(copyOf(repositories)); + return groupRepository; + } + + private Asset mockAsset(String name, long size) { + Asset asset = mock(Asset.class); + when(asset.name()).thenReturn(name); + when(asset.size()).thenReturn(size); + return asset; + } + + @Before + public void setUp() { + } + + @Test + public void should_return_0_for_an_empty_repository() throws Exception { + //Given + /** + * Un repository vide + */ + Repository repository = initRepository(); + initConfiguration(repository); + + //When + /** + * Appel de la méthode de comptage de la taille et du blob count + */ + SizeAssetCountAttributesFacet sizeAssetCountAttributesFacet = new SizeAssetCountAttributesFacetImpl(); + sizeAssetCountAttributesFacet.attach(repository); + sizeAssetCountAttributesFacet.calculateSizeAssetCount(); + + //Then + /** + * Return 0 for size and 0 for assetcount + */ + assertThat(repository.getConfiguration().getAttributes(). + get(SizeAssetCountAttributesFacetImpl.SIZE_ASSET_COUNT_KEY_ATTRIBUTES) + .get(SizeAssetCountAttributesFacetImpl.SIZE_KEY)).isEqualTo(0L); + assertThat(repository.getConfiguration().getAttributes(). + get(SizeAssetCountAttributesFacetImpl.SIZE_ASSET_COUNT_KEY_ATTRIBUTES) + .get(SizeAssetCountAttributesFacetImpl.ASSET_COUNT_KEY)).isEqualTo(0L); + + } + + @Test + public void should_return_0_for_a_repository_with_no_storage_facet() throws Exception { + //Given + /** + * Un repository vide + */ + Repository repository = initRepositoryWithoutStorageFacet(); + + //When + /** + * Appel de la méthode de comptage de la taille et du blob count + */ + SizeAssetCountAttributesFacet sizeAssetCountAttributesFacet = new SizeAssetCountAttributesFacetImpl(); + sizeAssetCountAttributesFacet.attach(repository); + sizeAssetCountAttributesFacet.calculateSizeAssetCount(); + //Then + /** + * Return 0 for size and 0 for assetcount + */ + assertThat(repository.getConfiguration().getAttributes(). + get(SizeAssetCountAttributesFacetImpl.SIZE_ASSET_COUNT_KEY_ATTRIBUTES) + .get(SizeAssetCountAttributesFacetImpl.SIZE_KEY)).isEqualTo(0L); + assertThat(repository.getConfiguration().getAttributes(). + get(SizeAssetCountAttributesFacetImpl.SIZE_ASSET_COUNT_KEY_ATTRIBUTES) + .get(SizeAssetCountAttributesFacetImpl.ASSET_COUNT_KEY)).isEqualTo(0L); + } + + @Test + public void should_return_the_size_and_the_asset_count_of_the_assets_for_a_repository_which_contains_just_those_assets() throws Exception { + + //Given + /** + * Un repository avec des assets + */ + Asset asset1 = mockAsset("org.edf.test:1.0", 1500); + Asset asset2 = mockAsset("org.edf.openam:1.0", 2500); + Repository repository = initRepositoryWithAssets(asset1, asset2); + initConfiguration(repository); + Map backing = new HashMap<>(); + backing.put(SIZE_KEY, 4000L); + backing.put(ASSET_COUNT_KEY, 2); + configuration.getAttributes().put(SIZE_ASSET_COUNT_KEY_ATTRIBUTES, backing); + + //When + /** + * Appel de la méthode de comptage de la taille et du blob count + */ + SizeAssetCountAttributesFacet sizeAssetCountAttributesFacet = new SizeAssetCountAttributesFacetImpl(); + sizeAssetCountAttributesFacet.attach(repository); + sizeAssetCountAttributesFacet.calculateSizeAssetCount(); + + //Then + /** + * Return the size and the blob count of the two assets + */ + assertThat(repository.getConfiguration().getAttributes(). + get(SizeAssetCountAttributesFacetImpl.SIZE_ASSET_COUNT_KEY_ATTRIBUTES) + .get(SizeAssetCountAttributesFacetImpl.SIZE_KEY)).isEqualTo(4000L); + assertThat(repository.getConfiguration().getAttributes(). + get(SizeAssetCountAttributesFacetImpl.SIZE_ASSET_COUNT_KEY_ATTRIBUTES) + .get(SizeAssetCountAttributesFacetImpl.ASSET_COUNT_KEY)).isEqualTo(2L); + } + + @Test + public void should_return_the_repository_attributes_of_a_group_repository() throws Exception { + //Given + Asset asset1 = mockAsset("org.edf.test:1.0", 1500); + Asset asset2 = mockAsset("org.edf.openam:1.0", 2500); + Repository repository = initRepositoryWithAssets(asset1, asset2); + Asset asset3 = mockAsset("org.edf.test:2.0", 15000); + Asset asset4 = mockAsset("org.edf.openam:2.0", 2500); + Repository repository2 = initRepositoryWithAssets(asset3, asset4); + Repository groupRepository = groupRepository("MY-REMO-MAVEN-GROUP", repository, repository2); + Bucket groupBucket = mock(Bucket.class); + when(storageTx.findBucket(groupRepository)).thenReturn(groupBucket); + Map backing = new HashMap<>(); + backing.put(SIZE_KEY, 0L); + backing.put(ASSET_COUNT_KEY, 0); + NestedAttributesMap attributesMap = new NestedAttributesMap(SIZE_ASSET_COUNT_KEY_ATTRIBUTES, backing); + when(groupBucket.attributes()).thenReturn(attributesMap); + initConfiguration(groupRepository); + + + //When + /** + * Appel de la méthode de comptage de la taille et du blob count + */ + SizeAssetCountAttributesFacet sizeAssetCountAttributesFacet = new SizeAssetCountAttributesFacetImpl(); + sizeAssetCountAttributesFacet.attach(groupRepository); + sizeAssetCountAttributesFacet.calculateSizeAssetCount(); + + //Then + /** + * Return the size and the blob count of the group repository + */ + assertThat(groupRepository.getConfiguration().getAttributes(). + get(SizeAssetCountAttributesFacetImpl.SIZE_ASSET_COUNT_KEY_ATTRIBUTES) + .get(SizeAssetCountAttributesFacetImpl.SIZE_KEY)).isEqualTo(0L); + assertThat(groupRepository.getConfiguration().getAttributes(). + get(SizeAssetCountAttributesFacetImpl.SIZE_ASSET_COUNT_KEY_ATTRIBUTES) + .get(SizeAssetCountAttributesFacetImpl.ASSET_COUNT_KEY)).isEqualTo(0L); + } +} diff --git a/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/sizeassetcount/internal/SizeAssetCountAttributesCalculatingTaskTest.java b/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/sizeassetcount/internal/SizeAssetCountAttributesCalculatingTaskTest.java new file mode 100644 index 0000000000..07157a8149 --- /dev/null +++ b/components/nexus-repository/src/test/java/org/sonatype/nexus/repository/sizeassetcount/internal/SizeAssetCountAttributesCalculatingTaskTest.java @@ -0,0 +1,95 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.repository.sizeassetcount.internal; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.sonatype.goodies.testsupport.TestSupport; +import org.sonatype.nexus.repository.Repository; +import org.sonatype.nexus.repository.RepositoryTaskSupport; +import org.sonatype.nexus.repository.sizeassetcount.SizeAssetCountAttributesFacet; +import org.sonatype.nexus.repository.types.GroupType; +import org.sonatype.nexus.repository.types.HostedType; +import org.sonatype.nexus.scheduling.TaskConfiguration; + +import static org.fest.assertions.api.Assertions.assertThat; +import static org.mockito.Mockito.*; + +public class SizeAssetCountAttributesCalculatingTaskTest extends TestSupport{ + + + private static final String MY_REPO_NAME = "my-repo"; + + @Mock + private Repository repository; + + @Mock + private HostedType hostedType; + + @Mock + private GroupType groupType; + + @Mock + private SizeAssetCountAttributesFacet sizeAssetCountAttributesFacet; + + + private TaskConfiguration taskConfiguration; + + + private SizeAssetCountAttributesCalculatingTask sizeAssetCountAttributesCalculatingTask; + + @Before + public void setUp() { + sizeAssetCountAttributesCalculatingTask = new SizeAssetCountAttributesCalculatingTask(hostedType); + taskConfiguration = new TaskConfiguration(); + taskConfiguration.setId("test"); + taskConfiguration.setTypeId("test"); + taskConfiguration.setString(RepositoryTaskSupport.REPOSITORY_NAME_FIELD_ID, MY_REPO_NAME); + + sizeAssetCountAttributesCalculatingTask.configure(taskConfiguration); + } + + @Test + public void verify_the_execution_of_the_calcul_of_size_and_asset_count() { + + //Given + when(repository.getName()).thenReturn(MY_REPO_NAME); + when(repository.getType()).thenReturn(hostedType); + when(repository.facet(SizeAssetCountAttributesFacet.class)).thenReturn(sizeAssetCountAttributesFacet); + + //When + sizeAssetCountAttributesCalculatingTask.execute(repository); + + //Then + verify(sizeAssetCountAttributesFacet, times(1)).calculateSizeAssetCount(); + assertThat(sizeAssetCountAttributesCalculatingTask.appliesTo(repository)).isTrue(); + assertThat(sizeAssetCountAttributesCalculatingTask.getMessage()).isEqualToIgnoringCase(SizeAssetCountAttributesCalculatingTask.PREFIX_MESSAGE + MY_REPO_NAME); + } + + @Test + public void verify_the_task_dont_apply_to_a_repository_type_different_of_hosted() { + + //Given + when(repository.getName()).thenReturn("my-repo"); + when(repository.getType()).thenReturn(groupType); + when(repository.facet(SizeAssetCountAttributesFacet.class)).thenReturn(sizeAssetCountAttributesFacet); + + //When + sizeAssetCountAttributesCalculatingTask.execute(repository); + + //Then + assertThat(sizeAssetCountAttributesCalculatingTask.appliesTo(repository)).isFalse(); + } + +} diff --git a/plugins/nexus-coreui-plugin/src/main/java/org/sonatype/nexus/coreui/RepositoryComponent.groovy b/plugins/nexus-coreui-plugin/src/main/java/org/sonatype/nexus/coreui/RepositoryComponent.groovy index 8eda2d8cf8..d486897033 100644 --- a/plugins/nexus-coreui-plugin/src/main/java/org/sonatype/nexus/coreui/RepositoryComponent.groovy +++ b/plugins/nexus-coreui-plugin/src/main/java/org/sonatype/nexus/coreui/RepositoryComponent.groovy @@ -12,17 +12,15 @@ */ package org.sonatype.nexus.coreui -import java.util.stream.Collectors -import java.util.stream.StreamSupport - -import javax.annotation.Nullable -import javax.inject.Inject -import javax.inject.Named -import javax.inject.Singleton -import javax.validation.Valid -import javax.validation.constraints.NotNull -import javax.validation.groups.Default - +import com.codahale.metrics.annotation.ExceptionMetered +import com.codahale.metrics.annotation.Timed +import com.google.common.collect.ImmutableList +import com.softwarementors.extjs.djn.config.annotations.DirectAction +import com.softwarementors.extjs.djn.config.annotations.DirectMethod +import com.softwarementors.extjs.djn.config.annotations.DirectPollMethod +import groovy.transform.PackageScope +import org.apache.shiro.authz.annotation.RequiresAuthentication +import org.hibernate.validator.constraints.NotEmpty import org.sonatype.nexus.common.app.BaseUrlHolder import org.sonatype.nexus.common.app.GlobalComponentLookupHelper import org.sonatype.nexus.coreui.internal.search.BrowseableFormatXO @@ -57,15 +55,15 @@ import org.sonatype.nexus.validation.Validate import org.sonatype.nexus.validation.group.Create import org.sonatype.nexus.validation.group.Update -import com.codahale.metrics.annotation.ExceptionMetered -import com.codahale.metrics.annotation.Timed -import com.google.common.collect.ImmutableList -import com.softwarementors.extjs.djn.config.annotations.DirectAction -import com.softwarementors.extjs.djn.config.annotations.DirectMethod -import com.softwarementors.extjs.djn.config.annotations.DirectPollMethod -import groovy.transform.PackageScope -import org.apache.shiro.authz.annotation.RequiresAuthentication -import org.hibernate.validator.constraints.NotEmpty +import javax.annotation.Nullable +import javax.inject.Inject +import javax.inject.Named +import javax.inject.Singleton +import javax.validation.Valid +import javax.validation.constraints.NotNull +import javax.validation.groups.Default +import java.util.stream.Collectors +import java.util.stream.StreamSupport /** * Repository {@link DirectComponent}. @@ -159,7 +157,9 @@ class RepositoryComponent format: repository.format.toString(), versionPolicy: repository.configuration.attributes.maven?.versionPolicy, status: buildStatus(repository), - url: "${BaseUrlHolder.get()}/repository/${repository.name}/" // trailing slash is important + size: repository?.configuration?.attributes?.sizeAssetCount?.size ?: 0L, + assetCount: repository?.configuration?.attributes?.sizeAssetCount?.assetCount ?: 0L, + url: "${BaseUrlHolder.get()}/repository/${repository.name}/"// trailing slash is important, ) } } @@ -281,6 +281,8 @@ class RepositoryComponent online: input.configuration.online, recipe: input.configuration.recipeName, status: buildStatus(input), + size: input?.configuration?.attributes?.sizeAssetCount?.size ?: 0L, + assetCount: input?.configuration?.attributes?.sizeAssetCount?.assetCount ?: 0L, attributes: filterAttributes(input.configuration.copy().attributes), url: "${BaseUrlHolder.get()}/repository/${input.name}/" // trailing slash is important ) diff --git a/plugins/nexus-coreui-plugin/src/main/java/org/sonatype/nexus/coreui/RepositoryReferenceXO.groovy b/plugins/nexus-coreui-plugin/src/main/java/org/sonatype/nexus/coreui/RepositoryReferenceXO.groovy index 00c30ac464..0965165589 100644 --- a/plugins/nexus-coreui-plugin/src/main/java/org/sonatype/nexus/coreui/RepositoryReferenceXO.groovy +++ b/plugins/nexus-coreui-plugin/src/main/java/org/sonatype/nexus/coreui/RepositoryReferenceXO.groovy @@ -13,6 +13,7 @@ package org.sonatype.nexus.coreui import groovy.transform.ToString +import org.hibernate.validator.constraints.Range /** @@ -29,6 +30,9 @@ class RepositoryReferenceXO String versionPolicy String url RepositoryStatusXO status + long assetCount + long size + /** * sortOrder will override the typical alphanumeric ordering in the UI, so the higher your sortOrder, the closer to * the top you will get diff --git a/plugins/nexus-coreui-plugin/src/main/java/org/sonatype/nexus/coreui/RepositoryXO.groovy b/plugins/nexus-coreui-plugin/src/main/java/org/sonatype/nexus/coreui/RepositoryXO.groovy index 7895ea7e4f..7f74f2c476 100644 --- a/plugins/nexus-coreui-plugin/src/main/java/org/sonatype/nexus/coreui/RepositoryXO.groovy +++ b/plugins/nexus-coreui-plugin/src/main/java/org/sonatype/nexus/coreui/RepositoryXO.groovy @@ -12,16 +12,15 @@ */ package org.sonatype.nexus.coreui -import javax.validation.constraints.NotNull -import javax.validation.constraints.Pattern - -import org.sonatype.nexus.validation.constraint.NamePatternConstants -import org.sonatype.nexus.repository.config.UniqueRepositoryName -import org.sonatype.nexus.validation.group.Create - import groovy.transform.ToString import org.hibernate.validator.constraints.NotBlank import org.hibernate.validator.constraints.NotEmpty +import org.sonatype.nexus.repository.config.UniqueRepositoryName +import org.sonatype.nexus.validation.constraint.NamePatternConstants +import org.sonatype.nexus.validation.group.Create + +import javax.validation.constraints.NotNull +import javax.validation.constraints.Pattern /** * Repository exchange object. @@ -52,4 +51,8 @@ class RepositoryXO String url RepositoryStatusXO status + + long assetCount + + long size } diff --git a/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/app/PluginStrings.js b/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/app/PluginStrings.js index 1a366ceb03..6a6bedb000 100644 --- a/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/app/PluginStrings.js +++ b/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/app/PluginStrings.js @@ -278,6 +278,8 @@ Ext.define('NX.coreui.app.PluginStrings', { Repository_RepositoryList_Format_Header: 'Format', Repository_RepositoryList_Status_Header: 'Status', Repository_RepositoryList_URL_Header: 'URL', + Repository_RepositoryList_Size_Header: 'Size', + Repository_RepositoryList_AssetCount_Header: 'Asset count', Repository_RepositoryList_Filter_EmptyText: 'No repositories matched "$filter"', Repository_RepositoryList_EmptyText: 'No repositories defined', Repository_RepositoryFeature_Delete_Button: 'Delete repository', diff --git a/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/controller/Repositories.js b/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/controller/Repositories.js index 1d40fa8bea..3ccf55a6fe 100644 --- a/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/controller/Repositories.js +++ b/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/controller/Repositories.js @@ -165,6 +165,7 @@ Ext.define('NX.coreui.controller.Repositories', { /** * @override */ + getDescription: function (model) { return model.get('name'); }, @@ -316,6 +317,7 @@ Ext.define('NX.coreui.controller.Repositories', { interval: 5000, baseParams: {}, listeners: { + data: function (provider, event) { if (event.data && event.data.success && event.data.data) { me.updateRepositoryModels(event.data.data); diff --git a/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/model/Repository.js b/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/model/Repository.js index 7763bc1e21..8040c13b86 100644 --- a/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/model/Repository.js +++ b/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/model/Repository.js @@ -28,6 +28,8 @@ Ext.define('NX.coreui.model.Repository', { {name: 'online', type: 'boolean'}, {name: 'status', type: 'auto' /*object*/}, {name: 'attributes', type: 'auto' /*object*/}, + {name: 'size', type: 'int'}, + {name: 'assetCount', type: 'int'}, {name: 'url', type: 'string', sortType: 'asUCText'} ] }); diff --git a/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/model/RepositoryReference.js b/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/model/RepositoryReference.js index 1757cacbdd..f294bedff2 100644 --- a/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/model/RepositoryReference.js +++ b/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/model/RepositoryReference.js @@ -26,7 +26,9 @@ Ext.define('NX.coreui.model.RepositoryReference', { {name: 'format', type: 'string', sortType: 'asUCText'}, {name: 'versionPolicy', type: 'string', sortType: 'asUCText'}, {name: 'status', type: 'auto' /*object*/}, - {name: 'url', type: 'string', sortType: 'asUCText'}, + {name: 'size', type: 'int'}, + {name: 'assetCount', type: 'int'}, + {name: 'url', type: 'string', sortType: 'asUCText'}, {name: 'sortOrder', sortType: 'asInt'} ] }); diff --git a/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/view/repository/RepositoryListTemplate.js b/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/view/repository/RepositoryListTemplate.js index 003b783145..24977d1638 100644 --- a/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/view/repository/RepositoryListTemplate.js +++ b/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/view/repository/RepositoryListTemplate.js @@ -54,6 +54,18 @@ Ext.define('NX.coreui.view.repository.RepositoryListTemplate', { dataIndex: 'format', stateId: 'format' }, + { + text: NX.I18n.get('Repository_RepositoryList_Size_Header'), + dataIndex: 'size', + stateId: 'size', + renderer: Ext.util.Format.fileSize + }, + { + text: NX.I18n.get('Repository_RepositoryList_AssetCount_Header'), + dataIndex: 'assetCount', + stateId: 'assetCount' + }, + { header: NX.I18n.get('Repository_RepositoryList_Status_Header'), dataIndex: 'status', stateId: 'status', flex: 1, xtype: 'templatecolumn', diff --git a/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/repository/maven/internal/recipes/Maven2HostedRecipe.groovy b/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/repository/maven/internal/recipes/Maven2HostedRecipe.groovy index c25f89917a..677ed0ec97 100644 --- a/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/repository/maven/internal/recipes/Maven2HostedRecipe.groovy +++ b/plugins/nexus-repository-maven/src/main/java/org/sonatype/nexus/repository/maven/internal/recipes/Maven2HostedRecipe.groovy @@ -12,12 +12,6 @@ */ package org.sonatype.nexus.repository.maven.internal.recipes -import javax.annotation.Nonnull -import javax.inject.Inject -import javax.inject.Named -import javax.inject.Provider -import javax.inject.Singleton - import org.sonatype.nexus.repository.Format import org.sonatype.nexus.repository.Repository import org.sonatype.nexus.repository.Type @@ -27,17 +21,20 @@ import org.sonatype.nexus.repository.maven.RemoveSnapshotsFacet import org.sonatype.nexus.repository.maven.internal.Maven2Format import org.sonatype.nexus.repository.maven.internal.MavenSecurityFacet import org.sonatype.nexus.repository.maven.internal.VersionPolicyHandler -import org.sonatype.nexus.repository.maven.internal.hosted.ArchetypeCatalogHandler -import org.sonatype.nexus.repository.maven.internal.hosted.HostedHandler -import org.sonatype.nexus.repository.maven.internal.hosted.MavenHostedComponentMaintenanceFacet -import org.sonatype.nexus.repository.maven.internal.hosted.MavenHostedFacetImpl -import org.sonatype.nexus.repository.maven.internal.hosted.MavenHostedIndexFacet +import org.sonatype.nexus.repository.maven.internal.hosted.* import org.sonatype.nexus.repository.search.SearchFacet +import org.sonatype.nexus.repository.sizeassetcount.SizeAssetCountAttributesFacet import org.sonatype.nexus.repository.types.HostedType import org.sonatype.nexus.repository.view.ConfigurableViewFacet import org.sonatype.nexus.repository.view.Router import org.sonatype.nexus.repository.view.ViewFacet +import javax.annotation.Nonnull +import javax.inject.Inject +import javax.inject.Named +import javax.inject.Provider +import javax.inject.Singleton + import static org.sonatype.nexus.repository.http.HttpHandlers.notFound /** @@ -79,6 +76,10 @@ class Maven2HostedRecipe @Inject Provider removeSnapshotsFacet + + @Inject + Provider sizeAssetCountAttributesfacet + @Inject Maven2HostedRecipe(@Named(HostedType.NAME) final Type type, @Named(Maven2Format.NAME) final Format format, @@ -101,6 +102,7 @@ class Maven2HostedRecipe repository.attach(mavenPurgeSnapshotsFacet.get()) repository.attach(removeSnapshotsFacet.get()) repository.attach(configure(viewFacet.get())) + repository.attach(sizeAssetCountAttributesfacet.get()) } private ViewFacet configure(final ConfigurableViewFacet facet) { diff --git a/plugins/nexus-repository-raw/src/main/java/org/sonatype/nexus/repository/raw/internal/RawHostedRecipe.groovy b/plugins/nexus-repository-raw/src/main/java/org/sonatype/nexus/repository/raw/internal/RawHostedRecipe.groovy index 9b11a5d386..be9e8c7c35 100644 --- a/plugins/nexus-repository-raw/src/main/java/org/sonatype/nexus/repository/raw/internal/RawHostedRecipe.groovy +++ b/plugins/nexus-repository-raw/src/main/java/org/sonatype/nexus/repository/raw/internal/RawHostedRecipe.groovy @@ -12,6 +12,8 @@ */ package org.sonatype.nexus.repository.raw.internal +import org.sonatype.nexus.repository.sizeassetcount.SizeAssetCountAttributesFacet + import javax.annotation.Nonnull import javax.inject.Inject import javax.inject.Named @@ -81,6 +83,9 @@ class RawHostedRecipe @Inject Provider searchFacet + @Inject + Provider sizeAssetCountAttributesfacet + @Inject ExceptionHandler exceptionHandler @@ -127,6 +132,7 @@ class RawHostedRecipe repository.attach(attributesFacet.get()) repository.attach(componentMaintenance.get()) repository.attach(searchFacet.get()); + repository.attach(sizeAssetCountAttributesfacet.get()) } /**