diff --git a/assemblies/nexus-base-overlay/src/main/resources/overlay/bin/nexus b/assemblies/nexus-base-overlay/src/main/resources/overlay/bin/nexus index 0f33c9b4cc..89e1463218 100644 --- a/assemblies/nexus-base-overlay/src/main/resources/overlay/bin/nexus +++ b/assemblies/nexus-base-overlay/src/main/resources/overlay/bin/nexus @@ -567,8 +567,8 @@ run() { launch "${JAVA}" ${JAVA_OPTS} \ --add-reads=java.xml=java.logging \ --add-exports=java.base/org.apache.karaf.specs.locator=java.xml,ALL-UNNAMED \ - --patch-module java.base=${KARAF_HOME}/lib/endorsed/org.apache.karaf.specs.locator-4.3.9.jar \ - --patch-module java.xml=${KARAF_HOME}/lib/endorsed/org.apache.karaf.specs.java.xml-4.3.9.jar \ + --patch-module java.base=${KARAF_HOME}/lib/endorsed/org.apache.karaf.specs.locator-4.4.2.jar \ + --patch-module java.xml=${KARAF_HOME}/lib/endorsed/org.apache.karaf.specs.java.xml-4.4.2.jar \ --add-opens java.base/java.security=ALL-UNNAMED \ --add-opens java.base/java.net=ALL-UNNAMED \ --add-opens java.base/java.lang=ALL-UNNAMED \ diff --git a/assemblies/nexus-base-overlay/src/main/resources/overlay/bin/nexus.bat b/assemblies/nexus-base-overlay/src/main/resources/overlay/bin/nexus.bat index 5012652f0d..3f4f06c120 100644 --- a/assemblies/nexus-base-overlay/src/main/resources/overlay/bin/nexus.bat +++ b/assemblies/nexus-base-overlay/src/main/resources/overlay/bin/nexus.bat @@ -363,8 +363,8 @@ if "%KARAF_PROFILER%" == "" goto :RUN "%JAVA%" %JAVA_OPTS% %OPTS% ^ --add-reads=java.xml=java.logging ^ --add-exports=java.base/org.apache.karaf.specs.locator=java.xml,ALL-UNNAMED ^ - --patch-module java.base=%KARAF_HOME%\lib\endorsed\org.apache.karaf.specs.locator-4.3.9.jar ^ - --patch-module java.xml=%KARAF_HOME%\lib\endorsed\org.apache.karaf.specs.java.xml-4.3.9.jar ^ + --patch-module java.base=%KARAF_HOME%\lib\endorsed\org.apache.karaf.specs.locator-4.4.2.jar ^ + --patch-module java.xml=%KARAF_HOME%\lib\endorsed\org.apache.karaf.specs.java.xml-4.4.2.jar ^ --add-opens java.base/java.security=ALL-UNNAMED ^ --add-opens java.base/java.net=ALL-UNNAMED ^ --add-opens java.base/java.lang=ALL-UNNAMED ^ diff --git a/assemblies/nexus-base-overlay/src/main/resources/overlay/etc/karaf/config.properties b/assemblies/nexus-base-overlay/src/main/resources/overlay/etc/karaf/config.properties index 72b7772b70..bfbe7fdc25 100644 --- a/assemblies/nexus-base-overlay/src/main/resources/overlay/etc/karaf/config.properties +++ b/assemblies/nexus-base-overlay/src/main/resources/overlay/etc/karaf/config.properties @@ -31,8 +31,8 @@ karaf.framework=felix # # Location of the OSGi frameworks # -karaf.framework.equinox=mvn\:org.eclipse.platform/org.eclipse.osgi/3.16.300 -karaf.framework.felix=mvn\:org.apache.felix/org.apache.felix.framework/6.0.5 +karaf.framework.equinox=mvn\:org.eclipse.platform/org.eclipse.osgi/3.18.0 +karaf.framework.felix=mvn\:org.apache.felix/org.apache.felix.framework/7.0.5 # # Framework config properties. @@ -41,8 +41,8 @@ org.osgi.framework.system.packages= \ org.osgi.dto;version="1.1",\ org.osgi.resource;version="1.0",\ org.osgi.resource.dto;version="1.0";uses:="org.osgi.dto",\ - org.osgi.framework;version="1.9",\ - org.osgi.framework.dto;version="1.9";uses:="org.osgi.dto",\ + org.osgi.framework;version="1.10",\ + org.osgi.framework.dto;version="1.10";uses:="org.osgi.dto",\ org.osgi.framework.hooks.bundle;version="1.1";uses:="org.osgi.framework",\ org.osgi.framework.hooks.resolver;version="1.0";uses:="org.osgi.framework.wiring",\ org.osgi.framework.hooks.service;version="1.1";uses:="org.osgi.framework",\ @@ -59,12 +59,12 @@ org.osgi.framework.system.packages= \ org.osgi.service.startlevel;version="1.1";uses:="org.osgi.framework",\ org.osgi.service.url;version="1.0",\ org.osgi.util.tracker;version="1.5.2";uses:="org.osgi.framework",\ - org.apache.karaf.version;version="4.3.9",\ - org.apache.karaf.diagnostic.core;uses:=org.osgi.framework;version="4.3.9",\ - org.apache.karaf.diagnostic.core.common;uses:=org.apache.karaf.diagnostic.core;version="4.3.9",\ - org.apache.karaf.jaas.boot.principal;uses:=javax.security.auth;version="4.3.9",\ - org.apache.karaf.jaas.boot;uses:="javax.security.auth,javax.security.auth.callback,javax.security.auth.login,javax.security.auth.spi,org.osgi.framework";version="4.3.9",\ - org.apache.karaf.info;version="4.3.9",\ + org.apache.karaf.version;version="4.4.2",\ + org.apache.karaf.diagnostic.core;uses:=org.osgi.framework;version="4.4.2",\ + org.apache.karaf.diagnostic.core.common;uses:=org.apache.karaf.diagnostic.core;version="4.4.2",\ + org.apache.karaf.jaas.boot.principal;uses:=javax.security.auth;version="4.4.2",\ + org.apache.karaf.jaas.boot;uses:="javax.security.auth,javax.security.auth.callback,javax.security.auth.login,javax.security.auth.spi,org.osgi.framework";version="4.4.2",\ + org.apache.karaf.info;version="4.4.2",\ ${jre-${java.specification.version}} # @@ -152,6 +152,18 @@ equinox-capabilities= \ osgi.service;objectClass:List=org.eclipse.osgi.service.security.TrustEngine;osgi.signedcontent.trust.engine=org.eclipse.osgi, \ osgi.service;objectClass:List=org.eclipse.osgi.service.urlconversion.URLConverter;protocol:List="bundleentry,bundleresource" +eecap-18 = osgi.ee; osgi.ee="OSGi/Minimum"; version:List="1.0,1.1,1.2", \ + osgi.ee; osgi.ee="JavaSE"; version:List="1.0,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,9.0,10.0,11.0,13.0,14.0,15.0,16.0,17.0,18.0", \ + osgi.ee; osgi.ee="JRE"; version:List="1.0,1.1", \ + osgi.ee; osgi.ee="JavaSE/compact1"; version:List="1.8,9.0,10.0,11.0,13.0,14.0,15.0,16.0,17.0,18.0", \ + osgi.ee; osgi.ee="JavaSE/compact2"; version:List="1.8,9.0,10.0,11.0,13.0,14.0,15.0,16.0,17.0,18.0", \ + osgi.ee; osgi.ee="JavaSE/compact3"; version:List="1.8,9.0,10.0,11.0,13.0,14.0,15.0,16.0,17.0,18.0" +eecap-17 = osgi.ee; osgi.ee="OSGi/Minimum"; version:List="1.0,1.1,1.2", \ + osgi.ee; osgi.ee="JavaSE"; version:List="1.0,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,9.0,10.0,11.0,13.0,14.0,15.0,16.0,17.0", \ + osgi.ee; osgi.ee="JRE"; version:List="1.0,1.1", \ + osgi.ee; osgi.ee="JavaSE/compact1"; version:List="1.8,9.0,10.0,11.0,13.0,14.0,15.0,16.0,17.0", \ + osgi.ee; osgi.ee="JavaSE/compact2"; version:List="1.8,9.0,10.0,11.0,13.0,14.0,15.0,16.0,17.0", \ + osgi.ee; osgi.ee="JavaSE/compact3"; version:List="1.8,9.0,10.0,11.0,13.0,14.0,15.0,16.0,17.0" eecap-16 = osgi.ee; osgi.ee="OSGi/Minimum"; version:List="1.0,1.1,1.2", \ osgi.ee; osgi.ee="JavaSE"; version:List="1.0,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,9.0,10.0,11.0,13.0,14.0,15.0,16.0", \ osgi.ee; osgi.ee="JRE"; version:List="1.0,1.1", \ diff --git a/buildsupport/groovy/pom.xml b/buildsupport/groovy/pom.xml index 49a91dbdfa..50eb2275af 100644 --- a/buildsupport/groovy/pom.xml +++ b/buildsupport/groovy/pom.xml @@ -122,7 +122,7 @@ org.codehaus.groovy groovy-dateutil - ${groovy.version} + 3.0.19 diff --git a/buildsupport/logging/pom.xml b/buildsupport/logging/pom.xml index f754565372..0cb124eeb0 100644 --- a/buildsupport/logging/pom.xml +++ b/buildsupport/logging/pom.xml @@ -30,7 +30,7 @@ 1.7.36 1.2.13 - 2.0.18 + 2.1.3 diff --git a/buildsupport/osgi/pom.xml b/buildsupport/osgi/pom.xml index 23afb09e0a..46e506f8b2 100644 --- a/buildsupport/osgi/pom.xml +++ b/buildsupport/osgi/pom.xml @@ -28,7 +28,7 @@ pom - 1.3.6 + 1.3.7 @@ -48,7 +48,7 @@ org.apache.felix org.apache.felix.framework - 6.0.5 + 7.0.5 @@ -66,7 +66,7 @@ org.apache.felix org.apache.felix.scr - 2.1.20 + 2.2.4 @@ -185,7 +185,7 @@ org.apache.aries.spifly org.apache.aries.spifly.dynamic.bundle - 1.2.1 + ${spifly.version} diff --git a/changed.groovy b/changed.groovy index 0ebcfb91d6..4150ab4026 100644 --- a/changed.groovy +++ b/changed.groovy @@ -13,26 +13,15 @@ * Eclipse Foundation. All other trademarks are the property of their respective owners. */ -@Grab(group = 'ch.qos.logback', module = 'logback-classic', version = '1.2.3') -@Grab(group = 'org.apache.maven', module = 'maven-model', version = '3.5.0') +@Grab(group = 'org.apache.maven', module = 'maven-model', version = '3.8.1') @Grab(group = 'org.ajoberstar', module = 'grgit', version = '2.2.1') -import ch.qos.logback.classic.Logger - import org.ajoberstar.grgit.* import org.ajoberstar.grgit.operation.* import org.ajoberstar.grgit.service.* import org.apache.maven.model.Model import org.apache.maven.model.io.xpp3.MavenXpp3Reader -import static ch.qos.logback.classic.Level.* - -import static org.slf4j.Logger.ROOT_LOGGER_NAME -import static org.slf4j.LoggerFactory.getLogger - -// set default log level (jgit seems to have something on DEBUG by default) -((Logger) getLogger(ROOT_LOGGER_NAME)).setLevel(OFF) - def getChangedProjects() { def grgit = Grgit.open() def changes = grgit.status().staged.getAllChanges() + grgit.status().unstaged.getAllChanges() diff --git a/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/osgi/NexusEditionPropertiesConfigurer.java b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/osgi/NexusEditionPropertiesConfigurer.java index 947a3c5eb5..2f060f1449 100644 --- a/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/osgi/NexusEditionPropertiesConfigurer.java +++ b/components/nexus-bootstrap/src/main/java/org/sonatype/nexus/bootstrap/osgi/NexusEditionPropertiesConfigurer.java @@ -20,6 +20,8 @@ import org.sonatype.nexus.bootstrap.internal.DirectoryHelper; +import org.osgi.framework.Version; + import com.google.common.annotations.VisibleForTesting; import static java.lang.Boolean.parseBoolean; @@ -35,6 +37,8 @@ public class NexusEditionPropertiesConfigurer private static final String NEXUS_EXCLUDE_FEATURES = "nexus-exclude-features"; + private static final Version ORIENT_MAX_JAVA_VERSION = new Version(11, 0, 0); + public Properties getPropertiesFromConfiguration() throws IOException { Properties properties = System.getProperties(); @@ -131,11 +135,22 @@ private void selectDbFeature(final Properties properties) { properties.setProperty("nexus.quartz.jobstore.jdbc", "true"); } else { + ensureOrientRunningWithCorrectJavaRuntime(); properties.setProperty(NEXUS_DB_FEATURE, "nexus-orient"); properties.setProperty(ORIENT_ENABLED, "true"); } } + @VisibleForTesting + void ensureOrientRunningWithCorrectJavaRuntime() { + Version currentVersion = new Version(System.getProperty("java.version").replace("_", ".")); + boolean versionAllowed = currentVersion.getMajor() <= ORIENT_MAX_JAVA_VERSION.getMajor(); + if (!versionAllowed) { + throw new IllegalStateException("The maximum Java version for OrientDb is Java 11. " + + "Please check current Java version meets this requirement."); + } + } + private void selectAuthenticationFeature(final Properties properties) { if (parseBoolean(properties.getProperty(SESSION_ENABLED, "true"))) { properties.setProperty(SESSION_ENABLED, "true"); diff --git a/components/nexus-bootstrap/src/test/java/org/sonatype/nexus/bootstrap/osgi/NexusEditionPropertiesConfigurerTest.java b/components/nexus-bootstrap/src/test/java/org/sonatype/nexus/bootstrap/osgi/NexusEditionPropertiesConfigurerTest.java index 24f2f5066e..99a58dc4b2 100644 --- a/components/nexus-bootstrap/src/test/java/org/sonatype/nexus/bootstrap/osgi/NexusEditionPropertiesConfigurerTest.java +++ b/components/nexus-bootstrap/src/test/java/org/sonatype/nexus/bootstrap/osgi/NexusEditionPropertiesConfigurerTest.java @@ -64,4 +64,19 @@ public void testDoNotThrowExceptionIfHACIsDisabledInEnvVariables() { underTest.ensureHACIsDisabled(); } + @Test(expected = Test.None.class) + public void testOrientDbWithAllowedVersion() { + System.setProperty("java.version", "11.0.0"); + underTest.ensureOrientRunningWithCorrectJavaRuntime(); + } + + @Test + public void testOrientDbWithNotAllowedVersion() { + System.setProperty("java.version", "17.0.0"); + IllegalStateException expected = assertThrows(IllegalStateException.class, + underTest::ensureOrientRunningWithCorrectJavaRuntime + ); + assertThat(expected.getMessage(), containsString("The maximum Java version for OrientDb is Java 11")); + } + } \ No newline at end of file diff --git a/components/nexus-common/src/main/java/org/sonatype/nexus/common/app/FeatureFlags.java b/components/nexus-common/src/main/java/org/sonatype/nexus/common/app/FeatureFlags.java index 975bd78405..c9ee83bfa9 100644 --- a/components/nexus-common/src/main/java/org/sonatype/nexus/common/app/FeatureFlags.java +++ b/components/nexus-common/src/main/java/org/sonatype/nexus/common/app/FeatureFlags.java @@ -155,4 +155,6 @@ public interface FeatureFlags * See https://owasp.org/www-community/controls/SecureCookieAttribute */ String NXSESSIONID_SECURE_COOKIE_NAMED = "${nexus.session.secureCookie:-true}"; + + String API_EXTENDED = "nexus.api.extended.enabled"; } diff --git a/components/nexus-core/src/main/java/org/sonatype/nexus/internal/script/groovy/GroovyScriptEngineLoader.java b/components/nexus-core/src/main/java/org/sonatype/nexus/internal/script/groovy/GroovyScriptEngineLoader.java new file mode 100644 index 0000000000..055f7f3132 --- /dev/null +++ b/components/nexus-core/src/main/java/org/sonatype/nexus/internal/script/groovy/GroovyScriptEngineLoader.java @@ -0,0 +1,59 @@ +/* + * 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.internal.script.groovy; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; +import javax.script.ScriptEngineManager; + +import org.sonatype.nexus.common.app.ApplicationDirectories; +import org.sonatype.nexus.common.app.ManagedLifecycle; +import org.sonatype.nexus.common.stateguard.StateGuardLifecycleSupport; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.sonatype.nexus.common.app.ManagedLifecycle.Phase.SERVICES; + +/** + * Groovy script engine loader. Is used to run groovy scripts. + */ +@Named +@Singleton +@ManagedLifecycle(phase = SERVICES) +public class GroovyScriptEngineLoader + extends StateGuardLifecycleSupport +{ + private final ClassLoader classLoader; + + private final ApplicationDirectories applicationDirectories; + + private final ScriptEngineManager scriptEngineManager; + + @Inject + public GroovyScriptEngineLoader( + final @Named("nexus-uber") ClassLoader classLoader, + final ApplicationDirectories applicationDirectories, + final ScriptEngineManager scriptEngineManager) + { + this.classLoader = checkNotNull(classLoader); + this.applicationDirectories = checkNotNull(applicationDirectories); + this.scriptEngineManager = checkNotNull(scriptEngineManager); + } + + @Override + protected void doStart() throws Exception { + GroovyScriptEngineFactory groovyEngineFactory = new GroovyScriptEngineFactory(classLoader, applicationDirectories); + log.debug("Registering engine-factory: {}", groovyEngineFactory); + groovyEngineFactory.getNames().forEach(name -> scriptEngineManager.registerEngineName(name, groovyEngineFactory)); + } +} diff --git a/components/nexus-orient/pom.xml b/components/nexus-orient/pom.xml index be11d58040..2c2928e656 100644 --- a/components/nexus-orient/pom.xml +++ b/components/nexus-orient/pom.xml @@ -192,26 +192,4 @@ - - - - test-on-java17 - - [17,) - - - - - org.apache.maven.plugins - maven-surefire-plugin - - - --add-exports java.base/sun.nio.ch=ALL-UNNAMED - - - - - - - diff --git a/components/nexus-pax-exam/src/main/java/org/sonatype/nexus/pax/exam/NexusPaxExamSupport.java b/components/nexus-pax-exam/src/main/java/org/sonatype/nexus/pax/exam/NexusPaxExamSupport.java index 623a73107e..e986762833 100644 --- a/components/nexus-pax-exam/src/main/java/org/sonatype/nexus/pax/exam/NexusPaxExamSupport.java +++ b/components/nexus-pax-exam/src/main/java/org/sonatype/nexus/pax/exam/NexusPaxExamSupport.java @@ -53,6 +53,7 @@ import org.ops4j.pax.exam.Option; import org.ops4j.pax.exam.OptionUtils; import org.ops4j.pax.exam.junit.PaxExam; +import org.ops4j.pax.exam.karaf.container.internal.JavaVersionUtil; import org.ops4j.pax.exam.karaf.options.KarafDistributionConfigurationFileExtendOption; import org.ops4j.pax.exam.options.CompositeOption; import org.ops4j.pax.exam.options.DefaultCompositeOption; @@ -142,8 +143,14 @@ public abstract class NexusPaxExamSupport public static final String S3_ENDPOINT_PROPERTY = "mock.s3.service.endpoint"; + private static final String PATCH_MODULE = "--patch-module"; + + private static final String KARAF_VERSION = "karaf.version"; + public static String TEST_JDBC_URL_PROPERTY = "nexus.test.jdbcUrl"; + private static final String ADD_OPENS = "--add-opens"; + private static final String DATABASE_KEY = "it.database"; private static final String BLOB_STORE_KEY = "it.blobstore"; @@ -794,7 +801,7 @@ public static void captureLogs(final TestIndex testIndex, final File logDir, fin testIndex.recordAndCopyLink("nexus.log", new File(logDir, "nexus.log")); testIndex.recordAndCopyLink("request.log", new File(logDir, "request.log")); testIndex.recordAndCopyLink("jvm.log", new File(logDir, "jvm.log")); - + if ("true".equals(System.getProperty("it.nexus.recordTaskLogs"))) { File tasksDir = new File(logDir, "tasks"); File[] taskLogs = tasksDir.listFiles(f -> f.getName().endsWith(".log")); @@ -896,13 +903,49 @@ private static Option nexusPaxExam() { return result; } - public static CompositeOption java11CompositeOption() { - if (!System.getProperty("java.version").startsWith("1.8")) { + public static CompositeOption javaVMCompositeOption() { + if(JavaVersionUtil.getMajorVersion() == 11) { return new DefaultCompositeOption( new VMOption("--add-exports=java.base/org.apache.karaf.specs.locator=java.xml,ALL-UNNAMED"), - new VMOption("--patch-module"), + new VMOption(PATCH_MODULE), new VMOption("java.base=lib/endorsed/org.apache.karaf.specs.locator-" + - System.getProperty("karaf.version") + ".jar")); + System.getProperty(KARAF_VERSION) + ".jar")); + } + else if (JavaVersionUtil.getMajorVersion() == 17) { + return new DefaultCompositeOption( + new VMOption("--add-reads=java.xml=java.logging"), + new VMOption("--add-exports=java.base/org.apache.karaf.specs.locator=java.xml,ALL-UNNAMED"), + new VMOption(PATCH_MODULE), + new VMOption("java.base=lib/endorsed/org.apache.karaf.specs.locator-" + + System.getProperty(KARAF_VERSION) + ".jar"), + new VMOption(PATCH_MODULE), new VMOption("java.xml=lib/endorsed/org.apache.karaf.specs.java.xml-" + + System.getProperty(KARAF_VERSION) + ".jar"), + new VMOption(ADD_OPENS), + new VMOption("java.base/java.security=ALL-UNNAMED"), + new VMOption(ADD_OPENS), + new VMOption("java.base/java.net=ALL-UNNAMED"), + new VMOption(ADD_OPENS), + new VMOption("java.base/java.lang=ALL-UNNAMED"), + new VMOption(ADD_OPENS), + new VMOption("java.base/java.util=ALL-UNNAMED"), + new VMOption(ADD_OPENS), + new VMOption("java.naming/javax.naming.spi=ALL-UNNAMED"), + new VMOption(ADD_OPENS), + new VMOption("java.rmi/sun.rmi.transport.tcp=ALL-UNNAMED"), + new VMOption("--add-exports=java.base/sun.net.www.protocol.file=ALL-UNNAMED"), + new VMOption("--add-exports=java.base/sun.net.www.protocol.ftp=ALL-UNNAMED"), + new VMOption("--add-exports=java.base/sun.net.www.protocol.http=ALL-UNNAMED"), + new VMOption("--add-exports=java.base/sun.net.www.protocol.https=ALL-UNNAMED"), + new VMOption("--add-exports=java.base/sun.net.www.protocol.jar=ALL-UNNAMED"), + new VMOption("--add-exports=java.base/sun.net.www.content.text=ALL-UNNAMED"), + new VMOption("--add-exports=jdk.naming.rmi/com.sun.jndi.url.rmi=ALL-UNNAMED"), + new VMOption("--add-exports=java.rmi/sun.rmi.registry=ALL-UNNAMED"), + new VMOption("--add-exports=jdk.xml.dom/org.w3c.dom.html=ALL-UNNAMED"), + new VMOption("--add-exports=java.security.sasl/com.sun.security.sasl=ALL-UNNAMED"), + new VMOption("-classpath"), + new VMOption("lib/jdk9plus/*" + File.pathSeparator + "lib/boot/*" + + File.pathSeparator + "lib/endorsed/*") + ); } return new DefaultCompositeOption(); } diff --git a/components/nexus-pax-exam/src/main/java/org/sonatype/nexus/pax/exam/distribution/BaseNexusTestDistribution.java b/components/nexus-pax-exam/src/main/java/org/sonatype/nexus/pax/exam/distribution/BaseNexusTestDistribution.java index 1fdd7279d1..9f45d934ef 100644 --- a/components/nexus-pax-exam/src/main/java/org/sonatype/nexus/pax/exam/distribution/BaseNexusTestDistribution.java +++ b/components/nexus-pax-exam/src/main/java/org/sonatype/nexus/pax/exam/distribution/BaseNexusTestDistribution.java @@ -23,7 +23,7 @@ import static org.ops4j.pax.exam.options.WrappedUrlProvisionOption.OverwriteMode.MERGE; import static org.sonatype.nexus.pax.exam.NexusPaxExamSupport.NEXUS_PROPERTIES_FILE; import static org.sonatype.nexus.pax.exam.NexusPaxExamSupport.SYSTEM_PROPERTIES_FILE; -import static org.sonatype.nexus.pax.exam.NexusPaxExamSupport.java11CompositeOption; +import static org.sonatype.nexus.pax.exam.NexusPaxExamSupport.javaVMCompositeOption; import static org.sonatype.nexus.pax.exam.NexusPaxExamSupport.nexusDistribution; import static org.sonatype.nexus.pax.exam.NexusPaxExamSupport.nexusFeature; import static org.sonatype.nexus.pax.exam.NexusPaxExamSupport.options; @@ -60,6 +60,11 @@ public Option[] distribution(final Distribution dist) { editConfigurationFileExtend(NEXUS_PROPERTIES_FILE, "nexus.test.base", "true"), mavenBundle("org.apache.aries.spifly", "org.apache.aries.spifly.dynamic.bundle").versionAsInProject(), + mavenBundle(ORG_OW2_ASM_GROUP_ID, "asm").versionAsInProject(), + mavenBundle(ORG_OW2_ASM_GROUP_ID, "asm-commons").versionAsInProject(), + mavenBundle(ORG_OW2_ASM_GROUP_ID, "asm-util").versionAsInProject(), + mavenBundle(ORG_OW2_ASM_GROUP_ID, "asm-tree").versionAsInProject(), + mavenBundle(ORG_OW2_ASM_GROUP_ID, "asm-analysis").versionAsInProject(), mavenBundle(GROOVY_GROUP_ID, "groovy").versionAsInProject(), mavenBundle(GROOVY_GROUP_ID, "groovy-ant").versionAsInProject(), mavenBundle(GROOVY_GROUP_ID, "groovy-json").versionAsInProject(), @@ -73,6 +78,6 @@ public Option[] distribution(final Distribution dist) { nexusFeature("org.sonatype.nexus.testsuite", "nexus-repository-testsupport"), wrappedBundle(maven("org.awaitility", "awaitility").versionAsInProject()).overwriteManifest(MERGE) .imports("*"), - java11CompositeOption()); + javaVMCompositeOption()); } } diff --git a/components/nexus-pax-exam/src/main/java/org/sonatype/nexus/pax/exam/distribution/NexusTestDistribution.java b/components/nexus-pax-exam/src/main/java/org/sonatype/nexus/pax/exam/distribution/NexusTestDistribution.java index 6e145d8795..b774ff721a 100644 --- a/components/nexus-pax-exam/src/main/java/org/sonatype/nexus/pax/exam/distribution/NexusTestDistribution.java +++ b/components/nexus-pax-exam/src/main/java/org/sonatype/nexus/pax/exam/distribution/NexusTestDistribution.java @@ -24,14 +24,16 @@ import static org.ops4j.pax.exam.options.WrappedUrlProvisionOption.OverwriteMode.MERGE; import static org.sonatype.nexus.pax.exam.NexusPaxExamSupport.NEXUS_PROPERTIES_FILE; import static org.sonatype.nexus.pax.exam.NexusPaxExamSupport.SYSTEM_PROPERTIES_FILE; -import static org.sonatype.nexus.pax.exam.NexusPaxExamSupport.java11CompositeOption; +import static org.sonatype.nexus.pax.exam.NexusPaxExamSupport.javaVMCompositeOption; import static org.sonatype.nexus.pax.exam.NexusPaxExamSupport.nexusFeature; public interface NexusTestDistribution { static final String GROOVY_GROUP_ID = "org.codehaus.groovy"; - public static enum Distribution + String ORG_OW2_ASM_GROUP_ID = "org.ow2.asm"; + + enum Distribution { BASE, OSS, PRO, PRO_STARTER } @@ -48,6 +50,11 @@ default Option[] configureNexus(final Option distribution) { editConfigurationFileExtend(NEXUS_PROPERTIES_FILE, "nexus.scripts.allowCreation", "true"), editConfigurationFileExtend(NEXUS_PROPERTIES_FILE, "nexus.search.event.handler.flushOnCount", "1"), mavenBundle("org.apache.aries.spifly", "org.apache.aries.spifly.dynamic.bundle").versionAsInProject(), + mavenBundle(ORG_OW2_ASM_GROUP_ID, "asm").versionAsInProject(), + mavenBundle(ORG_OW2_ASM_GROUP_ID, "asm-commons").versionAsInProject(), + mavenBundle(ORG_OW2_ASM_GROUP_ID, "asm-util").versionAsInProject(), + mavenBundle(ORG_OW2_ASM_GROUP_ID, "asm-tree").versionAsInProject(), + mavenBundle(ORG_OW2_ASM_GROUP_ID, "asm-analysis").versionAsInProject(), mavenBundle(GROOVY_GROUP_ID, "groovy").versionAsInProject(), mavenBundle(GROOVY_GROUP_ID, "groovy-ant").versionAsInProject(), mavenBundle(GROOVY_GROUP_ID, "groovy-json").versionAsInProject(), @@ -57,7 +64,7 @@ default Option[] configureNexus(final Option distribution) { // install common test-support features nexusFeature("org.sonatype.nexus.testsuite", "nexus-repository-testsupport"), wrappedBundle(maven("org.awaitility", "awaitility").versionAsInProject()).overwriteManifest(MERGE).imports("*"), - java11CompositeOption() + javaVMCompositeOption() ); } } diff --git a/components/nexus-repository-content/src/main/java/org/sonatype/nexus/repository/content/store/internal/AssetBlobCleanupTask.java b/components/nexus-repository-content/src/main/java/org/sonatype/nexus/repository/content/store/internal/AssetBlobCleanupTask.java index adb8d5f738..c4a6af5317 100644 --- a/components/nexus-repository-content/src/main/java/org/sonatype/nexus/repository/content/store/internal/AssetBlobCleanupTask.java +++ b/components/nexus-repository-content/src/main/java/org/sonatype/nexus/repository/content/store/internal/AssetBlobCleanupTask.java @@ -66,13 +66,6 @@ public class AssetBlobCleanupTask static final int BLOB_CREATED_DELAY_MINUTE = getInteger(PROPERTY_PREFIX + "blobCreatedDelayMinute", 60); - /** - * Comma-separated formats that will ignore batch deletion. Default value null. - * maven2,npm - */ - static final String BATCH_DELETE_IGNORE_FORMATS = - getString(PROPERTY_PREFIX + "batchDeleteIgnoreForFormat", null); - static final int BATCH_DELETE_POOL_SIZE = getInteger( PROPERTY_PREFIX + "batchDeleteThreadPoolSize", 8); @@ -94,8 +87,11 @@ public AssetBlobCleanupTask( } protected void initBatchDeleteIfEnabled(final String format) { - if (BATCH_DELETE_IGNORE_FORMATS != null && format != null - && BATCH_DELETE_IGNORE_FORMATS.contains(format)) { + String batchDeleteIgnoreFormats = + getString(PROPERTY_PREFIX + "batchDeleteIgnoreForFormat", null); + + if (batchDeleteIgnoreFormats != null && format != null + && batchDeleteIgnoreFormats.contains(format)) { batchDeleteEnabled = false; } else { batchDeleteExecutorService = newFixedThreadPool( @@ -107,7 +103,6 @@ protected void initBatchDeleteIfEnabled(final String format) { @Override protected Void execute() throws Exception { - String format = getConfiguration().getString(FORMAT_FIELD_ID); String contentStore = getConfiguration().getString(CONTENT_STORE_FIELD_ID); initBatchDeleteIfEnabled(format); diff --git a/components/nexus-repository-content/src/test/java/org/sonatype/nexus/repository/content/store/AssetBlobDAOTest.java b/components/nexus-repository-content/src/test/java/org/sonatype/nexus/repository/content/store/AssetBlobDAOTest.java index 696c92f516..1f7b90e17c 100644 --- a/components/nexus-repository-content/src/test/java/org/sonatype/nexus/repository/content/store/AssetBlobDAOTest.java +++ b/components/nexus-repository-content/src/test/java/org/sonatype/nexus/repository/content/store/AssetBlobDAOTest.java @@ -15,6 +15,7 @@ import java.sql.Connection; import java.sql.SQLException; import java.time.OffsetDateTime; +import java.time.temporal.ChronoUnit; import java.util.Collection; import org.sonatype.nexus.blobstore.api.BlobRef; @@ -172,7 +173,8 @@ public void testBlob() { OffsetDateTime blobCreated = OffsetDateTime.now().minusDays(1); dao.setBlobCreated(blobRef1, blobCreated); - assertThat(dao.readAssetBlob(blobRef1).get().blobCreated(), time(blobCreated)); + assertThat(dao.readAssetBlob(blobRef1).get().blobCreated().truncatedTo(ChronoUnit.SECONDS), + time(blobCreated.truncatedTo(ChronoUnit.SECONDS))); } } diff --git a/components/nexus-repository-content/src/test/java/org/sonatype/nexus/repository/content/store/AssetDAOTestSupport.java b/components/nexus-repository-content/src/test/java/org/sonatype/nexus/repository/content/store/AssetDAOTestSupport.java index 6bce768ee6..51a02c5bff 100644 --- a/components/nexus-repository-content/src/test/java/org/sonatype/nexus/repository/content/store/AssetDAOTestSupport.java +++ b/components/nexus-repository-content/src/test/java/org/sonatype/nexus/repository/content/store/AssetDAOTestSupport.java @@ -462,7 +462,9 @@ public void testAttachingBlobs() throws InterruptedException { assertTrue(tempResult.blob().isPresent()); assertThat(tempResult.blob().get(), sameBlob(assetBlob2)); assertThat(tempResult, sameBlob(asset)); - assertThat(tempResult.created(), is(oldCreated)); + + assertThat(tempResult.created().truncatedTo(ChronoUnit.SECONDS), is(oldCreated.truncatedTo(ChronoUnit.SECONDS))); + assertTrue(tempResult.lastUpdated().isAfter(oldLastUpdated)); assertEntityVersion(componentData.componentId, session.access(TestComponentDAO.class), entityVersionEnabled ? 4 : null); @@ -493,8 +495,11 @@ public void testAttachingBlobs() throws InterruptedException { assertTrue(tempResult.blob().isPresent()); assertThat(tempResult.blob().get(), sameBlob(assetBlob2)); assertThat(tempResult, sameBlob(asset)); - assertThat(tempResult.created(), is(oldCreated)); - assertThat(tempResult.lastUpdated(), is(oldLastUpdated)); + + assertThat(tempResult.created().truncatedTo(ChronoUnit.SECONDS), is(oldCreated.truncatedTo(ChronoUnit.SECONDS))); + assertThat(tempResult.lastUpdated().truncatedTo(ChronoUnit.SECONDS), + is(oldLastUpdated.truncatedTo(ChronoUnit.SECONDS))); + assertEntityVersion(componentData.componentId, session.access(TestComponentDAO.class), entityVersionEnabled ? 4 : null); @@ -522,7 +527,7 @@ public void testAttachingBlobs() throws InterruptedException { tempResult = dao.readPath(repositoryId, path).get(); assertFalse(tempResult.blob().isPresent()); - assertThat(tempResult.created(), is(oldCreated)); + assertThat(tempResult.created().truncatedTo(ChronoUnit.SECONDS), is(oldCreated.truncatedTo(ChronoUnit.SECONDS))); assertTrue(tempResult.lastUpdated().isAfter(oldLastUpdated)); assertEntityVersion(componentData.componentId, session.access(TestComponentDAO.class), entityVersionEnabled ? 5 : null); @@ -551,8 +556,11 @@ public void testAttachingBlobs() throws InterruptedException { tempResult = dao.readPath(repositoryId, path).get(); assertFalse(tempResult.blob().isPresent()); - assertThat(tempResult.created(), is(oldCreated)); - assertThat(tempResult.lastUpdated(), is(oldLastUpdated)); + + assertThat(tempResult.created().truncatedTo(ChronoUnit.SECONDS), is(oldCreated.truncatedTo(ChronoUnit.SECONDS))); + assertThat(tempResult.lastUpdated().truncatedTo(ChronoUnit.SECONDS), + is(oldLastUpdated.truncatedTo(ChronoUnit.SECONDS))); + assertEntityVersion(componentData.componentId, session.access(TestComponentDAO.class), entityVersionEnabled ? 5 : null); @@ -935,7 +943,10 @@ public void testSetLastDownloaded() { OffsetDateTime dateTime = OffsetDateTime.now(ZoneOffset.UTC).minusDays(1); dao.lastDownloaded(asset1.assetId, dateTime); - assertThat(dao.readAsset(asset1.assetId).get().lastDownloaded().orElse(null), is(dateTime)); + Optional actual = dao.readAsset(asset1.assetId).get().lastDownloaded(); + assertThat(actual.map(t-> t.truncatedTo(ChronoUnit.SECONDS)).orElse(null), + is(dateTime.truncatedTo(ChronoUnit.SECONDS))); + assertEntityVersion(componentData.componentId, session.access(TestComponentDAO.class), entityVersionEnabled ? 2 : null); } @@ -957,7 +968,9 @@ public void testLastUpdated() { OffsetDateTime dateTime = OffsetDateTime.now(ZoneOffset.UTC).minusDays(1); dao.lastUpdated(asset1.assetId, dateTime); - assertThat(dao.readAsset(asset1.assetId).get().lastUpdated(), is(dateTime)); + OffsetDateTime actual = dao.readAsset(asset1.assetId).get().lastUpdated(); + assertThat(actual.truncatedTo(ChronoUnit.SECONDS), is(dateTime.truncatedTo(ChronoUnit.SECONDS))); + assertEntityVersion(componentData.componentId, session.access(TestComponentDAO.class), entityVersionEnabled ? 2 : null); } @@ -1194,7 +1207,7 @@ public void testDeleteByPaths() { assertThat(countAssets(dao, repositoryId), is(5)); assertThat(dao.readPathsFromRepository(repositoryId, - asList(asset1.path(), asset2.path(), asset3.path(), asset4.path(), asset5.path())).size(), + asList(asset1.path(), asset2.path(), asset3.path(), asset4.path(), asset5.path())).size(), is(5)); assertEntityVersion(component1.componentId, session.access(TestComponentDAO.class), diff --git a/components/nexus-repository-content/src/test/java/org/sonatype/nexus/repository/content/store/AssetStoreTestSupport.java b/components/nexus-repository-content/src/test/java/org/sonatype/nexus/repository/content/store/AssetStoreTestSupport.java index c5ba7cb31b..9a8d7d265c 100644 --- a/components/nexus-repository-content/src/test/java/org/sonatype/nexus/repository/content/store/AssetStoreTestSupport.java +++ b/components/nexus-repository-content/src/test/java/org/sonatype/nexus/repository/content/store/AssetStoreTestSupport.java @@ -13,6 +13,7 @@ package org.sonatype.nexus.repository.content.store; import java.time.OffsetDateTime; +import java.time.temporal.ChronoUnit; import java.util.Collection; import java.util.Optional; import java.util.stream.Stream; @@ -252,7 +253,7 @@ public void testBrowseUpdatedAssetsDifferentDates() { } public void testBrowseUpdatedAssetsIdenticalDates() { - OffsetDateTime time1 = OffsetDateTime.now(); + OffsetDateTime time1 = OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS); AssetData asset1 = generateAsset(repositoryId, "/asset1/asset1.jar"); AssetData asset2 = generateAsset(repositoryId, "/asset2/asset2.jar"); AssetData asset3 = generateAsset(repositoryId, "/asset3/asset3.jar"); diff --git a/components/nexus-repository-content/src/test/java/org/sonatype/nexus/repository/content/store/ExampleContentTestSupport.java b/components/nexus-repository-content/src/test/java/org/sonatype/nexus/repository/content/store/ExampleContentTestSupport.java index 9022b1d919..d115296694 100644 --- a/components/nexus-repository-content/src/test/java/org/sonatype/nexus/repository/content/store/ExampleContentTestSupport.java +++ b/components/nexus-repository-content/src/test/java/org/sonatype/nexus/repository/content/store/ExampleContentTestSupport.java @@ -13,6 +13,7 @@ package org.sonatype.nexus.repository.content.store; import java.time.OffsetDateTime; +import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -453,7 +454,8 @@ protected static Matcher sameBlob(final Asset expected) { protected static Matcher sameBlob(final AssetBlob expected) { return new FieldMatcher<>(expected, AssetBlob::blobRef, AssetBlob::blobSize, AssetBlob::contentType, - AssetBlob::blobCreated, AssetBlob::createdBy, AssetBlob::createdByIp); + assetBlob -> assetBlob.blobCreated().truncatedTo(ChronoUnit.SECONDS), + AssetBlob::createdBy, AssetBlob::createdByIp); } protected static Matcher sameLastDownloaded(final Asset expected) { diff --git a/components/nexus-repository-content/src/test/java/org/sonatype/nexus/repository/content/store/internal/AssetBlobCleanupTaskTest.java b/components/nexus-repository-content/src/test/java/org/sonatype/nexus/repository/content/store/internal/AssetBlobCleanupTaskTest.java index 0fdbdfcb2f..acf3f921d9 100644 --- a/components/nexus-repository-content/src/test/java/org/sonatype/nexus/repository/content/store/internal/AssetBlobCleanupTaskTest.java +++ b/components/nexus-repository-content/src/test/java/org/sonatype/nexus/repository/content/store/internal/AssetBlobCleanupTaskTest.java @@ -13,10 +13,9 @@ package org.sonatype.nexus.repository.content.store.internal; import java.lang.reflect.Field; -import java.lang.reflect.Modifier; import java.util.UUID; - import java.util.concurrent.ExecutorService; + import org.sonatype.goodies.testsupport.TestSupport; import org.sonatype.nexus.blobstore.api.BlobId; import org.sonatype.nexus.blobstore.api.BlobRef; @@ -24,16 +23,13 @@ import org.sonatype.nexus.blobstore.api.BlobStoreManager; import org.sonatype.nexus.common.entity.Continuation; import org.sonatype.nexus.datastore.mybatis.ContinuationArrayList; -import org.sonatype.nexus.repository.content.AssetBlob; import org.sonatype.nexus.repository.content.store.AssetBlobData; import org.sonatype.nexus.repository.content.store.AssetBlobStore; -import org.sonatype.nexus.repository.content.store.BlobRefTypeHandler; import org.sonatype.nexus.repository.content.store.FormatStoreManager; import org.sonatype.nexus.scheduling.TaskConfiguration; import com.google.common.collect.ImmutableMap; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.InOrder; @@ -124,9 +120,8 @@ public void setUp() { @Test public void testUnusedBlobsAreDeleted() throws Exception { - AssetBlobCleanupTask task = new AssetBlobCleanupTask(ImmutableMap.of("raw", formatStoreManager), blobStoreManager); - setBatchDeleteIgnoreFinalField("raw"); + AssetBlobCleanupTask task = new AssetBlobCleanupTask(ImmutableMap.of("raw", formatStoreManager), blobStoreManager); TaskConfiguration taskConfiguration = new TaskConfiguration(); taskConfiguration.setString(FORMAT_FIELD_ID, "raw"); @@ -170,14 +165,12 @@ public void testUnusedBlobsAreDeleted() throws Exception { .browseUnusedAssetBlobs(BATCH_SIZE, BLOB_CREATED_DELAY_MINUTE, "EOL"); inOrder.verifyNoMoreInteractions(); - - setBatchDeleteIgnoreFinalField(null); } @Test public void testExecutorServiceShutdown() throws Exception { - AssetBlobCleanupTask task = spy(new AssetBlobCleanupTask(ImmutableMap.of("raw", formatStoreManager), blobStoreManager)); setBatchDeleteIgnoreFinalField(null); + AssetBlobCleanupTask task = spy(new AssetBlobCleanupTask(ImmutableMap.of("raw", formatStoreManager), blobStoreManager)); TaskConfiguration taskConfiguration = new TaskConfiguration(); taskConfiguration.setString(FORMAT_FIELD_ID, "raw"); @@ -204,10 +197,10 @@ public void testExecutorServiceShutdown() throws Exception { @Test public void testUnusedBlobsAreDeletedBatch() throws Exception { - AssetBlobCleanupTask task = new AssetBlobCleanupTask(ImmutableMap.of("raw", formatStoreManager), blobStoreManager); - setBatchDeleteIgnoreFinalField(null); + AssetBlobCleanupTask task = new AssetBlobCleanupTask(ImmutableMap.of("raw", formatStoreManager), blobStoreManager); + TaskConfiguration taskConfiguration = new TaskConfiguration(); taskConfiguration.setString(FORMAT_FIELD_ID, "raw"); taskConfiguration.setString(CONTENT_STORE_FIELD_ID, "content"); @@ -235,14 +228,9 @@ public void testUnusedBlobsAreDeletedBatch() throws Exception { inOrder.verifyNoMoreInteractions(); } - private void setBatchDeleteIgnoreFinalField(String batchDeleteFormats) - throws NoSuchFieldException, IllegalAccessException { - Field field = AssetBlobCleanupTask.class.getDeclaredField("BATCH_DELETE_IGNORE_FORMATS"); - field.setAccessible(true); - Field modifiers = Field.class.getDeclaredField("modifiers"); - modifiers.setAccessible(true); - modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL); - field.set(null, batchDeleteFormats); + private void setBatchDeleteIgnoreFinalField(String batchDeleteFormats) { + System.setProperty(AssetBlobCleanupTask.PROPERTY_PREFIX + "batchDeleteIgnoreForFormat", + batchDeleteFormats == null ? "" : batchDeleteFormats); } private AssetBlobData newAssetBlob() { diff --git a/components/nexus-security/src/main/java/org/sonatype/nexus/security/privilege/ApplicationPrivilegeDescriptor.java b/components/nexus-security/src/main/java/org/sonatype/nexus/security/privilege/ApplicationPrivilegeDescriptor.java index 13c1151aec..a2b93fe920 100644 --- a/components/nexus-security/src/main/java/org/sonatype/nexus/security/privilege/ApplicationPrivilegeDescriptor.java +++ b/components/nexus-security/src/main/java/org/sonatype/nexus/security/privilege/ApplicationPrivilegeDescriptor.java @@ -68,7 +68,7 @@ private interface Messages String actions(); @DefaultMessage("A comma-delimited list (without whitespace) of actions allowed with this privilege; " + - "options include create, read, update, delete, and a wildcard (*) " + + "options include create, read, update, delete, start, stop, associate, disassociate, and a wildcard (*) " + "Help") String actionsHelp(); @@ -98,13 +98,14 @@ public ApplicationPrivilegeDescriptor(@Named(REACT_PRIVILEGES_NAMED) final boole messages.actions(), messages.actionsCheckboxesHelp(), FormField.MANDATORY - ).withAttribute(P_OPTIONS, PrivilegeAction.getCrudActionStrings()) : + ).withAttribute(P_OPTIONS, PrivilegeAction.getCrudTaskActionStrings()) : new StringTextFormField( P_ACTIONS, messages.actions(), messages.actionsHelp(), FormField.MANDATORY, - "(^(create|read|update|delete)(,(create|read|update|delete)){0,3}$)|(^\\*$)" + "(^(create|read|update|delete|start|stop|associate|disassociate)" + + "(,(create|read|update|delete|start|stop|associate|disassociate)){0,3}$)|(^\\*$)" ) ); } diff --git a/components/nexus-security/src/main/java/org/sonatype/nexus/security/privilege/rest/PrivilegeAction.java b/components/nexus-security/src/main/java/org/sonatype/nexus/security/privilege/rest/PrivilegeAction.java index 16a0c57322..8e3976e92b 100644 --- a/components/nexus-security/src/main/java/org/sonatype/nexus/security/privilege/rest/PrivilegeAction.java +++ b/components/nexus-security/src/main/java/org/sonatype/nexus/security/privilege/rest/PrivilegeAction.java @@ -23,10 +23,14 @@ public enum PrivilegeAction { //the names/actions of these are very important, do not change without great consideration - READ("read"), BROWSE("browse"), EDIT("edit"), ADD("add"), DELETE("delete"), RUN("run"), ASSOCIATE("associate"), DISASSOCIATE("disassociate"), ALL("*"); + READ("read"), BROWSE("browse"), EDIT("edit"), ADD("add"), DELETE("delete"), RUN("run"), START("start"), STOP("stop"), ASSOCIATE("associate"), DISASSOCIATE("disassociate"), ALL("*"); private final String action; + private static final String CREATE = "create"; + + private static final String UPDATE = "update"; + PrivilegeAction(String action) { this.action = action; } @@ -66,13 +70,33 @@ public String getBreadRunAction() { public String getCrudAction() { switch (this) { case ADD: - return "create"; + return CREATE; + case EDIT: + return UPDATE; + case ASSOCIATE: + case DISASSOCIATE: + case READ: + case DELETE: + case ALL: + return action; + default: + return null; + } + } + + @Nullable + public String getCrudTaskActions() { + switch (this) { + case ADD: + return CREATE; case EDIT: - return "update"; + return UPDATE; case ASSOCIATE: case DISASSOCIATE: case READ: case DELETE: + case START: + case STOP: case ALL: return action; default: @@ -84,9 +108,9 @@ public String getCrudAction() { public static PrivilegeAction fromAction(final String action) { String trimmed = action.trim(); switch (trimmed) { - case "create": + case CREATE: return PrivilegeAction.ADD; - case "update": + case UPDATE: return PrivilegeAction.EDIT; default: return Arrays.stream(PrivilegeAction.values()).filter(a -> a.action.equals(action)).findFirst().orElse(null); @@ -119,10 +143,10 @@ public static List getBreadRunActionStrings() { .collect(Collectors.toList()); } - public static List getCrudActionStrings() { - return Arrays.asList(ADD, READ, EDIT, DELETE) + public static List getCrudTaskActionStrings() { + return Arrays.asList(ADD, READ, EDIT, DELETE, START, STOP, ASSOCIATE, DISASSOCIATE) .stream() - .map(PrivilegeAction::getCrudAction) + .map(PrivilegeAction::getCrudTaskActions) .collect(Collectors.toList()); } } diff --git a/components/nexus-ui-plugin/src/frontend/src/interface/ExtJS.js b/components/nexus-ui-plugin/src/frontend/src/interface/ExtJS.js index 70f125264c..6686201f66 100644 --- a/components/nexus-ui-plugin/src/frontend/src/interface/ExtJS.js +++ b/components/nexus-ui-plugin/src/frontend/src/interface/ExtJS.js @@ -180,13 +180,6 @@ export default class { return this.state().getEdition() === 'PRO'; } - /** - * @returns {boolean} true if the edition is PRO-STARTER - */ - static isProStarterEdition() { - return this.state().getEdition() === 'PRO-STARTER'; - } - /** * @param permission {string} * @returns {boolean} true if the user has the requested permission diff --git a/nxrm.groovy b/nxrm.groovy index 3bf4c10b69..3150ed1341 100644 --- a/nxrm.groovy +++ b/nxrm.groovy @@ -20,16 +20,15 @@ * - Takari (optional but recommended. Much quicker builds.) - see http://takari.io/book/30-team-maven.html#takari-smart-builder * To enable: Add 'takari=true' to .nxrm/nxrmrc.groovy */ -@Grab(group = 'ch.qos.logback', module = 'logback-classic', version = '1.2.9') @Grab(group = 'com.aestasit.infrastructure.sshoogr', module = 'sshoogr', version = '0.9.26') @Grab(group = 'com.caseyscarborough.colorizer', module = 'groovy-colorizer', version = '1.0.0') @Grab(group = 'jline', module = 'jline', version = '2.14.2') @Grab(group = 'org.ajoberstar', module = 'grgit', version = '2.2.1') -@Grab(group = 'org.apache.commons', module = 'commons-compress', version = '1.15') -@Grab(group = 'commons-io', module = 'commons-io', version = '2.6') -@Grab(group = 'org.apache.maven', module = 'maven-model', version = '3.5.0') +@Grab(group = 'org.apache.commons', module = 'commons-compress', version = '1.24.0') +@Grab(group = 'commons-io', module = 'commons-io', version = '2.13.0') +@Grab(group = 'org.apache.maven', module = 'maven-model', version = '3.8.1') @Grab(group = 'org.rauschig', module = 'jarchivelib', version = '1.2.0') -@Grab(group = 'com.google.guava', module = 'guava', version = '25.0-jre') +@Grab(group = 'com.google.guava', module = 'guava', version = '32.1.1-jre') import java.nio.file.Paths import java.time.ZonedDateTime @@ -43,20 +42,14 @@ import org.apache.maven.model.Model import org.apache.maven.model.io.xpp3.MavenXpp3Reader import org.rauschig.jarchivelib.ArchiveFormat import org.rauschig.jarchivelib.ArchiverFactory -import ch.qos.logback.classic.Logger import com.google.common.base.Stopwatch import java.nio.charset.StandardCharsets -import static ch.qos.logback.classic.Level.* import static com.aestasit.infrastructure.ssh.DefaultSsh.* import static java.time.ZoneId.systemDefault import static java.time.format.DateTimeFormatter.ofPattern -import static org.slf4j.Logger.ROOT_LOGGER_NAME as ROOT -import static org.slf4j.LoggerFactory.getLogger import java.util.zip.* -// set default log level (jgit seems to have something on DEBUG by default) -((Logger) getLogger(ROOT)).setLevel(INFO) ant = new AntBuilder() ant.project.buildListeners[0].messageOutputLevel = 0 @@ -425,7 +418,7 @@ Examples: positionalOptions = cliOptions.arguments() positionalOptions.removeAll { it == '-x' } - if(positionalOptions.size > 0) { + if(!positionalOptions.isEmpty()) { debug("Positional options: " + positionalOptions.toString()) } return true diff --git a/plugins/nexus-coreui-plugin/pom.xml b/plugins/nexus-coreui-plugin/pom.xml index 33d5385aa9..c15cba0eb4 100644 --- a/plugins/nexus-coreui-plugin/pom.xml +++ b/plugins/nexus-coreui-plugin/pom.xml @@ -100,12 +100,6 @@ provided - - org.sonatype.nexus - nexus-repository-services - provided - - org.sonatype.nexus nexus-testsupport diff --git a/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/user/Welcome/UsageMetrics.jsx b/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/user/Welcome/UsageMetrics.jsx index f0867a3320..487a4a7fa3 100644 --- a/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/user/Welcome/UsageMetrics.jsx +++ b/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/user/Welcome/UsageMetrics.jsx @@ -22,7 +22,7 @@ import {ExtJS} from '@sonatype/nexus-ui-plugin'; import UIStrings from '../../../../constants/UIStrings'; import UsageMetricsMachine from './UsageMetricsMachine'; -import UsageMetricsWithCircuitBreaker from './UsageMetricsWithCircuitBreaker'; +import UsageMetricsWithCircuitB from './UsageMetricsWithCircuitB'; import './UsageMetrics.scss'; @@ -41,19 +41,19 @@ export default function UsageMetrics() { const isProEdition = ExtJS.isProEdition(); const isHa = ExtJS.state().getValue('nexus.datastore.clustered.enabled'); - const isCircuitBreakerEnabled = ExtJS.state().getValue('nexus.circuitb.enabled'); + const isCircuitBEnabled = ExtJS.state().getValue('nexus.circuitb.enabled'); function retry() { send('RETRY'); }; - return !isHa &&
+ return !isHa &&
{() => <> {MENU.TEXT} - {isCircuitBreakerEnabled ? : + {isCircuitBEnabled ? : <> diff --git a/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/user/Welcome/UsageMetrics.test.jsx b/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/user/Welcome/UsageMetrics.test.jsx index d8260ff0e4..5e159d48a1 100644 --- a/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/user/Welcome/UsageMetrics.test.jsx +++ b/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/user/Welcome/UsageMetrics.test.jsx @@ -19,14 +19,16 @@ import UsageMetrics from './UsageMetrics'; import TestUtils from '@sonatype/nexus-ui-plugin/src/frontend/src/interface/TestUtils'; import {APIConstants, ExtJS} from '@sonatype/nexus-ui-plugin'; import {act} from 'react-dom/test-utils'; +import UIStrings from '../../../../constants/UIStrings'; import { METRICS_CONTENT, - METRICS_CONTENT_WITH_CIRCUIT_BREAKER_OSS, - METRICS_CONTENT_WITH_CIRCUIT_BREAKER_PRO, - METRICS_CONTENT_WITH_CIRCUIT_BREAKER_PRO_POSTGRESQL, - METRICS_CONTENT_WITH_CIRCUIT_BREAKER_PRO_STARTER} from './UsageMetrics.testdata'; + METRICS_CONTENT_WITH_CIRCUIT_B_OSS, + METRICS_CONTENT_WITH_CIRCUIT_B_PRO, + METRICS_CONTENT_WITH_CIRCUIT_B_PRO_POSTGRESQL, + METRICS_CONTENT_WITH_CIRCUIT_B_STARTER} from './UsageMetrics.testdata'; const {USAGE_METRICS} = APIConstants.REST.INTERNAL; +const {WELCOME: {USAGE: {CIRCUIT_B: {STARTER}}}} = UIStrings; jest.mock('axios', () => ({ ...jest.requireActual('axios'), @@ -37,10 +39,10 @@ jest.mock('@sonatype/nexus-ui-plugin', () => ({ ...jest.requireActual('@sonatype/nexus-ui-plugin'), ExtJS: { isProEdition: jest.fn().mockReturnValue(false), - isProStarterEdition: jest.fn().mockReturnValue(false), state: jest.fn().mockReturnValue({ getValue: jest.fn(), getUser: jest.fn().mockReturnValue({ administrator: true }), + getEdition: jest.fn().mockReturnValue('OSS') }), }, })); @@ -60,18 +62,24 @@ const selectors = { describe('Usage Metrics', () => { async function renderView(usage = METRICS_CONTENT, - usageWithCircuitBreaker = METRICS_CONTENT_WITH_CIRCUIT_BREAKER_OSS) + usageWithCircuitB = METRICS_CONTENT_WITH_CIRCUIT_B_OSS) { when(axios.get) .calledWith(USAGE_METRICS).mockResolvedValue({data: usage}); when(ExtJS.state().getValue) .calledWith('contentUsageEvaluationResult', []) - .mockReturnValue(usageWithCircuitBreaker); + .mockReturnValue(usageWithCircuitB); render(); await waitForElementToBeRemoved(selectors.queryLoadingMask()); - }; + } + + beforeEach(function() { + global.NX = { + I18n: {get: jest.fn()}, + }; + }) it('renders data correctly', async () => { await renderView(); @@ -136,7 +144,7 @@ describe('Usage Metrics', () => { expect(selectors.queryAllCards().length).toBe(0); }); - describe('Metrics with Circuit Breaker', () => { + describe('Metrics with Circuit B', () => { beforeEach( () => { when(ExtJS.state().getValue) .calledWith('nexus.circuitb.enabled') @@ -230,7 +238,7 @@ describe('Usage Metrics', () => { it('renders data correctly when PRO edition', async () => { ExtJS.isProEdition.mockReturnValue(true); - await renderView(METRICS_CONTENT, METRICS_CONTENT_WITH_CIRCUIT_BREAKER_PRO); + await renderView(METRICS_CONTENT, METRICS_CONTENT_WITH_CIRCUIT_B_PRO); expect(selectors.getHeading('Usage')).toBeInTheDocument(); expect(selectors.getAllCards().length).toBe(3); @@ -309,7 +317,7 @@ describe('Usage Metrics', () => { when(ExtJS.state().getValue) .calledWith('datastore.isPostgresql') .mockReturnValue(true); - await renderView(METRICS_CONTENT, METRICS_CONTENT_WITH_CIRCUIT_BREAKER_PRO_POSTGRESQL); + await renderView(METRICS_CONTENT, METRICS_CONTENT_WITH_CIRCUIT_B_PRO_POSTGRESQL); expect(selectors.getHeading('Usage')).toBeInTheDocument(); expect(selectors.getAllCards().length).toBe(3); @@ -374,7 +382,7 @@ describe('Usage Metrics', () => { it('renders tooltips when hovering on the info icon when PRO edition', async () => { ExtJS.isProEdition.mockReturnValue(true); - await renderView(METRICS_CONTENT, METRICS_CONTENT_WITH_CIRCUIT_BREAKER_PRO); + await renderView(METRICS_CONTENT, METRICS_CONTENT_WITH_CIRCUIT_B_PRO); const totalComponentsCard = selectors.getCard('Total Components'), reqsPerMinuteCard = selectors.getCard('Requests Per Minute'), @@ -393,9 +401,10 @@ describe('Usage Metrics', () => { 'Sonatype Nexus Repository Pro using an embedded database performs best when your requests per day remain under the threshold. If you are exceeding the threshold, we strongly recommend migrating to a PostgreSQL database.'); }); - it('renders data correctly when PRO STARTER edition', async () => { - ExtJS.isProStarterEdition.mockReturnValue(true); - await renderView(METRICS_CONTENT, METRICS_CONTENT_WITH_CIRCUIT_BREAKER_PRO_STARTER); + it('renders data correctly when STARTER edition', async () => { + when(NX.I18n.get).calledWith(STARTER).mockReturnValue('starter'); + ExtJS.state().getEdition.mockReturnValue('starter'); + await renderView(METRICS_CONTENT, METRICS_CONTENT_WITH_CIRCUIT_B_STARTER); expect(selectors.getHeading('Usage')).toBeInTheDocument(); expect(selectors.getAllCards().length).toBe(3); @@ -468,27 +477,6 @@ describe('Usage Metrics', () => { // only render link when reaching 75% or more of threshold expect(card3TextLink).not.toBeInTheDocument(); }); - - it('renders tooltips when hovering on the info icon when PRO STARTER edition', async () => { - ExtJS.isProStarterEdition.mockReturnValue(true); - await renderView(METRICS_CONTENT, METRICS_CONTENT_WITH_CIRCUIT_BREAKER_PRO_STARTER); - - const totalComponentsCard = selectors.getCard('Total Components'), - uniqueLoginsCard = selectors.getCard('Unique Logins'), - reqsPerDayCard = selectors.getCard('Requests Per Day'); - - let infoIcon = selectors.getCardInfoIcon(totalComponentsCard); - await TestUtils.expectToSeeTooltipOnHover(infoIcon, - 'Sonatype Nexus Repository\'s Pro Starter version only supports up to 120,000 components. Upgrade to Pro with a PostgreSQL database for unlimited component support.'); - - infoIcon = selectors.getCardInfoIcon(uniqueLoginsCard); - await TestUtils.expectToSeeTooltipOnHover(infoIcon, - 'Unique successful logins to this Sonatype Nexus Repository instance in the last 30 days.'); - - infoIcon = selectors.getCardInfoIcon(reqsPerDayCard); - await TestUtils.expectToSeeTooltipOnHover(infoIcon, - 'Sonatype Nexus Repository\'s Pro Starter version only supports up to 200,000 requests per day to repository endpoints for all repositories. Upgrade to Pro with a PostgreSQL database for unlimited requests.'); - }); }); }); diff --git a/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/user/Welcome/UsageMetrics.testdata.js b/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/user/Welcome/UsageMetrics.testdata.js index 8a970cc064..3ddbb47162 100644 --- a/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/user/Welcome/UsageMetrics.testdata.js +++ b/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/user/Welcome/UsageMetrics.testdata.js @@ -25,7 +25,7 @@ export const METRICS_CONTENT = { }] }; -export const METRICS_CONTENT_WITH_CIRCUIT_BREAKER_OSS = [ +export const METRICS_CONTENT_WITH_CIRCUIT_B_OSS = [ { "metricName": "peak_requests_per_day", "metricValue": 3300, @@ -35,7 +35,7 @@ export const METRICS_CONTENT_WITH_CIRCUIT_BREAKER_OSS = [ "thresholdValue": 20000 } ], - "usageLevel": "FREE_TIER", + "utilization": "FREE_TIER", "aggregates": [ { "name": "content_request_count", @@ -53,7 +53,7 @@ export const METRICS_CONTENT_WITH_CIRCUIT_BREAKER_OSS = [ "thresholdValue": 100000 } ], - "usageLevel": "FREE_TIER", + "utilization": "FREE_TIER", "aggregates": [ { "name": "component_total_count", @@ -71,7 +71,7 @@ export const METRICS_CONTENT_WITH_CIRCUIT_BREAKER_OSS = [ "thresholdValue": 100 } ], - "usageLevel": "FREE_TIER", + "utilization": "FREE_TIER", "aggregates": [ { "name": "unique_user_count", @@ -82,7 +82,7 @@ export const METRICS_CONTENT_WITH_CIRCUIT_BREAKER_OSS = [ } ]; -export const METRICS_CONTENT_WITH_CIRCUIT_BREAKER_PRO = [ +export const METRICS_CONTENT_WITH_CIRCUIT_B_PRO = [ { "metricName": "peak_requests_per_day", "metricValue": 12500, @@ -92,7 +92,7 @@ export const METRICS_CONTENT_WITH_CIRCUIT_BREAKER_PRO = [ "thresholdValue": 20000 } ], - "usageLevel": "FREE_TIER", + "utilization": "FREE_TIER", "aggregates": [ { "name": "content_request_count", @@ -125,7 +125,7 @@ export const METRICS_CONTENT_WITH_CIRCUIT_BREAKER_PRO = [ "thresholdValue": 100000 } ], - "usageLevel": "FREE_TIER", + "utilization": "FREE_TIER", "aggregates": [ { "name": "component_total_count", @@ -136,12 +136,12 @@ export const METRICS_CONTENT_WITH_CIRCUIT_BREAKER_PRO = [ } ]; -export const METRICS_CONTENT_WITH_CIRCUIT_BREAKER_PRO_POSTGRESQL = [ +export const METRICS_CONTENT_WITH_CIRCUIT_B_PRO_POSTGRESQL = [ { "metricName": "peak_requests_per_day_30d", "metricValue": 145302, "thresholds": [], - "usageLevel": "UNLIMITED", + "utilization": "FULL", "aggregates": [] }, { @@ -153,12 +153,12 @@ export const METRICS_CONTENT_WITH_CIRCUIT_BREAKER_PRO_POSTGRESQL = [ "metricName": "component_total_count", "metricValue": 4758, "thresholds": [], - "usageLevel": "UNLIMITED", + "utilization": "FULL", "aggregates": [] } ]; -export const METRICS_CONTENT_WITH_CIRCUIT_BREAKER_PRO_STARTER = [ +export const METRICS_CONTENT_WITH_CIRCUIT_B_STARTER = [ { "metricName": "peak_requests_per_day", "metricValue": 5800, @@ -172,7 +172,7 @@ export const METRICS_CONTENT_WITH_CIRCUIT_BREAKER_PRO_STARTER = [ "thresholdValue": 200000 } ], - "usageLevel": "FREE_TIER", + "utilization": "FREE_TIER", "aggregates": [ { "name": "content_request_count", @@ -194,7 +194,7 @@ export const METRICS_CONTENT_WITH_CIRCUIT_BREAKER_PRO_STARTER = [ "thresholdValue": 120000 } ], - "usageLevel": "STARTER_THRESHOLD", + "utilization": "STARTER_THRESHOLD", "aggregates": [ { "name": "component_total_count", @@ -212,7 +212,7 @@ export const METRICS_CONTENT_WITH_CIRCUIT_BREAKER_PRO_STARTER = [ "thresholdValue": 100 } ], - "usageLevel": "FREE_TIER", + "utilization": "FREE_TIER", "aggregates": [ { "name": "unique_user_count", diff --git a/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/user/Welcome/UsageMetricsAlert.jsx b/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/user/Welcome/UsageMetricsAlert.jsx index 57437989c3..2a33893c74 100644 --- a/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/user/Welcome/UsageMetricsAlert.jsx +++ b/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/user/Welcome/UsageMetricsAlert.jsx @@ -22,11 +22,12 @@ import './UsageMetricsAlert.scss'; const { WELCOME: { USAGE: { - CIRCUIT_BREAKER: { + CIRCUIT_B: { PERCENTAGE, TOTAL_COMPONENTS, REQUESTS_PER_DAY, - STARTER_THRESHOLD + STARTER_THRESHOLD, + STARTER }, ALERTS: { EXCEEDING_THRESHOLDS, @@ -42,7 +43,7 @@ const MessageContent = function({metricMessage, threshold}){ const suffix = NX.I18n.get(SUFFIX); return

- {replace('{}', threshold, prefix)} + {replace('{}', threshold, prefix || '')} {REVIEW_YOUR_USAGE.TEXT} {mid} {UPGRADING_PRO.TEXT} @@ -62,7 +63,7 @@ const AlertContent = function({metric, content}) { export default function UsageMetricsAlert({onClose}) { const metrics = ExtJS.state().getValue('contentUsageEvaluationResult', []); - const isProStarterEdition = ExtJS.isProStarterEdition(); + const isStarterEdition = ExtJS.state().getEdition() === NX.I18n.get(STARTER); const approachingThresholdMetrics = metrics.filter(m => { const thresholds = m.thresholds ?? []; @@ -76,15 +77,15 @@ export default function UsageMetricsAlert({onClose}) { const exceedingThresholdMetrics = metrics.filter(m => { const thresholds = m.thresholds ?? []; const threshold = thresholds.find(l => l.thresholdName === STARTER_THRESHOLD); - if (m.metricName && m.usageLevel && threshold && threshold.thresholdValue && !isNaN(threshold.thresholdValue)) { - return (m.metricName === REQUESTS_PER_DAY.METRIC_NAME && m.usageLevel === STARTER_THRESHOLD) || - (m.metricName === TOTAL_COMPONENTS.METRIC_NAME && m.usageLevel === STARTER_THRESHOLD) + if (m.metricName && m.utilization && threshold && threshold.thresholdValue && !isNaN(threshold.thresholdValue)) { + return (m.metricName === REQUESTS_PER_DAY.METRIC_NAME && m.utilization === STARTER_THRESHOLD) || + (m.metricName === TOTAL_COMPONENTS.METRIC_NAME && m.utilization === STARTER_THRESHOLD) } }); const showApproachingThresholdAlert = isEmpty(exceedingThresholdMetrics) && !isEmpty(approachingThresholdMetrics); - return isProStarterEdition && <> + return isStarterEdition && <> {!isEmpty(exceedingThresholdMetrics) &&

{exceedingThresholdMetrics.map(m => )} diff --git a/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/user/Welcome/UsageMetricsAlert.test.jsx b/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/user/Welcome/UsageMetricsAlert.test.jsx index 345fe7d604..1dab5bb557 100644 --- a/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/user/Welcome/UsageMetricsAlert.test.jsx +++ b/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/user/Welcome/UsageMetricsAlert.test.jsx @@ -24,7 +24,7 @@ import { NO_THRESHOLDS_DATA, STARTER_THRESHOLD_REACHED, SOFT_THRESHOLD_REACHED, - NO_USAGE_LEVEL_DATA + NO_UTILIZATION_DATA } from './UsageMetricsAlert.testdata'; jest.mock('@sonatype/nexus-ui-plugin', () => ({ @@ -32,8 +32,8 @@ jest.mock('@sonatype/nexus-ui-plugin', () => ({ ExtJS: { state: jest.fn().mockReturnValue({ getValue: jest.fn(), + getEdition: jest.fn().mockReturnValue('starter') }), - isProStarterEdition: jest.fn().mockReturnValue(true), } })); @@ -55,7 +55,7 @@ describe('Usage Metrics Alert', () => { beforeEach(function() { global.NX = { - I18n: {get: jest.fn().mockReturnValue('')}, + I18n: {get: jest.fn().mockReturnValue('starter')}, }; }) @@ -93,8 +93,8 @@ describe('Usage Metrics Alert', () => { expect(alert).not.toBeInTheDocument(); }); - it('should not render alert when there is no usage level', async () => { - await renderView(NO_USAGE_LEVEL_DATA); + it('should not render alert when there is no utilization', async () => { + await renderView(NO_UTILIZATION_DATA); const alert = selectors.queryAlert(); expect(alert).not.toBeInTheDocument(); }); @@ -123,8 +123,8 @@ describe('Usage Metrics Alert', () => { expect(alert).not.toBeInTheDocument(); }); - it('should not render alert when edition is not pro-starter', async () => { - ExtJS.isProStarterEdition.mockReturnValue(false); + it('should not render alert when edition is not starter', async () => { + ExtJS.state().getEdition.mockReturnValue('pro'); await renderView(SOFT_THRESHOLD_REACHED); const alert = selectors.queryAlert(); expect(alert).not.toBeInTheDocument(); diff --git a/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/user/Welcome/UsageMetricsAlert.testdata.js b/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/user/Welcome/UsageMetricsAlert.testdata.js index cf2b56d4e9..ab04b0d2f7 100644 --- a/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/user/Welcome/UsageMetricsAlert.testdata.js +++ b/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/user/Welcome/UsageMetricsAlert.testdata.js @@ -28,7 +28,7 @@ export const STARTER_THRESHOLD_REACHED = [ "thresholdValue": 200000 } ], - "usageLevel": "STARTER_THRESHOLD" + "utilization": "STARTER_THRESHOLD" }, { "metricName": "component_total_count", @@ -43,7 +43,7 @@ export const STARTER_THRESHOLD_REACHED = [ "thresholdValue": 120000 } ], - "usageLevel": "STARTER_THRESHOLD" + "utilization": "STARTER_THRESHOLD" }, { "metricName": "successful_last_24h", @@ -54,7 +54,7 @@ export const STARTER_THRESHOLD_REACHED = [ "thresholdValue": 75 } ], - "usageLevel": "FREE_TIER" + "utilization": "FREE_TIER" } ]; @@ -72,7 +72,7 @@ export const SOFT_THRESHOLD_REACHED = [ "thresholdValue": 200000 } ], - "usageLevel": "SOFT_THRESHOLD" + "utilization": "SOFT_THRESHOLD" }, { "metricName": "component_total_count", @@ -87,7 +87,7 @@ export const SOFT_THRESHOLD_REACHED = [ "thresholdValue": 120000 } ], - "usageLevel": "FREE_TIER" + "utilization": "FREE_TIER" }, { "metricName": "successful_last_24h", @@ -98,7 +98,7 @@ export const SOFT_THRESHOLD_REACHED = [ "thresholdValue": 75 } ], - "usageLevel": "FREE_TIER" + "utilization": "FREE_TIER" } ]; @@ -106,7 +106,7 @@ export const NO_THRESHOLDS_DATA = [ { "metricName": "component_total_count", "metricValue": 90000, - "usageLevel": "FREE_TIER" + "utilization": "FREE_TIER" }, ] @@ -123,7 +123,7 @@ export const NO_THRESHOLD_VALUE_DATA = [ "thresholdName": "STARTER_THRESHOLD", } ], - "usageLevel": "STARTER_THRESHOLD" + "utilization": "STARTER_THRESHOLD" } ] @@ -140,7 +140,7 @@ export const NO_THRESHOLD_NAME_DATA = [ "thresholdValue": 187500 } ], - "usageLevel": "STARTER_THRESHOLD" + "utilization": "STARTER_THRESHOLD" } ] @@ -158,11 +158,11 @@ export const INVALID_THRESHOLD_VALUE_DATA = [ "thresholdValue": "XYZ" } ], - "usageLevel": "STARTER_THRESHOLD" + "utilization": "STARTER_THRESHOLD" } ] -export const NO_USAGE_LEVEL_DATA = [ +export const NO_UTILIZATION_DATA = [ { "metricName": "peak_requests_per_day", "metricValue": 200000, diff --git a/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/user/Welcome/UsageMetricsWithCircuitBreaker.jsx b/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/user/Welcome/UsageMetricsWithCircuitB.jsx similarity index 89% rename from plugins/nexus-coreui-plugin/src/frontend/src/components/pages/user/Welcome/UsageMetricsWithCircuitBreaker.jsx rename to plugins/nexus-coreui-plugin/src/frontend/src/components/pages/user/Welcome/UsageMetricsWithCircuitB.jsx index a66fe35795..37a79b7bc3 100644 --- a/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/user/Welcome/UsageMetricsWithCircuitBreaker.jsx +++ b/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/user/Welcome/UsageMetricsWithCircuitB.jsx @@ -20,19 +20,19 @@ import { NxTextLink, NxTooltip} from '@sonatype/react-shared-components'; import {faExclamationCircle, faInfoCircle} from '@fortawesome/free-solid-svg-icons'; -import {indexBy, pathOr, prop} from 'ramda'; +import {indexBy, pathOr, prop, replace} from 'ramda'; import classNames from 'classnames'; import UIStrings from '../../../../constants/UIStrings'; -import './UsageMetricsWithCircuitBreaker.scss'; +import './UsageMetricsWithCircuitB.scss'; const { WELCOME: { USAGE: { - CIRCUIT_BREAKER, + CIRCUIT_B, CARD_LINK_OSS, CARD_LINK_PRO, - CARD_LINK_PRO_STARTER}}} = UIStrings; + CARD_LINK_STARTER}}} = UIStrings; const { TOTAL_COMPONENTS, @@ -43,14 +43,14 @@ const { SOFT_THRESHOLD, STARTER_THRESHOLD, PRO, - PRO_STARTER, OSS, + STARTER, CARD_SHARED_LABELS: { THRESHOLD, THRESHOLD_NAME, THRESHOLD_VALUE, PERIOD, - VALUE}} = CIRCUIT_BREAKER; + VALUE}} = CIRCUIT_B; function Card({card, usage}) { const {METRIC_NAME_PRO_POSTGRESQL, SUB_TITLE_PRO_POSTGRESQL, TITLE, TITLE_PRO_POSTGRESQL} = card; @@ -76,19 +76,19 @@ function CardWithThreshold({card, usage, link, tooltip, edition}) { const {AGGREGATE_PERIOD_30_D, HIGHEST_RECORDED_COUNT, METRIC_NAME, SUB_TITLE, TITLE} = card; const cardData = usage.find(m => m.metricName === METRIC_NAME) ?? []; const {aggregates = [], thresholds = [], metricValue = 0} = cardData; - const thresholdType = edition === PRO_STARTER ? STARTER_THRESHOLD : SOFT_THRESHOLD; + const thresholdType = edition === STARTER ? STARTER_THRESHOLD : SOFT_THRESHOLD; const thresholdValue = pathOr(0, [thresholdType, THRESHOLD_VALUE], indexBy(prop(THRESHOLD_NAME), thresholds)); const approachingThreshold = metricValue >= thresholdValue * PERCENTAGE; const exceedingThreshold = metricValue >= thresholdValue; const highestRecordedCount = pathOr(0, [AGGREGATE_PERIOD_30_D, VALUE], indexBy(prop(PERIOD), aggregates)); const showErrorIcon = highestRecordedCount >= thresholdValue; const meterClassNames = classNames({ - 'pro-starter-edition': edition === PRO_STARTER, + 'starter-edition': edition === STARTER, 'nxrm-meter-approaching' : approachingThreshold && !exceedingThreshold, 'nxrm-meter-exceeding' : exceedingThreshold }); const errorIconClassNames = classNames({ - 'pro-starter-edition': edition === PRO_STARTER, + 'starter-edition': edition === STARTER, 'recorded-count-with-error-icon': showErrorIcon }); @@ -96,7 +96,7 @@ function CardWithThreshold({card, usage, link, tooltip, edition}) { {TITLE} - + @@ -165,9 +165,9 @@ function CardWithoutThreshold({card, usage, tooltip}) { } -export default function UsageMetricsWithCircuitBreaker() { +export default function UsageMetricsWithCircuitB() { const isProEdition = ExtJS.isProEdition(); - const isProStarterEdition = ExtJS.isProStarterEdition(); + const isStarterEdition = ExtJS.state().getEdition() === NX.I18n.get(STARTER); const isPostgresql = ExtJS.state().getValue('datastore.isPostgresql'); const usage = ExtJS.state().getValue('contentUsageEvaluationResult', []); @@ -183,11 +183,11 @@ export default function UsageMetricsWithCircuitBreaker() { - } else if (isProStarterEdition) { + } else if (isStarterEdition) { return <> - - - + + + } else { return <> diff --git a/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/user/Welcome/UsageMetricsWithCircuitBreaker.scss b/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/user/Welcome/UsageMetricsWithCircuitB.scss similarity index 95% rename from plugins/nexus-coreui-plugin/src/frontend/src/components/pages/user/Welcome/UsageMetricsWithCircuitBreaker.scss rename to plugins/nexus-coreui-plugin/src/frontend/src/components/pages/user/Welcome/UsageMetricsWithCircuitB.scss index 4aa907d779..3de27a515f 100644 --- a/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/user/Welcome/UsageMetricsWithCircuitBreaker.scss +++ b/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/user/Welcome/UsageMetricsWithCircuitB.scss @@ -12,7 +12,7 @@ */ $card-width: 1016px; -.nxrm-usage-metrics-circuit-breaker { +.nxrm-usage-metrics-circuit-b { box-sizing: border-box; padding: var(--nx-spacing-6x) 0 0 var(--nx-spacing-6x); @@ -91,11 +91,11 @@ $card-width: 1016px; background: var(--nx-swatch-orange-50); } - &.pro-starter-edition::-webkit-meter-optimum-value { + &.starter-edition::-webkit-meter-optimum-value { background: var(--nx-swatch-red-40); } - &.pro-starter-edition::-moz-meter-bar { + &.starter-edition::-moz-meter-bar { background: var(--nx-swatch-red-40); } } @@ -151,7 +151,7 @@ $card-width: 1016px; .recorded-count-with-error-icon { color: var(--nx-swatch-orange-45); - &.pro-starter-edition { + &.starter-edition { color: var(--nx-swatch-red-40); } } diff --git a/plugins/nexus-coreui-plugin/src/frontend/src/constants/pages/user/WelcomeStrings.jsx b/plugins/nexus-coreui-plugin/src/frontend/src/constants/pages/user/WelcomeStrings.jsx index e3acb3e374..933a8b7c66 100644 --- a/plugins/nexus-coreui-plugin/src/frontend/src/constants/pages/user/WelcomeStrings.jsx +++ b/plugins/nexus-coreui-plugin/src/frontend/src/constants/pages/user/WelcomeStrings.jsx @@ -83,7 +83,7 @@ export default { TITLE: 'Peak requests per day', SUB_TITLE: 'Past 30 days' }, - CIRCUIT_BREAKER: { + CIRCUIT_B: { TOTAL_COMPONENTS: { TITLE: 'Total Components', SUB_TITLE: 'Current', @@ -91,13 +91,13 @@ export default { METRIC_NAME: 'component_total_count', METRIC_NAME_PRO_POSTGRESQL: 'component_total_count', AGGREGATE_PERIOD_30_D: 'peak_recorded_count_30d', - TOOLTIP: (threshold, edition) => { - if (edition === 'PRO-STARTER') { - return `Sonatype Nexus Repository\'s Pro Starter version only supports up to ${threshold} components. Upgrade to Pro with a PostgreSQL database for unlimited component support.` + TOOLTIP: (edition) => { + if (edition === 'Starter_Edition') { + return NX.I18n.get('Total_Components_Tooltip'); } else if (edition === 'PRO') { return 'Sonatype Nexus Repository Pro using an embedded database performs best when your total component counts remain under the threshold. If you are exceeding the threshold, we strongly recommend migrating to a PostgreSQL database.' } else { - return `Sonatype Nexus Repository OSS performs best when your total component counts remain under ${threshold} components across all repositories in your instance.` + return 'Sonatype Nexus Repository OSS performs best when your total component counts remain under {} components across all repositories in your instance.' } } }, @@ -108,7 +108,7 @@ export default { METRIC_NAME: 'successful_last_24h', AGGREGATE_PERIOD_30_D: 'peak_recorded_count_30d', TOOLTIP: 'Measures unique users who login over a period of time.', - TOOLTIP_PRO_STARTER: 'Unique successful logins to this Sonatype Nexus Repository instance in the last 30 days.' + TOOLTIP_STARTER: 'Unique successful logins to this Sonatype Nexus Repository instance in the last 30 days.' }, REQUESTS_PER_MINUTE: { TITLE: 'Requests Per Minute', @@ -131,13 +131,13 @@ export default { METRIC_NAME: 'peak_requests_per_day', METRIC_NAME_PRO_POSTGRESQL: 'peak_requests_per_day_30d', AGGREGATE_PERIOD_30_D: 'peak_recorded_count_30d', - TOOLTIP: (threshold, edition) => { - if (edition === 'PRO-STARTER') { - return `Sonatype Nexus Repository\'s Pro Starter version only supports up to ${threshold} requests per day to repository endpoints for all repositories. Upgrade to Pro with a PostgreSQL database for unlimited requests.` + TOOLTIP: (edition) => { + if (edition === 'Starter_Edition') { + return NX.I18n.get('Requests_Per_Day_Tooltip') } else if (edition === 'PRO') { return 'Sonatype Nexus Repository Pro using an embedded database performs best when your requests per day remain under the threshold. If you are exceeding the threshold, we strongly recommend migrating to a PostgreSQL database.' } else { - return `Sonatype Nexus Repository OSS performs best when requests per day remain under ${threshold} requests per day to all repository endpoints across all repositories in your instance.` + return `Sonatype Nexus Repository OSS performs best when requests per day remain under {} requests per day to all repository endpoints across all repositories in your instance.` } } }, @@ -153,7 +153,7 @@ export default { STARTER_THRESHOLD: 'STARTER_THRESHOLD', PRO: 'PRO', OSS: 'OSS', - PRO_STARTER: 'PRO-STARTER' + STARTER: 'Starter_Edition', }, CARD_LINK_OSS: { TEXT: 'Understand your usage', @@ -163,7 +163,7 @@ export default { TEXT: 'Understand your usage', URL: 'https://links.sonatype.com/products/nxrm3/docs/optimize-performance-pro' }, - CARD_LINK_PRO_STARTER: { + CARD_LINK_STARTER: { TEXT: 'Understand your usage', URL: 'https://links.sonatype.com/products/nxrm3/docs/review-usage' }, diff --git a/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/view/react/FooterContainer.js b/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/view/react/FooterContainer.js index 570c5a97cb..9856c60086 100644 --- a/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/view/react/FooterContainer.js +++ b/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/view/react/FooterContainer.js @@ -65,10 +65,9 @@ Ext.define('NX.coreui.view.react.FooterContainer', { const user = NX.State.getUser(); const isAdmin = user ? user.administrator : false; const isHa = NX.State.getValue('nexus.datastore.clustered.enabled'); - const isProStarter = NX.State.getEdition() === 'PRO-STARTER'; const metrics = NX.State.getValue('contentUsageEvaluationResult', []); - this.setVisible(isAdmin && !isHa && isProStarter && (metrics.length > 0)); + this.setVisible(isAdmin && !isHa && (metrics.length > 0)); this.updateLayout(); }, diff --git a/plugins/nexus-coreui-plugin/src/test/java/org/sonatype/nexus/coreui/internal/privileges/PrivilegesUIResourceTest.java b/plugins/nexus-coreui-plugin/src/test/java/org/sonatype/nexus/coreui/internal/privileges/PrivilegesUIResourceTest.java index f8e1bca172..101ccaca15 100644 --- a/plugins/nexus-coreui-plugin/src/test/java/org/sonatype/nexus/coreui/internal/privileges/PrivilegesUIResourceTest.java +++ b/plugins/nexus-coreui-plugin/src/test/java/org/sonatype/nexus/coreui/internal/privileges/PrivilegesUIResourceTest.java @@ -32,7 +32,6 @@ import org.sonatype.nexus.script.plugin.internal.security.ScriptPrivilegeDescriptor; import org.sonatype.nexus.security.privilege.ApplicationPrivilegeDescriptor; import org.sonatype.nexus.security.privilege.PrivilegeDescriptor; -import org.sonatype.nexus.security.privilege.rest.PrivilegeAction; import org.sonatype.nexus.selector.SelectorConfiguration; import org.sonatype.nexus.selector.SelectorManager; @@ -57,11 +56,12 @@ public class PrivilegesUIResourceTest public static final String ACTIONS_KEY = "options"; - public static final List CRUD_ACTION_STRINGS = PrivilegeAction.getCrudActionStrings(); + public static final List CRUD_ACTION_STRINGS = Arrays.asList( + "create", "read", "update", "delete", "start", "stop", "associate", "disassociate"); - public static final List BREAD_ACTION_STRINGS = PrivilegeAction.getBreadActionStrings(); + public static final List BREAD_ACTION_STRINGS = Arrays.asList("browse", "read", "edit", "add", "delete"); - public static final List BREAD_RUN_ACTION_STRINGS = PrivilegeAction.getBreadRunActionStrings(); + public static final List BREAD_RUN_ACTION_STRINGS = Arrays.asList("browse", "read", "edit", "add", "delete", "run"); @Mock private RepositoryManager repositoryManager; diff --git a/pom.xml b/pom.xml index a222dde6ae..8e12059362 100644 --- a/pom.xml +++ b/pom.xml @@ -65,7 +65,7 @@ 3.69.0-SNAPSHOT 2.15.1-02 - 4.3.9 + 4.4.2 v18.17.1 @@ -684,64 +684,6 @@ - - - org.apache.maven.plugins - maven-surefire-plugin - 3.2.5 - - false - 1 - - true - - ${surefire.maxHeap} - -XX:MaxDirectMemorySize=2G - -XX:+HeapDumpOnOutOfMemoryError - -XX:HeapDumpPath=${project.build.directory}/surefire-reports - - - **/Abstract* - **/*$* - **/*Tests - - ${it.includedGroups} - ${it.excludedGroups} - - - - - - org.apache.maven.plugins - maven-failsafe-plugin - 3.2.5 - - false - 1 - false - - - -Xmx400M - -XX:MaxDirectMemorySize=2G - -XX:+HeapDumpOnOutOfMemoryError - -XX:HeapDumpPath=${project.build.directory}/failsafe-reports - - - **/Abstract* - **/*$* - - - ${it.includes} - - ${it.includedGroups} - ${it.excludedGroups} - - - @@ -1363,6 +1305,221 @@ + + surefire-jdk8 + + 1.8 + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.2.5 + + false + 1 + + true + + ${surefire.maxHeap} + -XX:MaxDirectMemorySize=2G + -XX:+HeapDumpOnOutOfMemoryError + -XX:HeapDumpPath=${project.build.directory}/surefire-reports + + + **/Abstract* + **/*$* + **/*Tests + + ${it.includedGroups} + ${it.excludedGroups} + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + 3.2.5 + + false + 1 + false + + + -Xmx400M + -XX:MaxDirectMemorySize=2G + -XX:+HeapDumpOnOutOfMemoryError + -XX:HeapDumpPath=${project.build.directory}/failsafe-reports + + + **/Abstract* + **/*$* + + + ${it.includes} + + ${it.includedGroups} + ${it.excludedGroups} + + + + + + + + + surefire-jdk11 + + 11 + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.2.5 + + false + 1 + + true + + ${surefire.maxHeap} + -XX:MaxDirectMemorySize=2G + -XX:+HeapDumpOnOutOfMemoryError + -XX:HeapDumpPath=${project.build.directory}/surefire-reports + + + **/Abstract* + **/*$* + **/*Tests + + ${it.includedGroups} + ${it.excludedGroups} + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + 3.2.5 + + false + 1 + false + + + -Xmx400M + -XX:MaxDirectMemorySize=2G + -XX:+HeapDumpOnOutOfMemoryError + -XX:HeapDumpPath=${project.build.directory}/failsafe-reports + + + **/Abstract* + **/*$* + + + ${it.includes} + + ${it.includedGroups} + ${it.excludedGroups} + + + + + + + + + + + --add-exports=java.base/sun.nio.ch=ALL-UNNAMED + --add-opens=java.base/java.net=ALL-UNNAMED + --add-opens=java.base/java.util=ALL-UNNAMED + + + surefire-jdk17 + + 17 + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.2.5 + + false + 1 + + true + + @{argLine} + ${surefire.maxHeap} + -XX:MaxDirectMemorySize=2G + -XX:+HeapDumpOnOutOfMemoryError + -XX:HeapDumpPath=${project.build.directory}/surefire-reports + + + **/Abstract* + **/*$* + **/*Tests + + ${it.includedGroups} + ${it.excludedGroups} + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + 3.2.5 + + false + 1 + false + + + @{argLine} + -Xmx400M + -XX:MaxDirectMemorySize=2G + -XX:+HeapDumpOnOutOfMemoryError + -XX:HeapDumpPath=${project.build.directory}/failsafe-reports + + + **/Abstract* + **/*$* + + + ${it.includes} + + ${it.includedGroups} + ${it.excludedGroups} + + + + + +