diff --git a/pom.xml b/pom.xml index 383ed6186d..e0e4646e9b 100644 --- a/pom.xml +++ b/pom.xml @@ -485,6 +485,7 @@ see https://blogs.oracle.com/darcy/entry/bootclasspath_older_source --> -Xlint:unchecked,-options + -parameters @@ -757,7 +758,7 @@ 4.0 - 1.4.0 + 1.4.2 9.2.10.v20150310 diff --git a/scm-annotations/src/main/java/sonia/scm/security/AllowAnonymousAccess.java b/scm-annotations/src/main/java/sonia/scm/security/AllowAnonymousAccess.java new file mode 100644 index 0000000000..b770914d69 --- /dev/null +++ b/scm-annotations/src/main/java/sonia/scm/security/AllowAnonymousAccess.java @@ -0,0 +1,15 @@ +package sonia.scm.security; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Use this annotation to mark REST resource methods that may be accessed without authentication. + * To mark all methods of a complete class you can annotate the class instead. + */ +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface AllowAnonymousAccess { +} diff --git a/scm-core/src/main/java/sonia/scm/AlreadyExistsException.java b/scm-core/src/main/java/sonia/scm/AlreadyExistsException.java index 4c6a52b878..3ca8335346 100644 --- a/scm-core/src/main/java/sonia/scm/AlreadyExistsException.java +++ b/scm-core/src/main/java/sonia/scm/AlreadyExistsException.java @@ -1,11 +1,34 @@ package sonia.scm; -public class AlreadyExistsException extends Exception { +import java.util.List; - public AlreadyExistsException(String message) { - super(message); +import static java.util.Collections.singletonList; +import static java.util.stream.Collectors.joining; + +public class AlreadyExistsException extends ExceptionWithContext { + + private static final String CODE = "FtR7UznKU1"; + + public AlreadyExistsException(ModelObject object) { + this(singletonList(new ContextEntry(object.getClass(), object.getId()))); } - public AlreadyExistsException() { + public static AlreadyExistsException alreadyExists(ContextEntry.ContextBuilder builder) { + return new AlreadyExistsException(builder.build()); + } + + private AlreadyExistsException(List context) { + super(context, createMessage(context)); + } + + @Override + public String getCode() { + return CODE; + } + + private static String createMessage(List context) { + return context.stream() + .map(c -> c.getType().toLowerCase() + " with id " + c.getId()) + .collect(joining(" in ", "", " already exists")); } } diff --git a/scm-core/src/main/java/sonia/scm/ConcurrentModificationException.java b/scm-core/src/main/java/sonia/scm/ConcurrentModificationException.java index f0340a6a59..d566859b4c 100644 --- a/scm-core/src/main/java/sonia/scm/ConcurrentModificationException.java +++ b/scm-core/src/main/java/sonia/scm/ConcurrentModificationException.java @@ -1,4 +1,34 @@ package sonia.scm; -public class ConcurrentModificationException extends Exception { +import java.util.Collections; +import java.util.List; + +import static java.util.stream.Collectors.joining; + +public class ConcurrentModificationException extends ExceptionWithContext { + + private static final String CODE = "2wR7UzpPG1"; + + public ConcurrentModificationException(Class type, String id) { + this(Collections.singletonList(new ContextEntry(type, id))); + } + + public ConcurrentModificationException(String type, String id) { + this(Collections.singletonList(new ContextEntry(type, id))); + } + + private ConcurrentModificationException(List context) { + super(context, createMessage(context)); + } + + @Override + public String getCode() { + return CODE; + } + + private static String createMessage(List context) { + return context.stream() + .map(c -> c.getType().toLowerCase() + " with id " + c.getId()) + .collect(joining(" in ", "", " has been modified concurrently")); + } } diff --git a/scm-core/src/main/java/sonia/scm/ContextEntry.java b/scm-core/src/main/java/sonia/scm/ContextEntry.java new file mode 100644 index 0000000000..bbd2fadc5d --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/ContextEntry.java @@ -0,0 +1,84 @@ +package sonia.scm; + +import sonia.scm.repository.NamespaceAndName; +import sonia.scm.repository.Repository; +import sonia.scm.util.AssertUtil; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +public class ContextEntry { + private final String type; + private final String id; + + ContextEntry(Class type, String id) { + this(type.getSimpleName(), id); + } + + ContextEntry(String type, String id) { + AssertUtil.assertIsNotEmpty(type); + AssertUtil.assertIsNotEmpty(id); + this.type = type; + this.id = id; + } + + public String getType() { + return type; + } + + public String getId() { + return id; + } + + + public static class ContextBuilder { + private final List context = new LinkedList<>(); + + public static List noContext() { + return new ContextBuilder().build(); + } + + public static List only(String type, String id) { + return new ContextBuilder().in(type, id).build(); + } + + public static ContextBuilder entity(Repository repository) { + return new ContextBuilder().in(repository.getNamespaceAndName()); + } + + public static ContextBuilder entity(NamespaceAndName namespaceAndName) { + return new ContextBuilder().in(Repository.class, namespaceAndName.logString()); + } + + public static ContextBuilder entity(Class type, String id) { + return new ContextBuilder().in(type, id); + } + + public static ContextBuilder entity(String type, String id) { + return new ContextBuilder().in(type, id); + } + + public ContextBuilder in(Repository repository) { + return in(repository.getNamespaceAndName()); + } + + public ContextBuilder in(NamespaceAndName namespaceAndName) { + return this.in(Repository.class, namespaceAndName.logString()); + } + + public ContextBuilder in(Class type, String id) { + context.add(new ContextEntry(type, id)); + return this; + } + + public ContextBuilder in(String type, String id) { + context.add(new ContextEntry(type, id)); + return this; + } + + public List build() { + return Collections.unmodifiableList(context); + } + } +} diff --git a/scm-core/src/main/java/sonia/scm/ExceptionWithContext.java b/scm-core/src/main/java/sonia/scm/ExceptionWithContext.java new file mode 100644 index 0000000000..dd87b77210 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/ExceptionWithContext.java @@ -0,0 +1,26 @@ +package sonia.scm; + +import java.util.List; + +import static java.util.Collections.unmodifiableList; + +public abstract class ExceptionWithContext extends RuntimeException { + + private final List context; + + public ExceptionWithContext(List context, String message) { + super(message); + this.context = context; + } + + public ExceptionWithContext(List context, String message, Exception cause) { + super(message, cause); + this.context = context; + } + + public List getContext() { + return unmodifiableList(context); + } + + public abstract String getCode(); +} diff --git a/scm-core/src/main/java/sonia/scm/HandlerBase.java b/scm-core/src/main/java/sonia/scm/HandlerBase.java index bbe78c8cf7..a621f4f697 100644 --- a/scm-core/src/main/java/sonia/scm/HandlerBase.java +++ b/scm-core/src/main/java/sonia/scm/HandlerBase.java @@ -54,25 +54,21 @@ public interface HandlerBase * * @return The persisted object. */ - T create(T object) throws AlreadyExistsException; + T create(T object); /** * Removes a persistent object. * * * @param object to delete - * - * @throws IOException */ - void delete(T object) throws NotFoundException; + void delete(T object); /** * Modifies a persistent object. * * * @param object to modify - * - * @throws IOException */ - void modify(T object) throws NotFoundException; + void modify(T object); } diff --git a/scm-core/src/main/java/sonia/scm/Manager.java b/scm-core/src/main/java/sonia/scm/Manager.java index 20b3a16a8e..390777958d 100644 --- a/scm-core/src/main/java/sonia/scm/Manager.java +++ b/scm-core/src/main/java/sonia/scm/Manager.java @@ -58,7 +58,7 @@ public interface Manager * * @throws NotFoundException */ - void refresh(T object) throws NotFoundException; + void refresh(T object); //~--- get methods ---------------------------------------------------------- diff --git a/scm-core/src/main/java/sonia/scm/ManagerDecorator.java b/scm-core/src/main/java/sonia/scm/ManagerDecorator.java index ef20a374cb..f6e91aeced 100644 --- a/scm-core/src/main/java/sonia/scm/ManagerDecorator.java +++ b/scm-core/src/main/java/sonia/scm/ManagerDecorator.java @@ -66,7 +66,7 @@ public class ManagerDecorator implements Manager { } @Override - public T create(T object) throws AlreadyExistsException { + public T create(T object) { return decorated.create(object); } diff --git a/scm-core/src/main/java/sonia/scm/NotFoundException.java b/scm-core/src/main/java/sonia/scm/NotFoundException.java index 37546be0b8..69b9617e93 100644 --- a/scm-core/src/main/java/sonia/scm/NotFoundException.java +++ b/scm-core/src/main/java/sonia/scm/NotFoundException.java @@ -1,10 +1,38 @@ package sonia.scm; -public class NotFoundException extends RuntimeException { - public NotFoundException(String type, String id) { - super(type + " with id '" + id + "' not found"); +import java.util.Collections; +import java.util.List; + +import static java.util.stream.Collectors.joining; + +public class NotFoundException extends ExceptionWithContext { + + private static final String CODE = "AGR7UzkhA1"; + + public NotFoundException(Class type, String id) { + this(Collections.singletonList(new ContextEntry(type, id))); } - public NotFoundException() { + public NotFoundException(String type, String id) { + this(Collections.singletonList(new ContextEntry(type, id))); + } + + public static NotFoundException notFound(ContextEntry.ContextBuilder contextBuilder) { + return new NotFoundException(contextBuilder.build()); + } + + private NotFoundException(List context) { + super(context, createMessage(context)); + } + + @Override + public String getCode() { + return CODE; + } + + private static String createMessage(List context) { + return context.stream() + .map(c -> c.getType().toLowerCase() + " with id " + c.getId()) + .collect(joining(" in ", "could not find ", "")); } } diff --git a/scm-core/src/main/java/sonia/scm/NotSupportedFeatuerException.java b/scm-core/src/main/java/sonia/scm/NotSupportedFeatureException.java similarity index 79% rename from scm-core/src/main/java/sonia/scm/NotSupportedFeatuerException.java rename to scm-core/src/main/java/sonia/scm/NotSupportedFeatureException.java index e6fea6cfa7..daf996ee6c 100644 --- a/scm-core/src/main/java/sonia/scm/NotSupportedFeatuerException.java +++ b/scm-core/src/main/java/sonia/scm/NotSupportedFeatureException.java @@ -33,33 +33,30 @@ package sonia.scm; +import java.util.Collections; + /** * * @author Sebastian Sdorra * @version 1.6 */ -public class NotSupportedFeatuerException extends Exception -{ +public class NotSupportedFeatureException extends ExceptionWithContext { - /** Field description */ private static final long serialVersionUID = 256498734456613496L; - //~--- constructors --------------------------------------------------------- + private static final String CODE = "9SR8G0kmU1"; - /** - * Constructs ... - * - */ - public NotSupportedFeatuerException() {} - - /** - * Constructs ... - * - * - * @param message - */ - public NotSupportedFeatuerException(String message) + public NotSupportedFeatureException(String feature) { - super(message); + super(Collections.emptyList(),createMessage(feature)); + } + + @Override + public String getCode() { + return CODE; + } + + private static String createMessage(String feature) { + return "feature " + feature + " is not supported by this repository"; } } diff --git a/scm-core/src/main/java/sonia/scm/filter/Filters.java b/scm-core/src/main/java/sonia/scm/filter/Filters.java index b1f5ea47cf..d3ac708296 100644 --- a/scm-core/src/main/java/sonia/scm/filter/Filters.java +++ b/scm-core/src/main/java/sonia/scm/filter/Filters.java @@ -45,28 +45,12 @@ public final class Filters /** Field description */ public static final String PATTERN_ALL = "/*"; - /** Field description */ - public static final String PATTERN_CONFIG = REST_API_PATH + "/config*"; - /** Field description */ public static final String PATTERN_DEBUG = "/debug.html"; - /** Field description */ - public static final String PATTERN_GROUPS = REST_API_PATH + "/groups*"; - - /** Field description */ - public static final String PATTERN_PLUGINS = REST_API_PATH + "/plugins*"; - - /** Field description */ - public static final String PATTERN_RESOURCE_REGEX = - "^/(?:resources|api|plugins|index)[\\./].*(?:html|\\.css|\\.js|\\.xml|\\.json|\\.txt)"; - /** Field description */ public static final String PATTERN_RESTAPI = REST_API_PATH + "/*"; - /** Field description */ - public static final String PATTERN_USERS = REST_API_PATH + "/users*"; - /** authentication priority */ public static final int PRIORITY_AUTHENTICATION = 5000; diff --git a/scm-core/src/main/java/sonia/scm/repository/AbstactImportHandler.java b/scm-core/src/main/java/sonia/scm/repository/AbstactImportHandler.java index 2195e87730..f4438807c6 100644 --- a/scm-core/src/main/java/sonia/scm/repository/AbstactImportHandler.java +++ b/scm-core/src/main/java/sonia/scm/repository/AbstactImportHandler.java @@ -37,7 +37,6 @@ package sonia.scm.repository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sonia.scm.AlreadyExistsException; import sonia.scm.repository.ImportResult.Builder; import java.io.File; @@ -243,7 +242,7 @@ public abstract class AbstactImportHandler implements AdvancedImportHandler */ private void importRepository(RepositoryManager manager, String repositoryName) - throws IOException, AlreadyExistsException { + throws IOException { Repository repository = createRepository(getRepositoryDirectory(repositoryName), repositoryName); diff --git a/scm-core/src/main/java/sonia/scm/repository/AbstractRepositoryHandler.java b/scm-core/src/main/java/sonia/scm/repository/AbstractRepositoryHandler.java index 33b6cb8030..42c8f22a0f 100644 --- a/scm-core/src/main/java/sonia/scm/repository/AbstractRepositoryHandler.java +++ b/scm-core/src/main/java/sonia/scm/repository/AbstractRepositoryHandler.java @@ -38,7 +38,7 @@ package sonia.scm.repository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sonia.scm.NotSupportedFeatuerException; +import sonia.scm.NotSupportedFeatureException; import sonia.scm.SCMContextProvider; import sonia.scm.event.ScmEventBus; @@ -165,13 +165,12 @@ public abstract class AbstractRepositoryHandler * * @return * - * @throws NotSupportedFeatuerException + * @throws NotSupportedFeatureException */ @Override - public ImportHandler getImportHandler() throws NotSupportedFeatuerException + public ImportHandler getImportHandler() throws NotSupportedFeatureException { - throw new NotSupportedFeatuerException( - "import handler is not supported by this repository handler"); + throw new NotSupportedFeatureException("import"); } /** diff --git a/scm-core/src/main/java/sonia/scm/repository/AbstractSimpleRepositoryHandler.java b/scm-core/src/main/java/sonia/scm/repository/AbstractSimpleRepositoryHandler.java index a717cdf05b..d190935fae 100644 --- a/scm-core/src/main/java/sonia/scm/repository/AbstractSimpleRepositoryHandler.java +++ b/scm-core/src/main/java/sonia/scm/repository/AbstractSimpleRepositoryHandler.java @@ -40,6 +40,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.AlreadyExistsException; import sonia.scm.ConfigurationException; +import sonia.scm.ContextEntry; import sonia.scm.io.CommandResult; import sonia.scm.io.ExtendedCommand; import sonia.scm.io.FileSystem; @@ -81,14 +82,14 @@ public abstract class AbstractSimpleRepositoryHandler context, String message, Exception cause) { + super(context, message, cause); + } + + @Override + public String getCode() { + return null; } } diff --git a/scm-core/src/main/java/sonia/scm/repository/NamespaceAndName.java b/scm-core/src/main/java/sonia/scm/repository/NamespaceAndName.java index 7b71078f67..fd0a72ad7c 100644 --- a/scm-core/src/main/java/sonia/scm/repository/NamespaceAndName.java +++ b/scm-core/src/main/java/sonia/scm/repository/NamespaceAndName.java @@ -25,9 +25,13 @@ public class NamespaceAndName implements Comparable { return name; } + public String logString() { + return getNamespace() + "/" + getName(); + } + @Override public String toString() { - return getNamespace() + "/" + getName(); + return logString(); } @Override diff --git a/scm-core/src/main/java/sonia/scm/repository/PathNotFoundException.java b/scm-core/src/main/java/sonia/scm/repository/PathNotFoundException.java deleted file mode 100644 index ed62a5967c..0000000000 --- a/scm-core/src/main/java/sonia/scm/repository/PathNotFoundException.java +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Copyright (c) 2010, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ - - - -package sonia.scm.repository; - -//~--- non-JDK imports -------------------------------------------------------- - -import sonia.scm.NotFoundException; -import sonia.scm.util.Util; - -/** - * Signals that the specified path could be found. - * - * @author Sebastian Sdorra - */ -public class PathNotFoundException extends NotFoundException -{ - - /** Field description */ - private static final long serialVersionUID = 4629690181172951809L; - - //~--- constructors --------------------------------------------------------- - - /** - * Constructs a new {@link PathNotFoundException} - * with the specified path. - * - * - * @param path path which could not be found - */ - public PathNotFoundException(String path) - { - super("path", Util.nonNull(path)); - this.path = Util.nonNull(path); - } - - //~--- get methods ---------------------------------------------------------- - - /** - * Return the path which could not be found. - * - * - * @return path which could not be found - */ - public String getPath() - { - return path; - } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private String path; -} diff --git a/scm-core/src/main/java/sonia/scm/repository/Repository.java b/scm-core/src/main/java/sonia/scm/repository/Repository.java index 19c5b31349..fd8f07df8d 100644 --- a/scm-core/src/main/java/sonia/scm/repository/Repository.java +++ b/scm-core/src/main/java/sonia/scm/repository/Repository.java @@ -37,7 +37,6 @@ import com.github.sdorra.ssp.PermissionObject; import com.github.sdorra.ssp.StaticPermissions; import com.google.common.base.MoreObjects; import com.google.common.base.Objects; -import com.google.common.collect.Lists; import sonia.scm.BasicPropertiesAware; import sonia.scm.ModelObject; import sonia.scm.util.Util; @@ -50,8 +49,11 @@ import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlTransient; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; /** * Source code repository. @@ -79,7 +81,7 @@ public class Repository extends BasicPropertiesAware implements ModelObject, Per private Long lastModified; private String namespace; private String name; - private List permissions; + private final Set permissions = new HashSet<>(); @XmlElement(name = "public") private boolean publicReadable = false; private boolean archived = false; @@ -127,7 +129,6 @@ public class Repository extends BasicPropertiesAware implements ModelObject, Per this.name = name; this.contact = contact; this.description = description; - this.permissions = Lists.newArrayList(); if (Util.isNotEmpty(permissions)) { this.permissions.addAll(Arrays.asList(permissions)); @@ -200,12 +201,8 @@ public class Repository extends BasicPropertiesAware implements ModelObject, Per return new NamespaceAndName(getNamespace(), getName()); } - public List getPermissions() { - if (permissions == null) { - permissions = Lists.newArrayList(); - } - - return permissions; + public Collection getPermissions() { + return Collections.unmodifiableCollection(permissions); } /** @@ -300,8 +297,17 @@ public class Repository extends BasicPropertiesAware implements ModelObject, Per this.name = name; } - public void setPermissions(List permissions) { - this.permissions = permissions; + public void setPermissions(Collection permissions) { + this.permissions.clear(); + this.permissions.addAll(permissions); + } + + public void addPermission(Permission newPermission) { + this.permissions.add(newPermission); + } + + public void removePermission(Permission permission) { + this.permissions.remove(permission); } public void setPublicReadable(boolean publicReadable) { diff --git a/scm-core/src/main/java/sonia/scm/repository/RepositoryHandler.java b/scm-core/src/main/java/sonia/scm/repository/RepositoryHandler.java index 445fc22ab8..79c06d03f9 100644 --- a/scm-core/src/main/java/sonia/scm/repository/RepositoryHandler.java +++ b/scm-core/src/main/java/sonia/scm/repository/RepositoryHandler.java @@ -36,7 +36,7 @@ package sonia.scm.repository; //~--- non-JDK imports -------------------------------------------------------- import sonia.scm.Handler; -import sonia.scm.NotSupportedFeatuerException; +import sonia.scm.NotSupportedFeatureException; import sonia.scm.plugin.ExtensionPoint; /** @@ -70,9 +70,9 @@ public interface RepositoryHandler * @return {@link ImportHandler} for the repository type of this handler * @since 1.12 * - * @throws NotSupportedFeatuerException + * @throws NotSupportedFeatureException */ - public ImportHandler getImportHandler() throws NotSupportedFeatuerException; + public ImportHandler getImportHandler() throws NotSupportedFeatureException; /** * Returns informations about the version of the RepositoryHandler. diff --git a/scm-core/src/main/java/sonia/scm/repository/RepositoryManager.java b/scm-core/src/main/java/sonia/scm/repository/RepositoryManager.java index 1e2fdccf42..7f4880c258 100644 --- a/scm-core/src/main/java/sonia/scm/repository/RepositoryManager.java +++ b/scm-core/src/main/java/sonia/scm/repository/RepositoryManager.java @@ -35,7 +35,6 @@ package sonia.scm.repository; //~--- non-JDK imports -------------------------------------------------------- -import sonia.scm.AlreadyExistsException; import sonia.scm.TypeManager; import java.io.IOException; @@ -73,7 +72,7 @@ public interface RepositoryManager * * @throws IOException */ - public void importRepository(Repository repository) throws IOException, AlreadyExistsException; + public void importRepository(Repository repository) throws IOException; //~--- get methods ---------------------------------------------------------- diff --git a/scm-core/src/main/java/sonia/scm/repository/RepositoryManagerDecorator.java b/scm-core/src/main/java/sonia/scm/repository/RepositoryManagerDecorator.java index 87df960da7..b7a819e5f9 100644 --- a/scm-core/src/main/java/sonia/scm/repository/RepositoryManagerDecorator.java +++ b/scm-core/src/main/java/sonia/scm/repository/RepositoryManagerDecorator.java @@ -35,7 +35,6 @@ package sonia.scm.repository; //~--- non-JDK imports -------------------------------------------------------- -import sonia.scm.AlreadyExistsException; import sonia.scm.ManagerDecorator; import sonia.scm.Type; @@ -82,7 +81,7 @@ public class RepositoryManagerDecorator * {@inheritDoc} */ @Override - public void importRepository(Repository repository) throws IOException, AlreadyExistsException { + public void importRepository(Repository repository) throws IOException { decorated.importRepository(repository); } diff --git a/scm-core/src/main/java/sonia/scm/repository/RepositoryNotFoundException.java b/scm-core/src/main/java/sonia/scm/repository/RepositoryNotFoundException.java deleted file mode 100644 index 9dd866daa4..0000000000 --- a/scm-core/src/main/java/sonia/scm/repository/RepositoryNotFoundException.java +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright (c) 2010, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ - - - -package sonia.scm.repository; - -import sonia.scm.NotFoundException; - -/** - * Signals that the specified {@link Repository} could be found. - * - * @author Sebastian Sdorra - * @since 1.6 - */ -public class RepositoryNotFoundException extends NotFoundException -{ - - private static final long serialVersionUID = -6583078808900520166L; - private static final String TYPE_REPOSITORY = "repository"; - - //~--- constructors --------------------------------------------------------- - - /** - * Constructs a new {@link RepositoryNotFoundException} with null as its - * error detail message. - * - */ - public RepositoryNotFoundException(Repository repository) { - super(TYPE_REPOSITORY, repository.getName() + "/" + repository.getNamespace()); - } - - public RepositoryNotFoundException(String repositoryId) { - super(TYPE_REPOSITORY, repositoryId); - } - - public RepositoryNotFoundException(NamespaceAndName namespaceAndName) { - super(TYPE_REPOSITORY, namespaceAndName.toString()); - } -} diff --git a/scm-core/src/main/java/sonia/scm/repository/RevisionNotFoundException.java b/scm-core/src/main/java/sonia/scm/repository/RevisionNotFoundException.java deleted file mode 100644 index 4185e3223d..0000000000 --- a/scm-core/src/main/java/sonia/scm/repository/RevisionNotFoundException.java +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Copyright (c) 2010, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ - - - -package sonia.scm.repository; - -//~--- non-JDK imports -------------------------------------------------------- - -import sonia.scm.NotFoundException; -import sonia.scm.util.Util; - -/** - * Signals that the specified revision could be found. - * - * @author Sebastian Sdorra - */ -public class RevisionNotFoundException extends NotFoundException { - - /** Field description */ - private static final long serialVersionUID = -5594008535358811998L; - - //~--- constructors --------------------------------------------------------- - - /** - * Constructs a new {@link RevisionNotFoundException} - * with the specified revision. - * - * - * @param revision revision which could not be found - */ - public RevisionNotFoundException(String revision) - { - super("revision", revision); - this.revision = Util.nonNull(revision); - } - - //~--- get methods ---------------------------------------------------------- - - /** - * Return the revision which could not be found. - * - * - * @return revision which could not be found - */ - public String getRevision() - { - return revision; - } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private String revision; -} diff --git a/scm-core/src/main/java/sonia/scm/repository/api/BrowseCommandBuilder.java b/scm-core/src/main/java/sonia/scm/repository/api/BrowseCommandBuilder.java index d2db6856a7..d482c04ea4 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/BrowseCommandBuilder.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/BrowseCommandBuilder.java @@ -38,7 +38,6 @@ package sonia.scm.repository.api; import com.google.common.base.Objects; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sonia.scm.NotFoundException; import sonia.scm.cache.Cache; import sonia.scm.cache.CacheManager; import sonia.scm.repository.BrowserResult; @@ -46,7 +45,6 @@ import sonia.scm.repository.FileObject; import sonia.scm.repository.PreProcessorUtil; import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryCacheKey; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.repository.spi.BrowseCommand; import sonia.scm.repository.spi.BrowseCommandRequest; @@ -136,7 +134,7 @@ public final class BrowseCommandBuilder * * @throws IOException */ - public BrowserResult getBrowserResult() throws IOException, NotFoundException { + public BrowserResult getBrowserResult() throws IOException { BrowserResult result = null; if (disableCache) diff --git a/scm-core/src/main/java/sonia/scm/repository/api/CatCommandBuilder.java b/scm-core/src/main/java/sonia/scm/repository/api/CatCommandBuilder.java index bf896efed8..d1e0cbc5f1 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/CatCommandBuilder.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/CatCommandBuilder.java @@ -37,9 +37,7 @@ import com.google.common.base.Preconditions; import com.google.common.base.Strings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sonia.scm.repository.PathNotFoundException; import sonia.scm.repository.Repository; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.repository.spi.CatCommand; import sonia.scm.repository.spi.CatCommandRequest; import sonia.scm.util.IOUtil; @@ -107,7 +105,7 @@ public final class CatCommandBuilder * @param outputStream output stream for the content * @param path file path */ - public void retriveContent(OutputStream outputStream, String path) throws IOException, PathNotFoundException, RevisionNotFoundException { + public void retriveContent(OutputStream outputStream, String path) throws IOException { getCatResult(outputStream, path); } @@ -116,7 +114,7 @@ public final class CatCommandBuilder * * @param path file path */ - public InputStream getStream(String path) throws IOException, PathNotFoundException, RevisionNotFoundException { + public InputStream getStream(String path) throws IOException { Preconditions.checkArgument(!Strings.isNullOrEmpty(path), "path is required"); @@ -139,7 +137,7 @@ public final class CatCommandBuilder * * @throws IOException */ - public String getContent(String path) throws IOException, PathNotFoundException, RevisionNotFoundException { + public String getContent(String path) throws IOException { String content = null; ByteArrayOutputStream baos = null; @@ -186,7 +184,7 @@ public final class CatCommandBuilder * @throws IOException */ private void getCatResult(OutputStream outputStream, String path) - throws IOException, PathNotFoundException, RevisionNotFoundException { + throws IOException { Preconditions.checkNotNull(outputStream, "OutputStream is required"); Preconditions.checkArgument(!Strings.isNullOrEmpty(path), "path is required"); diff --git a/scm-core/src/main/java/sonia/scm/repository/api/Command.java b/scm-core/src/main/java/sonia/scm/repository/api/Command.java index 2f844cfbfb..fa09cea2cb 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/Command.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/Command.java @@ -66,6 +66,10 @@ public enum Command /** * @since 2.0 */ - MODIFICATIONS + MODIFICATIONS, + /** + * @since 2.0 + */ + MERGE } diff --git a/scm-core/src/main/java/sonia/scm/repository/api/DiffCommandBuilder.java b/scm-core/src/main/java/sonia/scm/repository/api/DiffCommandBuilder.java index d92cd57f1f..7217d0e97a 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/DiffCommandBuilder.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/DiffCommandBuilder.java @@ -38,7 +38,6 @@ package sonia.scm.repository.api; import com.google.common.base.Preconditions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.repository.spi.DiffCommand; import sonia.scm.repository.spi.DiffCommandRequest; import sonia.scm.util.IOUtil; @@ -104,7 +103,7 @@ public final class DiffCommandBuilder * * @throws IOException */ - public DiffCommandBuilder retrieveContent(OutputStream outputStream) throws IOException, RevisionNotFoundException { + public DiffCommandBuilder retrieveContent(OutputStream outputStream) throws IOException { getDiffResult(outputStream); return this; @@ -119,7 +118,7 @@ public final class DiffCommandBuilder * * @throws IOException */ - public String getContent() throws IOException, RevisionNotFoundException { + public String getContent() throws IOException { String content = null; ByteArrayOutputStream baos = null; @@ -199,7 +198,7 @@ public final class DiffCommandBuilder * * @throws IOException */ - private void getDiffResult(OutputStream outputStream) throws IOException, RevisionNotFoundException { + private void getDiffResult(OutputStream outputStream) throws IOException { Preconditions.checkNotNull(outputStream, "OutputStream is required"); Preconditions.checkArgument(request.isValid(), "path and/or revision is required"); diff --git a/scm-core/src/main/java/sonia/scm/repository/api/LogCommandBuilder.java b/scm-core/src/main/java/sonia/scm/repository/api/LogCommandBuilder.java index 3db8d6aae0..73062a0244 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/LogCommandBuilder.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/LogCommandBuilder.java @@ -46,7 +46,6 @@ import sonia.scm.repository.ChangesetPagingResult; import sonia.scm.repository.PreProcessorUtil; import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryCacheKey; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.repository.spi.LogCommand; import sonia.scm.repository.spi.LogCommandRequest; @@ -165,7 +164,7 @@ public final class LogCommandBuilder * * @throws IOException */ - public Changeset getChangeset(String id) throws IOException, RevisionNotFoundException { + public Changeset getChangeset(String id) throws IOException { Changeset changeset; if (disableCache) @@ -224,7 +223,7 @@ public final class LogCommandBuilder * * @throws IOException */ - public ChangesetPagingResult getChangesets() throws IOException, RevisionNotFoundException { + public ChangesetPagingResult getChangesets() throws IOException { ChangesetPagingResult cpr; if (disableCache) diff --git a/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandBuilder.java b/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandBuilder.java new file mode 100644 index 0000000000..881a374864 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandBuilder.java @@ -0,0 +1,143 @@ +package sonia.scm.repository.api; + +import com.google.common.base.Preconditions; +import sonia.scm.repository.Person; +import sonia.scm.repository.spi.MergeCommand; +import sonia.scm.repository.spi.MergeCommandRequest; + +/** + * Use this {@link MergeCommandBuilder} to merge two branches of a repository ({@link #executeMerge()}) or to check if + * the branches could be merged without conflicts ({@link #dryRun()}). To do so, you have to specify the name of + * the target branch ({@link #setTargetBranch(String)}) and the name of the branch that should be merged + * ({@link #setBranchToMerge(String)}). Additionally you can specify an author that should be used for the commit + * ({@link #setAuthor(Person)}) and a message template ({@link #setMessageTemplate(String)}) if you are not doing a dry + * run only. If no author is specified, the logged in user and a default message will be used instead. + * + * To actually merge feature_branch into integration_branch do this: + *

+ *     repositoryService.gerMergeCommand()
+ *       .setBranchToMerge("feature_branch")
+ *       .setTargetBranch("integration_branch")
+ *       .executeMerge();
+ * 
+ * + * If the merge is successful, the result will look like this: + *

+ *                            O    <- Merge result (new head of integration_branch)
+ *                            |\
+ *                            | \
+ *  old integration_branch -> O  O <- feature_branch
+ *                            |  |
+ *                            O  O
+ * 
+ * + * To check whether they can be merged without conflicts beforehand do this: + *

+ *     repositoryService.gerMergeCommand()
+ *       .setBranchToMerge("feature_branch")
+ *       .setTargetBranch("integration_branch")
+ *       .dryRun()
+ *       .isMergeable();
+ * 
+ * + * Keep in mind that you should always check the result of a merge even though you may have done a dry run + * beforehand, because the branches can change between the dry run and the actual merge. + * + * @since 2.0.0 + */ +public class MergeCommandBuilder { + + private final MergeCommand mergeCommand; + private final MergeCommandRequest request = new MergeCommandRequest(); + + MergeCommandBuilder(MergeCommand mergeCommand) { + this.mergeCommand = mergeCommand; + } + + /** + * Use this to set the branch that should be merged into the target branch. + * + * This is mandatory. + * + * @return This builder instance. + */ + public MergeCommandBuilder setBranchToMerge(String branchToMerge) { + request.setBranchToMerge(branchToMerge); + return this; + } + + /** + * Use this to set the target branch the other branch should be merged into. + * + * This is mandatory. + * + * @return This builder instance. + */ + public MergeCommandBuilder setTargetBranch(String targetBranch) { + request.setTargetBranch(targetBranch); + return this; + } + + /** + * Use this to set the author of the merge commit manually. If this is omitted, the currently logged in user will be + * used instead. + * + * This is optional and for {@link #executeMerge()} only. + * + * @return This builder instance. + */ + public MergeCommandBuilder setAuthor(Person author) { + request.setAuthor(author); + return this; + } + + /** + * Use this to set a template for the commit message. If no message is set, a default message will be used. + * + * You can use the placeholder {0} for the branch to be merged and {1} for the target + * branch, eg.: + * + *

+   * ...setMessageTemplate("Merge of {0} into {1}")...
+   * 
+ * + * This is optional and for {@link #executeMerge()} only. + * + * @return This builder instance. + */ + public MergeCommandBuilder setMessageTemplate(String messageTemplate) { + request.setMessageTemplate(messageTemplate); + return this; + } + + /** + * Use this to reset the command. + * @return This builder instance. + */ + public MergeCommandBuilder reset() { + request.reset(); + return this; + } + + /** + * Use this to actually do the merge. If an automatic merge is not possible, {@link MergeCommandResult#isSuccess()} + * will return false. + * + * @return The result of the merge. + */ + public MergeCommandResult executeMerge() { + Preconditions.checkArgument(request.isValid(), "revision to merge and target revision is required"); + return mergeCommand.merge(request); + } + + /** + * Use this to check whether the given branches can be merged autmatically. If this is possible, + * {@link MergeDryRunCommandResult#isMergeable()} will return true. + * + * @return The result whether the given branches can be merged automatically. + */ + public MergeDryRunCommandResult dryRun() { + Preconditions.checkArgument(request.isValid(), "revision to merge and target revision is required"); + return mergeCommand.dryRun(request); + } +} diff --git a/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandResult.java b/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandResult.java new file mode 100644 index 0000000000..53f712cddc --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/repository/api/MergeCommandResult.java @@ -0,0 +1,44 @@ +package sonia.scm.repository.api; + +import java.util.Collection; +import java.util.HashSet; + +import static java.util.Collections.emptyList; +import static java.util.Collections.unmodifiableCollection; + +/** + * This class keeps the result of a merge of branches. Use {@link #isSuccess()} to check whether the merge was + * sucessfully executed. If the result is false the merge could not be done without conflicts. In this + * case you can use {@link #getFilesWithConflict()} to get a list of files with merge conflicts. + */ +public class MergeCommandResult { + private final Collection filesWithConflict; + + private MergeCommandResult(Collection filesWithConflict) { + this.filesWithConflict = filesWithConflict; + } + + public static MergeCommandResult success() { + return new MergeCommandResult(emptyList()); + } + + public static MergeCommandResult failure(Collection filesWithConflict) { + return new MergeCommandResult(new HashSet<>(filesWithConflict)); + } + + /** + * If this returns true, the merge was successfull. If this returns false there were + * merge conflicts. In this case you can use {@link #getFilesWithConflict()} to check what files could not be merged. + */ + public boolean isSuccess() { + return filesWithConflict.isEmpty(); + } + + /** + * If the merge was not successful ({@link #isSuccess()} returns false) this will give you a list of + * file paths that could not be merged automatically. + */ + public Collection getFilesWithConflict() { + return unmodifiableCollection(filesWithConflict); + } +} diff --git a/scm-core/src/main/java/sonia/scm/repository/api/MergeDryRunCommandResult.java b/scm-core/src/main/java/sonia/scm/repository/api/MergeDryRunCommandResult.java new file mode 100644 index 0000000000..6ebb330aae --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/repository/api/MergeDryRunCommandResult.java @@ -0,0 +1,22 @@ +package sonia.scm.repository.api; + +/** + * This class keeps the result of a merge dry run. Use {@link #isMergeable()} to check whether an automatic merge is + * possible or not. + */ +public class MergeDryRunCommandResult { + + private final boolean mergeable; + + public MergeDryRunCommandResult(boolean mergeable) { + this.mergeable = mergeable; + } + + /** + * This will return true, when an automatic merge is possible at the moment; false + * otherwise. + */ + public boolean isMergeable() { + return mergeable; + } +} diff --git a/scm-core/src/main/java/sonia/scm/repository/api/ModificationsCommandBuilder.java b/scm-core/src/main/java/sonia/scm/repository/api/ModificationsCommandBuilder.java index 6459b47cd5..498746cc60 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/ModificationsCommandBuilder.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/ModificationsCommandBuilder.java @@ -13,7 +13,6 @@ import sonia.scm.repository.Modifications; import sonia.scm.repository.PreProcessorUtil; import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryCacheKey; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.repository.spi.ModificationsCommand; import sonia.scm.repository.spi.ModificationsCommandRequest; @@ -67,7 +66,7 @@ public final class ModificationsCommandBuilder { return this; } - public Modifications getModifications() throws IOException, RevisionNotFoundException { + public Modifications getModifications() throws IOException { Modifications modifications; if (disableCache) { log.info("Get modifications for {} with disabled cache", request); diff --git a/scm-core/src/main/java/sonia/scm/repository/api/RepositoryService.java b/scm-core/src/main/java/sonia/scm/repository/api/RepositoryService.java index bdd6e4b320..fe0529e6b5 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/RepositoryService.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/RepositoryService.java @@ -79,6 +79,7 @@ import java.util.stream.Stream; * @apiviz.uses sonia.scm.repository.api.PushCommandBuilder * @apiviz.uses sonia.scm.repository.api.BundleCommandBuilder * @apiviz.uses sonia.scm.repository.api.UnbundleCommandBuilder + * @apiviz.uses sonia.scm.repository.api.MergeCommandBuilder * @since 1.17 */ @Slf4j @@ -353,6 +354,22 @@ public final class RepositoryService implements Closeable { repository); } + /** + * The merge command executes a merge of two branches. It is possible to do a dry run to check, whether the given + * branches can be merged without conflicts. + * + * @return instance of {@link MergeCommandBuilder} + * @throws CommandNotSupportedException if the command is not supported + * by the implementation of the repository service provider. + * @since 2.0.0 + */ + public MergeCommandBuilder gerMergeCommand() { + logger.debug("create unbundle command for repository {}", + repository.getNamespaceAndName()); + + return new MergeCommandBuilder(provider.getMergeCommand()); + } + /** * Returns true if the command is supported by the repository service. * diff --git a/scm-core/src/main/java/sonia/scm/repository/api/RepositoryServiceFactory.java b/scm-core/src/main/java/sonia/scm/repository/api/RepositoryServiceFactory.java index fbb1ee6b58..8db3ab7546 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/RepositoryServiceFactory.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/RepositoryServiceFactory.java @@ -45,6 +45,7 @@ import com.google.inject.Singleton; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.HandlerEventType; +import sonia.scm.NotFoundException; import sonia.scm.cache.Cache; import sonia.scm.cache.CacheManager; import sonia.scm.config.ScmConfiguration; @@ -57,7 +58,6 @@ import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryCacheKeyPredicate; import sonia.scm.repository.RepositoryEvent; import sonia.scm.repository.RepositoryManager; -import sonia.scm.repository.RepositoryNotFoundException; import sonia.scm.repository.RepositoryPermissions; import sonia.scm.repository.spi.RepositoryServiceProvider; import sonia.scm.repository.spi.RepositoryServiceResolver; @@ -65,6 +65,9 @@ import sonia.scm.security.ScmSecurityException; import java.util.Set; +import static sonia.scm.ContextEntry.ContextBuilder.entity; +import static sonia.scm.NotFoundException.notFound; + //~--- JDK imports ------------------------------------------------------------ /** @@ -161,7 +164,7 @@ public final class RepositoryServiceFactory * @return a implementation of RepositoryService * for the given type of repository * - * @throws RepositoryNotFoundException if no repository + * @throws NotFoundException if no repository * with the given id is available * @throws RepositoryServiceNotFoundException if no repository service * implementation for this kind of repository is available @@ -169,7 +172,7 @@ public final class RepositoryServiceFactory * @throws ScmSecurityException if current user has not read permissions * for that repository */ - public RepositoryService create(String repositoryId) throws RepositoryNotFoundException { + public RepositoryService create(String repositoryId) { Preconditions.checkArgument(!Strings.isNullOrEmpty(repositoryId), "a non empty repositoryId is required"); @@ -177,7 +180,7 @@ public final class RepositoryServiceFactory if (repository == null) { - throw new RepositoryNotFoundException(repositoryId); + throw new NotFoundException(Repository.class, repositoryId); } return create(repository); @@ -192,7 +195,7 @@ public final class RepositoryServiceFactory * @return a implementation of RepositoryService * for the given type of repository * - * @throws RepositoryNotFoundException if no repository + * @throws NotFoundException if no repository * with the given id is available * @throws RepositoryServiceNotFoundException if no repository service * implementation for this kind of repository is available @@ -201,7 +204,6 @@ public final class RepositoryServiceFactory * for that repository */ public RepositoryService create(NamespaceAndName namespaceAndName) - throws RepositoryNotFoundException { Preconditions.checkArgument(namespaceAndName != null, "a non empty namespace and name is required"); @@ -210,7 +212,7 @@ public final class RepositoryServiceFactory if (repository == null) { - throw new RepositoryNotFoundException(namespaceAndName); + throw notFound(entity(namespaceAndName)); } return create(repository); diff --git a/scm-core/src/main/java/sonia/scm/repository/spi/BrowseCommand.java b/scm-core/src/main/java/sonia/scm/repository/spi/BrowseCommand.java index be679f9df1..ee37d6243e 100644 --- a/scm-core/src/main/java/sonia/scm/repository/spi/BrowseCommand.java +++ b/scm-core/src/main/java/sonia/scm/repository/spi/BrowseCommand.java @@ -35,7 +35,6 @@ package sonia.scm.repository.spi; //~--- non-JDK imports -------------------------------------------------------- -import sonia.scm.NotFoundException; import sonia.scm.repository.BrowserResult; import java.io.IOException; @@ -60,5 +59,5 @@ public interface BrowseCommand * * @throws IOException */ - BrowserResult getBrowserResult(BrowseCommandRequest request) throws IOException, NotFoundException; + BrowserResult getBrowserResult(BrowseCommandRequest request) throws IOException; } diff --git a/scm-core/src/main/java/sonia/scm/repository/spi/CatCommand.java b/scm-core/src/main/java/sonia/scm/repository/spi/CatCommand.java index 06f242783b..81600230db 100644 --- a/scm-core/src/main/java/sonia/scm/repository/spi/CatCommand.java +++ b/scm-core/src/main/java/sonia/scm/repository/spi/CatCommand.java @@ -33,9 +33,6 @@ package sonia.scm.repository.spi; -import sonia.scm.repository.PathNotFoundException; -import sonia.scm.repository.RevisionNotFoundException; - import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -47,7 +44,7 @@ import java.io.OutputStream; */ public interface CatCommand { - void getCatResult(CatCommandRequest request, OutputStream output) throws IOException, RevisionNotFoundException, PathNotFoundException; + void getCatResult(CatCommandRequest request, OutputStream output) throws IOException; - InputStream getCatResultStream(CatCommandRequest request) throws IOException, RevisionNotFoundException, PathNotFoundException; + InputStream getCatResultStream(CatCommandRequest request) throws IOException; } diff --git a/scm-core/src/main/java/sonia/scm/repository/spi/DiffCommand.java b/scm-core/src/main/java/sonia/scm/repository/spi/DiffCommand.java index 105f4e48e1..bba42cd86d 100644 --- a/scm-core/src/main/java/sonia/scm/repository/spi/DiffCommand.java +++ b/scm-core/src/main/java/sonia/scm/repository/spi/DiffCommand.java @@ -33,8 +33,6 @@ package sonia.scm.repository.spi; -import sonia.scm.repository.RevisionNotFoundException; - import java.io.IOException; import java.io.OutputStream; @@ -56,5 +54,5 @@ public interface DiffCommand * @throws IOException * @throws RuntimeException */ - public void getDiffResult(DiffCommandRequest request, OutputStream output) throws IOException, RevisionNotFoundException; + public void getDiffResult(DiffCommandRequest request, OutputStream output) throws IOException; } diff --git a/scm-core/src/main/java/sonia/scm/repository/spi/HookEventFacade.java b/scm-core/src/main/java/sonia/scm/repository/spi/HookEventFacade.java index 52ef68f272..4bbe61ea41 100644 --- a/scm-core/src/main/java/sonia/scm/repository/spi/HookEventFacade.java +++ b/scm-core/src/main/java/sonia/scm/repository/spi/HookEventFacade.java @@ -40,10 +40,12 @@ import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryHookEvent; import sonia.scm.repository.RepositoryHookType; import sonia.scm.repository.RepositoryManager; -import sonia.scm.repository.RepositoryNotFoundException; import sonia.scm.repository.api.HookContext; import sonia.scm.repository.api.HookContextFactory; +import static sonia.scm.ContextEntry.ContextBuilder.entity; +import static sonia.scm.NotFoundException.notFound; + /** * * @author Sebastian Sdorra @@ -71,18 +73,18 @@ public final class HookEventFacade //~--- methods -------------------------------------------------------------- - public HookEventHandler handle(String id) throws RepositoryNotFoundException { + public HookEventHandler handle(String id) { return handle(repositoryManagerProvider.get().get(id)); } - public HookEventHandler handle(NamespaceAndName namespaceAndName) throws RepositoryNotFoundException { + public HookEventHandler handle(NamespaceAndName namespaceAndName) { return handle(repositoryManagerProvider.get().get(namespaceAndName)); } - public HookEventHandler handle(Repository repository) throws RepositoryNotFoundException { + public HookEventHandler handle(Repository repository) { if (repository == null) { - throw new RepositoryNotFoundException(repository); + throw notFound(entity(repository)); } return new HookEventHandler(repositoryManagerProvider.get(), diff --git a/scm-core/src/main/java/sonia/scm/repository/spi/LogCommand.java b/scm-core/src/main/java/sonia/scm/repository/spi/LogCommand.java index e4a7f3437b..f4babcee72 100644 --- a/scm-core/src/main/java/sonia/scm/repository/spi/LogCommand.java +++ b/scm-core/src/main/java/sonia/scm/repository/spi/LogCommand.java @@ -37,7 +37,6 @@ package sonia.scm.repository.spi; import sonia.scm.repository.Changeset; import sonia.scm.repository.ChangesetPagingResult; -import sonia.scm.repository.RevisionNotFoundException; import java.io.IOException; @@ -50,7 +49,7 @@ import java.io.IOException; */ public interface LogCommand { - Changeset getChangeset(String id) throws IOException, RevisionNotFoundException; + Changeset getChangeset(String id) throws IOException; - ChangesetPagingResult getChangesets(LogCommandRequest request) throws IOException, RevisionNotFoundException; + ChangesetPagingResult getChangesets(LogCommandRequest request) throws IOException; } diff --git a/scm-core/src/main/java/sonia/scm/repository/spi/MergeCommand.java b/scm-core/src/main/java/sonia/scm/repository/spi/MergeCommand.java new file mode 100644 index 0000000000..0a3680f6b3 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/repository/spi/MergeCommand.java @@ -0,0 +1,10 @@ +package sonia.scm.repository.spi; + +import sonia.scm.repository.api.MergeCommandResult; +import sonia.scm.repository.api.MergeDryRunCommandResult; + +public interface MergeCommand { + MergeCommandResult merge(MergeCommandRequest request); + + MergeDryRunCommandResult dryRun(MergeCommandRequest request); +} diff --git a/scm-core/src/main/java/sonia/scm/repository/spi/MergeCommandRequest.java b/scm-core/src/main/java/sonia/scm/repository/spi/MergeCommandRequest.java new file mode 100644 index 0000000000..baf03a0aef --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/repository/spi/MergeCommandRequest.java @@ -0,0 +1,93 @@ +package sonia.scm.repository.spi; + +import com.google.common.base.MoreObjects; +import com.google.common.base.Objects; +import com.google.common.base.Strings; +import sonia.scm.Validateable; +import sonia.scm.repository.Person; +import sonia.scm.util.Util; + +import java.io.Serializable; + +public class MergeCommandRequest implements Validateable, Resetable, Serializable, Cloneable { + + private static final long serialVersionUID = -2650236557922431528L; + + private String branchToMerge; + private String targetBranch; + private Person author; + private String messageTemplate; + + public String getBranchToMerge() { + return branchToMerge; + } + + public void setBranchToMerge(String branchToMerge) { + this.branchToMerge = branchToMerge; + } + + public String getTargetBranch() { + return targetBranch; + } + + public void setTargetBranch(String targetBranch) { + this.targetBranch = targetBranch; + } + + public Person getAuthor() { + return author; + } + + public void setAuthor(Person author) { + this.author = author; + } + + public String getMessageTemplate() { + return messageTemplate; + } + + public void setMessageTemplate(String messageTemplate) { + this.messageTemplate = messageTemplate; + } + + public boolean isValid() { + return !Strings.isNullOrEmpty(getBranchToMerge()) + && !Strings.isNullOrEmpty(getTargetBranch()); + } + + public void reset() { + this.setBranchToMerge(null); + this.setTargetBranch(null); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + + if (getClass() != obj.getClass()) { + return false; + } + + final MergeCommandRequest other = (MergeCommandRequest) obj; + + return Objects.equal(branchToMerge, other.branchToMerge) + && Objects.equal(targetBranch, other.targetBranch) + && Objects.equal(author, other.author); + } + + @Override + public int hashCode() { + return Objects.hashCode(branchToMerge, targetBranch, author); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("branchToMerge", branchToMerge) + .add("targetBranch", targetBranch) + .add("author", author) + .toString(); + } +} diff --git a/scm-core/src/main/java/sonia/scm/repository/spi/ModificationsCommand.java b/scm-core/src/main/java/sonia/scm/repository/spi/ModificationsCommand.java index e9b40e8a17..322468f827 100644 --- a/scm-core/src/main/java/sonia/scm/repository/spi/ModificationsCommand.java +++ b/scm-core/src/main/java/sonia/scm/repository/spi/ModificationsCommand.java @@ -32,7 +32,6 @@ package sonia.scm.repository.spi; import sonia.scm.repository.Modifications; -import sonia.scm.repository.RevisionNotFoundException; import java.io.IOException; @@ -46,8 +45,8 @@ import java.io.IOException; */ public interface ModificationsCommand { - Modifications getModifications(String revision) throws IOException, RevisionNotFoundException; + Modifications getModifications(String revision) throws IOException; - Modifications getModifications(ModificationsCommandRequest request) throws IOException, RevisionNotFoundException; + Modifications getModifications(ModificationsCommandRequest request) throws IOException; } diff --git a/scm-core/src/main/java/sonia/scm/repository/spi/RepositoryServiceProvider.java b/scm-core/src/main/java/sonia/scm/repository/spi/RepositoryServiceProvider.java index c66c56c0f1..77201d1a72 100644 --- a/scm-core/src/main/java/sonia/scm/repository/spi/RepositoryServiceProvider.java +++ b/scm-core/src/main/java/sonia/scm/repository/spi/RepositoryServiceProvider.java @@ -251,4 +251,12 @@ public abstract class RepositoryServiceProvider implements Closeable { throw new CommandNotSupportedException(Command.UNBUNDLE); } + + /** + * @since 2.0 + */ + public MergeCommand getMergeCommand() + { + throw new CommandNotSupportedException(Command.MERGE); + } } diff --git a/scm-core/src/main/java/sonia/scm/user/ChangePasswordNotAllowedException.java b/scm-core/src/main/java/sonia/scm/user/ChangePasswordNotAllowedException.java index fda5e69323..caa35e0b88 100644 --- a/scm-core/src/main/java/sonia/scm/user/ChangePasswordNotAllowedException.java +++ b/scm-core/src/main/java/sonia/scm/user/ChangePasswordNotAllowedException.java @@ -1,11 +1,19 @@ package sonia.scm.user; -public class ChangePasswordNotAllowedException extends RuntimeException { +import sonia.scm.ContextEntry; +import sonia.scm.ExceptionWithContext; +public class ChangePasswordNotAllowedException extends ExceptionWithContext { + + private static final String CODE = "9BR7qpDAe1"; public static final String WRONG_USER_TYPE = "User of type %s are not allowed to change password"; - public ChangePasswordNotAllowedException(String type) { - super(String.format(WRONG_USER_TYPE, type)); + public ChangePasswordNotAllowedException(ContextEntry.ContextBuilder context, String type) { + super(context.build(), String.format(WRONG_USER_TYPE, type)); } + @Override + public String getCode() { + return CODE; + } } diff --git a/scm-core/src/main/java/sonia/scm/user/InvalidPasswordException.java b/scm-core/src/main/java/sonia/scm/user/InvalidPasswordException.java index 870430a1bb..93a6a7c1d1 100644 --- a/scm-core/src/main/java/sonia/scm/user/InvalidPasswordException.java +++ b/scm-core/src/main/java/sonia/scm/user/InvalidPasswordException.java @@ -1,8 +1,18 @@ package sonia.scm.user; -public class InvalidPasswordException extends RuntimeException { +import sonia.scm.ContextEntry; +import sonia.scm.ExceptionWithContext; - public InvalidPasswordException() { - super("The given Password does not match with the stored one."); +public class InvalidPasswordException extends ExceptionWithContext { + + private static final String CODE = "8YR7aawFW1"; + + public InvalidPasswordException(ContextEntry.ContextBuilder context) { + super(context.build(), "The given old password does not match with the stored one."); + } + + @Override + public String getCode() { + return CODE; } } diff --git a/scm-core/src/main/java/sonia/scm/web/VndMediaType.java b/scm-core/src/main/java/sonia/scm/web/VndMediaType.java index 7b6d5cb039..e2a2218d34 100644 --- a/scm-core/src/main/java/sonia/scm/web/VndMediaType.java +++ b/scm-core/src/main/java/sonia/scm/web/VndMediaType.java @@ -44,6 +44,7 @@ public class VndMediaType { public static final String ME = PREFIX + "me" + SUFFIX; public static final String SOURCE = PREFIX + "source" + SUFFIX; + public static final String ERROR_TYPE = PREFIX + "error" + SUFFIX; private VndMediaType() { } diff --git a/scm-core/src/main/java/sonia/scm/web/filter/AuthenticationFilter.java b/scm-core/src/main/java/sonia/scm/web/filter/AuthenticationFilter.java index b88dda8a97..ffe6ecc787 100644 --- a/scm-core/src/main/java/sonia/scm/web/filter/AuthenticationFilter.java +++ b/scm-core/src/main/java/sonia/scm/web/filter/AuthenticationFilter.java @@ -301,7 +301,7 @@ public class AuthenticationFilter extends HttpFilter } } - chain.doFilter(new SecurityHttpServletRequestWrapper(request, username), + chain.doFilter(new PropagatePrincipleServletRequestWrapper(request, username), response); } diff --git a/scm-core/src/main/java/sonia/scm/web/filter/SecurityHttpServletRequestWrapper.java b/scm-core/src/main/java/sonia/scm/web/filter/PropagatePrincipleServletRequestWrapper.java similarity index 78% rename from scm-core/src/main/java/sonia/scm/web/filter/SecurityHttpServletRequestWrapper.java rename to scm-core/src/main/java/sonia/scm/web/filter/PropagatePrincipleServletRequestWrapper.java index 2cd78ce807..2b40b0e73f 100644 --- a/scm-core/src/main/java/sonia/scm/web/filter/SecurityHttpServletRequestWrapper.java +++ b/scm-core/src/main/java/sonia/scm/web/filter/PropagatePrincipleServletRequestWrapper.java @@ -38,37 +38,17 @@ package sonia.scm.web.filter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; -/** - * - * @author Sebastian Sdorra - */ -public class SecurityHttpServletRequestWrapper extends HttpServletRequestWrapper -{ +public class PropagatePrincipleServletRequestWrapper extends HttpServletRequestWrapper { - /** - * Constructs ... - * - * - * @param request - * @param principal - */ - public SecurityHttpServletRequestWrapper(HttpServletRequest request, - String principal) - { + private final String principal; + + public PropagatePrincipleServletRequestWrapper(HttpServletRequest request, String principal) { super(request); this.principal = principal; } - //~--- get methods ---------------------------------------------------------- - @Override - public String getRemoteUser() - { + public String getRemoteUser() { return principal; } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private final String principal; } diff --git a/scm-core/src/test/java/sonia/scm/security/SyncingRealmHelperTest.java b/scm-core/src/test/java/sonia/scm/security/SyncingRealmHelperTest.java index 93b0752766..50bde53ae1 100644 --- a/scm-core/src/test/java/sonia/scm/security/SyncingRealmHelperTest.java +++ b/scm-core/src/test/java/sonia/scm/security/SyncingRealmHelperTest.java @@ -43,7 +43,6 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.AlreadyExistsException; -import sonia.scm.NotFoundException; import sonia.scm.group.Group; import sonia.scm.group.GroupManager; import sonia.scm.group.GroupNames; @@ -132,7 +131,7 @@ public class SyncingRealmHelperTest { * @throws IOException */ @Test - public void testStoreGroupCreate() throws AlreadyExistsException { + public void testStoreGroupCreate() { Group group = new Group("unit-test", "heartOfGold"); helper.store(group); @@ -143,7 +142,7 @@ public class SyncingRealmHelperTest { * Tests {@link SyncingRealmHelper#store(Group)}. */ @Test(expected = IllegalStateException.class) - public void testStoreGroupFailure() throws AlreadyExistsException { + public void testStoreGroupFailure() { Group group = new Group("unit-test", "heartOfGold"); doThrow(AlreadyExistsException.class).when(groupManager).create(group); @@ -169,7 +168,7 @@ public class SyncingRealmHelperTest { * @throws IOException */ @Test - public void testStoreUserCreate() throws AlreadyExistsException { + public void testStoreUserCreate() { User user = new User("tricia"); helper.store(user); @@ -180,7 +179,7 @@ public class SyncingRealmHelperTest { * Tests {@link SyncingRealmHelper#store(User)} with a thrown {@link AlreadyExistsException}. */ @Test(expected = IllegalStateException.class) - public void testStoreUserFailure() throws AlreadyExistsException { + public void testStoreUserFailure() { User user = new User("tricia"); doThrow(AlreadyExistsException.class).when(userManager).create(user); diff --git a/scm-it/src/test/java/sonia/scm/it/AnonymousAccessITCase.java b/scm-it/src/test/java/sonia/scm/it/AnonymousAccessITCase.java new file mode 100644 index 0000000000..0a329b8e1e --- /dev/null +++ b/scm-it/src/test/java/sonia/scm/it/AnonymousAccessITCase.java @@ -0,0 +1,26 @@ +package sonia.scm.it; + +import io.restassured.RestAssured; +import org.junit.Test; +import sonia.scm.it.utils.RestUtil; +import sonia.scm.it.utils.ScmRequests; + +import static org.junit.Assert.assertEquals; + +public class AnonymousAccessITCase { + + @Test + public void shouldAccessIndexResourceWithoutAuthentication() { + ScmRequests.start() + .requestIndexResource() + .assertStatusCode(200); + } + + @Test + public void shouldRejectUserResourceWithoutAuthentication() { + assertEquals(401, RestAssured.given() + .when() + .get(RestUtil.REST_BASE_URL.resolve("users/")) + .statusCode()); + } +} diff --git a/scm-it/src/test/java/sonia/scm/it/PermissionsITCase.java b/scm-it/src/test/java/sonia/scm/it/PermissionsITCase.java index 8785f1d8ce..aa91e67022 100644 --- a/scm-it/src/test/java/sonia/scm/it/PermissionsITCase.java +++ b/scm-it/src/test/java/sonia/scm/it/PermissionsITCase.java @@ -32,6 +32,7 @@ package sonia.scm.it; import org.apache.http.HttpStatus; +import org.assertj.core.api.Assertions; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -49,8 +50,10 @@ import sonia.scm.web.VndMediaType; import java.io.IOException; import java.util.Collection; import java.util.List; +import java.util.Map; import java.util.Objects; +import static java.util.Arrays.asList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static sonia.scm.it.utils.RepositoryUtil.addAndCommitRandomFile; @@ -72,7 +75,7 @@ public class PermissionsITCase { public TemporaryFolder temporaryFolder = new TemporaryFolder(); private final String repositoryType; - private int createdPermissions; + private Collection createdPermissions; public PermissionsITCase(String repositoryType) { @@ -94,7 +97,7 @@ public class PermissionsITCase { TestData.createNotAdminUser(USER_OWNER, USER_PASS); TestData.createUserPermission(USER_OWNER, PermissionType.OWNER, repositoryType); TestData.createNotAdminUser(USER_OTHER, USER_PASS); - createdPermissions = 3; + createdPermissions = asList(USER_READ, USER_WRITE, USER_OWNER); } @Test @@ -131,8 +134,8 @@ public class PermissionsITCase { @Test public void ownerShouldSeePermissions() { - List userPermissions = TestData.getUserPermissions(USER_OWNER, USER_PASS, repositoryType); - assertEquals(userPermissions.size(), createdPermissions); + List userPermissions = TestData.getUserPermissions(USER_OWNER, USER_PASS, repositoryType); + Assertions.assertThat(userPermissions).extracting(e -> e.get("name")).containsAll(createdPermissions); } @Test diff --git a/scm-it/src/test/java/sonia/scm/it/utils/ScmRequests.java b/scm-it/src/test/java/sonia/scm/it/utils/ScmRequests.java index e784d3b4b1..bde3892773 100644 --- a/scm-it/src/test/java/sonia/scm/it/utils/ScmRequests.java +++ b/scm-it/src/test/java/sonia/scm/it/utils/ScmRequests.java @@ -38,6 +38,10 @@ public class ScmRequests { return new ScmRequests(); } + public IndexResponse requestIndexResource() { + return new IndexResponse(applyGETRequest(RestUtil.REST_BASE_URL.toString())); + } + public IndexResponse requestIndexResource(String username, String password) { setUsername(username); setPassword(password); diff --git a/scm-it/src/test/java/sonia/scm/it/utils/TestData.java b/scm-it/src/test/java/sonia/scm/it/utils/TestData.java index a164fc6649..48605437c6 100644 --- a/scm-it/src/test/java/sonia/scm/it/utils/TestData.java +++ b/scm-it/src/test/java/sonia/scm/it/utils/TestData.java @@ -99,10 +99,10 @@ public class TestData { ; } - public static List getUserPermissions(String username, String password, String repositoryType) { + public static List getUserPermissions(String username, String password, String repositoryType) { return callUserPermissions(username, password, repositoryType, HttpStatus.SC_OK) .extract() - .body().jsonPath().getList("_embedded.permissions"); + .body().jsonPath().getList("_embedded.permissions"); } public static ValidatableResponse callUserPermissions(String username, String password, String repositoryType, int expectedStatusCode) { diff --git a/scm-plugins/pom.xml b/scm-plugins/pom.xml index 94d751dff7..db0173d1df 100644 --- a/scm-plugins/pom.xml +++ b/scm-plugins/pom.xml @@ -115,12 +115,6 @@ smp-maven-plugin 1.0.0-alpha-3 true - - - @scm-manager/ui-types - @scm-manager/ui-components - - diff --git a/scm-plugins/scm-git-plugin/pom.xml b/scm-plugins/scm-git-plugin/pom.xml index bf8769daca..a838e2f146 100644 --- a/scm-plugins/scm-git-plugin/pom.xml +++ b/scm-plugins/scm-git-plugin/pom.xml @@ -52,6 +52,10 @@ true true + + @scm-manager/ui-types + @scm-manager/ui-components + diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/rest/resources/GitConfigResource.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/rest/resources/GitConfigResource.java deleted file mode 100644 index bace7d0b73..0000000000 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/rest/resources/GitConfigResource.java +++ /dev/null @@ -1,124 +0,0 @@ -/** - * Copyright (c) 2010, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ - -package sonia.scm.api.rest.resources; - -//~--- non-JDK imports -------------------------------------------------------- - -import com.google.inject.Inject; -import com.google.inject.Singleton; - -import sonia.scm.repository.GitConfig; -import sonia.scm.repository.GitRepositoryHandler; - -//~--- JDK imports ------------------------------------------------------------ - -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; - -/** - * - * @author Sebastian Sdorra - */ -@Singleton -@Path("config/repositories/git") -@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) -public class GitConfigResource -{ - - /** - * Constructs ... - * - * - * - * @param repositoryHandler - */ - @Inject - public GitConfigResource(GitRepositoryHandler repositoryHandler) - { - this.repositoryHandler = repositoryHandler; - } - - //~--- get methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @return - */ - @GET - public GitConfig getConfig() - { - GitConfig config = repositoryHandler.getConfig(); - - if (config == null) - { - config = new GitConfig(); - repositoryHandler.setConfig(config); - } - - return config; - } - - //~--- set methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @param uriInfo - * @param config - * - * @return - */ - @POST - @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response setConfig(@Context UriInfo uriInfo, GitConfig config) - { - repositoryHandler.setConfig(config); - repositoryHandler.storeConfig(); - - return Response.created(uriInfo.getRequestUri()).build(); - } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private GitRepositoryHandler repositoryHandler; -} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/CloseableWrapper.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/CloseableWrapper.java new file mode 100644 index 0000000000..553f0f5a00 --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/CloseableWrapper.java @@ -0,0 +1,25 @@ +package sonia.scm.repository; + +import java.util.function.Consumer; + +public class CloseableWrapper implements AutoCloseable { + + private final C wrapped; + private final Consumer cleanup; + + public CloseableWrapper(C wrapped, Consumer cleanup) { + this.wrapped = wrapped; + this.cleanup = cleanup; + } + + public C get() { return wrapped; } + + @Override + public void close() { + try { + cleanup.accept(wrapped); + } catch (Throwable t) { + throw new RuntimeException(t); + } + } +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryHandler.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryHandler.java index 7ebc9b6f45..9da44d245f 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryHandler.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryHandler.java @@ -90,6 +90,8 @@ public class GitRepositoryHandler private static final Object LOCK = new Object(); private final Scheduler scheduler; + + private final GitWorkdirFactory workdirFactory; private Task task; @@ -104,10 +106,11 @@ public class GitRepositoryHandler * @param scheduler */ @Inject - public GitRepositoryHandler(ConfigurationStoreFactory storeFactory, FileSystem fileSystem, Scheduler scheduler) + public GitRepositoryHandler(ConfigurationStoreFactory storeFactory, FileSystem fileSystem, Scheduler scheduler, GitWorkdirFactory workdirFactory) { super(storeFactory, fileSystem); this.scheduler = scheduler; + this.workdirFactory = workdirFactory; } //~--- get methods ---------------------------------------------------------- @@ -234,4 +237,8 @@ public class GitRepositoryHandler { return new File(directory, DIRECTORY_REFS).exists(); } + + public GitWorkdirFactory getWorkdirFactory() { + return workdirFactory; + } } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitUtil.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitUtil.java index 6d7be2e93c..f490b7ea4f 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitUtil.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitUtil.java @@ -56,6 +56,7 @@ import org.eclipse.jgit.treewalk.TreeWalk; import org.eclipse.jgit.util.FS; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import sonia.scm.ContextEntry; import sonia.scm.util.HttpUtil; import sonia.scm.util.Util; import sonia.scm.web.GitUserAgentProvider; @@ -204,7 +205,7 @@ public final class GitUtil } catch (GitAPIException ex) { - throw new InternalRepositoryException("could not fetch", ex); + throw new InternalRepositoryException(ContextEntry.ContextBuilder.entity("remote", directory.toString()).in(remoteRepository), "could not fetch", ex); } } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitWorkdirFactory.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitWorkdirFactory.java new file mode 100644 index 0000000000..f93713a221 --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitWorkdirFactory.java @@ -0,0 +1,8 @@ +package sonia.scm.repository; + +import sonia.scm.repository.spi.GitContext; +import sonia.scm.repository.spi.WorkingCopy; + +public interface GitWorkdirFactory { + WorkingCopy createWorkingCopy(GitContext gitContext); +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitIncomingOutgoingCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitIncomingOutgoingCommand.java index e90a1c11ed..3cf72166ea 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitIncomingOutgoingCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitIncomingOutgoingCommand.java @@ -160,7 +160,7 @@ public abstract class AbstractGitIncomingOutgoingCommand } catch (Exception ex) { - throw new InternalRepositoryException("could not execute incoming command", ex); + throw new InternalRepositoryException(repository, "could not execute incoming command", ex); } finally { @@ -200,13 +200,7 @@ public abstract class AbstractGitIncomingOutgoingCommand { if (e.getKey().startsWith(prefix)) { - if (ref != null) - { - throw new InternalRepositoryException("could not find remote branch"); - } - ref = e.getValue(); - break; } } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitPushOrPullCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitPushOrPullCommand.java index 75050c26ea..e4e37d6fed 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitPushOrPullCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitPushOrPullCommand.java @@ -114,7 +114,7 @@ public abstract class AbstractGitPushOrPullCommand extends AbstractGitCommand } catch (Exception ex) { - throw new InternalRepositoryException("could not execute push/pull command", ex); + throw new InternalRepositoryException(repository, "could not execute push/pull command", ex); } return counter; diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBlameCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBlameCommand.java index f50f245963..2ad38648da 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBlameCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBlameCommand.java @@ -55,6 +55,8 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; +import static sonia.scm.ContextEntry.ContextBuilder.entity; + //~--- JDK imports ------------------------------------------------------------ /** @@ -108,9 +110,8 @@ public class GitBlameCommand extends AbstractGitCommand implements BlameCommand if (gitBlameResult == null) { - throw new InternalRepositoryException( - "could not create blame result for path ".concat( - request.getPath())); + throw new InternalRepositoryException(entity("path", request.getPath()).in(repository), + "could not create blame result for path"); } List blameLines = new ArrayList(); @@ -150,7 +151,7 @@ public class GitBlameCommand extends AbstractGitCommand implements BlameCommand } catch (GitAPIException ex) { - throw new InternalRepositoryException("could not create blame view", ex); + throw new InternalRepositoryException(repository, "could not create blame view", ex); } return result; diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBranchesCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBranchesCommand.java index 0cc47100de..4922752b6f 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBranchesCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBranchesCommand.java @@ -102,7 +102,7 @@ public class GitBranchesCommand extends AbstractGitCommand } catch (GitAPIException ex) { - throw new InternalRepositoryException("could not read branches", ex); + throw new InternalRepositoryException(repository, "could not read branches", ex); } return branches; diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBrowseCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBrowseCommand.java index ab1b0ae420..9186572858 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBrowseCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitBrowseCommand.java @@ -55,9 +55,7 @@ import sonia.scm.repository.BrowserResult; import sonia.scm.repository.FileObject; import sonia.scm.repository.GitSubModuleParser; import sonia.scm.repository.GitUtil; -import sonia.scm.repository.PathNotFoundException; import sonia.scm.repository.Repository; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.repository.SubRepository; import sonia.scm.util.Util; @@ -104,7 +102,7 @@ public class GitBrowseCommand extends AbstractGitCommand @Override @SuppressWarnings("unchecked") public BrowserResult getBrowserResult(BrowseCommandRequest request) - throws IOException, NotFoundException { + throws IOException { logger.debug("try to create browse result for {}", request); BrowserResult result; @@ -166,7 +164,7 @@ public class GitBrowseCommand extends AbstractGitCommand */ private FileObject createFileObject(org.eclipse.jgit.lib.Repository repo, BrowseCommandRequest request, ObjectId revId, TreeWalk treeWalk) - throws IOException, RevisionNotFoundException { + throws IOException { FileObject file = new FileObject(); @@ -258,7 +256,7 @@ public class GitBrowseCommand extends AbstractGitCommand return result; } - private FileObject getEntry(org.eclipse.jgit.lib.Repository repo, BrowseCommandRequest request, ObjectId revId) throws IOException, NotFoundException { + private FileObject getEntry(org.eclipse.jgit.lib.Repository repo, BrowseCommandRequest request, ObjectId revId) throws IOException { RevWalk revWalk = null; TreeWalk treeWalk = null; @@ -309,7 +307,7 @@ public class GitBrowseCommand extends AbstractGitCommand return Strings.isNullOrEmpty(request.getPath()) || "/".equals(request.getPath()); } - private FileObject findChildren(FileObject parent, org.eclipse.jgit.lib.Repository repo, BrowseCommandRequest request, ObjectId revId, TreeWalk treeWalk) throws IOException, NotFoundException { + private FileObject findChildren(FileObject parent, org.eclipse.jgit.lib.Repository repo, BrowseCommandRequest request, ObjectId revId, TreeWalk treeWalk) throws IOException { List files = Lists.newArrayList(); while (treeWalk.next()) { @@ -337,7 +335,7 @@ public class GitBrowseCommand extends AbstractGitCommand } private FileObject findFirstMatch(org.eclipse.jgit.lib.Repository repo, - BrowseCommandRequest request, ObjectId revId, TreeWalk treeWalk) throws IOException, NotFoundException { + BrowseCommandRequest request, ObjectId revId, TreeWalk treeWalk) throws IOException { String[] pathElements = request.getPath().split("/"); int currentDepth = 0; int limit = pathElements.length; @@ -363,7 +361,7 @@ public class GitBrowseCommand extends AbstractGitCommand private Map getSubRepositories(org.eclipse.jgit.lib.Repository repo, ObjectId revision) - throws IOException, RevisionNotFoundException { + throws IOException { if (logger.isDebugEnabled()) { logger.debug("read submodules of {} at {}", repository.getName(), @@ -377,7 +375,7 @@ public class GitBrowseCommand extends AbstractGitCommand PATH_MODULES, baos); subRepositories = GitSubModuleParser.parse(baos.toString()); } - catch (PathNotFoundException ex) + catch (NotFoundException ex) { logger.trace("could not find .gitmodules", ex); subRepositories = Collections.EMPTY_MAP; @@ -388,7 +386,7 @@ public class GitBrowseCommand extends AbstractGitCommand private SubRepository getSubRepository(org.eclipse.jgit.lib.Repository repo, ObjectId revId, String path) - throws IOException, RevisionNotFoundException { + throws IOException { Map subRepositories = subrepositoryCache.get(revId); if (subRepositories == null) diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitCatCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitCatCommand.java index 4b05098d03..7477e0aee3 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitCatCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitCatCommand.java @@ -45,8 +45,6 @@ import org.eclipse.jgit.treewalk.filter.PathFilter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.repository.GitUtil; -import sonia.scm.repository.PathNotFoundException; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.util.Util; import java.io.Closeable; @@ -55,6 +53,9 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import static sonia.scm.ContextEntry.ContextBuilder.entity; +import static sonia.scm.NotFoundException.notFound; + public class GitCatCommand extends AbstractGitCommand implements CatCommand { @@ -65,7 +66,7 @@ public class GitCatCommand extends AbstractGitCommand implements CatCommand { } @Override - public void getCatResult(CatCommandRequest request, OutputStream output) throws IOException, PathNotFoundException, RevisionNotFoundException { + public void getCatResult(CatCommandRequest request, OutputStream output) throws IOException { logger.debug("try to read content for {}", request); try (ClosableObjectLoaderContainer closableObjectLoaderContainer = getLoader(request)) { closableObjectLoaderContainer.objectLoader.copyTo(output); @@ -73,24 +74,24 @@ public class GitCatCommand extends AbstractGitCommand implements CatCommand { } @Override - public InputStream getCatResultStream(CatCommandRequest request) throws IOException, PathNotFoundException, RevisionNotFoundException { + public InputStream getCatResultStream(CatCommandRequest request) throws IOException { logger.debug("try to read content for {}", request); return new InputStreamWrapper(getLoader(request)); } - void getContent(org.eclipse.jgit.lib.Repository repo, ObjectId revId, String path, OutputStream output) throws IOException, PathNotFoundException, RevisionNotFoundException { + void getContent(org.eclipse.jgit.lib.Repository repo, ObjectId revId, String path, OutputStream output) throws IOException { try (ClosableObjectLoaderContainer closableObjectLoaderContainer = getLoader(repo, revId, path)) { closableObjectLoaderContainer.objectLoader.copyTo(output); } } - private ClosableObjectLoaderContainer getLoader(CatCommandRequest request) throws IOException, PathNotFoundException, RevisionNotFoundException { + private ClosableObjectLoaderContainer getLoader(CatCommandRequest request) throws IOException { org.eclipse.jgit.lib.Repository repo = open(); ObjectId revId = getCommitOrDefault(repo, request.getRevision()); return getLoader(repo, revId, request.getPath()); } - private ClosableObjectLoaderContainer getLoader(Repository repo, ObjectId revId, String path) throws IOException, PathNotFoundException, RevisionNotFoundException { + private ClosableObjectLoaderContainer getLoader(Repository repo, ObjectId revId, String path) throws IOException { TreeWalk treeWalk = new TreeWalk(repo); treeWalk.setRecursive(Util.nonNull(path).contains("/")); @@ -102,7 +103,7 @@ public class GitCatCommand extends AbstractGitCommand implements CatCommand { try { entry = revWalk.parseCommit(revId); } catch (MissingObjectException e) { - throw new RevisionNotFoundException(revId.getName()); + throw notFound(entity("Revision", revId.getName()).in(repository)); } RevTree revTree = entry.getTree(); @@ -120,7 +121,7 @@ public class GitCatCommand extends AbstractGitCommand implements CatCommand { return new ClosableObjectLoaderContainer(loader, treeWalk, revWalk); } else { - throw new PathNotFoundException(path); + throw notFound(entity("Path", path).in("Revision", revId.getName()).in(repository)); } } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitContext.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitContext.java index 2175846d5a..b0dd8f1fd6 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitContext.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitContext.java @@ -38,6 +38,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.repository.GitUtil; +import sonia.scm.repository.Repository; //~--- JDK imports ------------------------------------------------------------ @@ -65,10 +66,12 @@ public class GitContext implements Closeable * * * @param directory + * @param repository */ - public GitContext(File directory) + public GitContext(File directory, Repository repository) { this.directory = directory; + this.repository = repository; } //~--- methods -------------------------------------------------------------- @@ -82,8 +85,8 @@ public class GitContext implements Closeable { logger.trace("close git repository {}", directory); - GitUtil.close(repository); - repository = null; + GitUtil.close(gitRepository); + gitRepository = null; } /** @@ -96,21 +99,30 @@ public class GitContext implements Closeable */ public org.eclipse.jgit.lib.Repository open() throws IOException { - if (repository == null) + if (gitRepository == null) { logger.trace("open git repository {}", directory); - repository = GitUtil.open(directory); + gitRepository = GitUtil.open(directory); } + return gitRepository; + } + + Repository getRepository() { return repository; } + File getDirectory() { + return directory; + } + //~--- fields --------------------------------------------------------------- /** Field description */ private final File directory; + private final Repository repository; /** Field description */ - private org.eclipse.jgit.lib.Repository repository; + private org.eclipse.jgit.lib.Repository gitRepository; } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLogCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLogCommand.java index 7175df81f4..2ea25126cf 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLogCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLogCommand.java @@ -54,7 +54,6 @@ import sonia.scm.repository.ChangesetPagingResult; import sonia.scm.repository.GitChangesetConverter; import sonia.scm.repository.GitUtil; import sonia.scm.repository.InternalRepositoryException; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.util.IOUtil; import java.io.IOException; @@ -62,6 +61,9 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; +import static sonia.scm.ContextEntry.ContextBuilder.entity; +import static sonia.scm.NotFoundException.notFound; + //~--- JDK imports ------------------------------------------------------------ /** @@ -86,7 +88,6 @@ public class GitLogCommand extends AbstractGitCommand implements LogCommand * * @param context * @param repository - * @param repositoryDirectory */ GitLogCommand(GitContext context, sonia.scm.repository.Repository repository) { @@ -163,7 +164,7 @@ public class GitLogCommand extends AbstractGitCommand implements LogCommand */ @Override @SuppressWarnings("unchecked") - public ChangesetPagingResult getChangesets(LogCommandRequest request) throws RevisionNotFoundException { + public ChangesetPagingResult getChangesets(LogCommandRequest request) { if (logger.isDebugEnabled()) { logger.debug("fetch changesets for request: {}", request); } @@ -261,11 +262,11 @@ public class GitLogCommand extends AbstractGitCommand implements LogCommand } catch (MissingObjectException e) { - throw new RevisionNotFoundException(e.getObjectId().name()); + throw notFound(entity("Revision", e.getObjectId().getName()).in(repository)); } catch (Exception ex) { - throw new InternalRepositoryException("could not create change log", ex); + throw new InternalRepositoryException(repository, "could not create change log", ex); } finally { diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java new file mode 100644 index 0000000000..5e9eac5230 --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java @@ -0,0 +1,169 @@ +package sonia.scm.repository.spi; + +import com.google.common.base.Strings; +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.subject.Subject; +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.MergeResult; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.merge.MergeStrategy; +import org.eclipse.jgit.merge.ResolveMerger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import sonia.scm.repository.GitWorkdirFactory; +import sonia.scm.repository.InternalRepositoryException; +import sonia.scm.repository.Person; +import sonia.scm.repository.api.MergeCommandResult; +import sonia.scm.repository.api.MergeDryRunCommandResult; +import sonia.scm.user.User; + +import java.io.IOException; +import java.text.MessageFormat; + +public class GitMergeCommand extends AbstractGitCommand implements MergeCommand { + + private static final Logger logger = LoggerFactory.getLogger(GitMergeCommand.class); + + private static final String MERGE_COMMIT_MESSAGE_TEMPLATE = String.join("\n", + "Merge of branch {0} into {1}", + "", + "Automatic merge by SCM-Manager."); + + private final GitWorkdirFactory workdirFactory; + + GitMergeCommand(GitContext context, sonia.scm.repository.Repository repository, GitWorkdirFactory workdirFactory) { + super(context, repository); + this.workdirFactory = workdirFactory; + } + + @Override + public MergeCommandResult merge(MergeCommandRequest request) { + try (WorkingCopy workingCopy = workdirFactory.createWorkingCopy(context)) { + Repository repository = workingCopy.get(); + logger.debug("cloned repository to folder {}", repository.getWorkTree()); + return new MergeWorker(repository, request).merge(); + } catch (IOException e) { + throw new InternalRepositoryException(context.getRepository(), "could not clone repository for merge", e); + } + } + + @Override + public MergeDryRunCommandResult dryRun(MergeCommandRequest request) { + try { + Repository repository = context.open(); + ResolveMerger merger = (ResolveMerger) MergeStrategy.RECURSIVE.newMerger(repository, true); + return new MergeDryRunCommandResult(merger.merge(repository.resolve(request.getBranchToMerge()), repository.resolve(request.getTargetBranch()))); + } catch (IOException e) { + throw new InternalRepositoryException(context.getRepository(), "could not clone repository for merge", e); + } + } + + private class MergeWorker { + + private final String target; + private final String toMerge; + private final Person author; + private final Git clone; + private final String messageTemplate; + + private MergeWorker(Repository clone, MergeCommandRequest request) { + this.target = request.getTargetBranch(); + this.toMerge = request.getBranchToMerge(); + this.author = request.getAuthor(); + this.messageTemplate = request.getMessageTemplate(); + this.clone = new Git(clone); + } + + private MergeCommandResult merge() throws IOException { + checkOutTargetBranch(); + MergeResult result = doMergeInClone(); + if (result.getMergeStatus().isSuccessful()) { + doCommit(); + push(); + return MergeCommandResult.success(); + } else { + return analyseFailure(result); + } + } + + private void checkOutTargetBranch() { + try { + clone.checkout().setName(target).call(); + } catch (GitAPIException e) { + throw new InternalRepositoryException(context.getRepository(), "could not checkout target branch for merge: " + target, e); + } + } + + private MergeResult doMergeInClone() throws IOException { + MergeResult result; + try { + result = clone.merge() + .setCommit(false) // we want to set the author manually + .include(toMerge, resolveRevision(toMerge)) + .call(); + } catch (GitAPIException e) { + throw new InternalRepositoryException(context.getRepository(), "could not merge branch " + toMerge + " into " + target, e); + } + return result; + } + + private void doCommit() { + logger.debug("merged branch {} into {}", toMerge, target); + Person authorToUse = determineAuthor(); + try { + clone.commit() + .setAuthor(authorToUse.getName(), authorToUse.getMail()) + .setMessage(MessageFormat.format(determineMessageTemplate(), toMerge, target)) + .call(); + } catch (GitAPIException e) { + throw new InternalRepositoryException(context.getRepository(), "could not commit merge between branch " + toMerge + " and " + target, e); + } + } + + private String determineMessageTemplate() { + if (Strings.isNullOrEmpty(messageTemplate)) { + return MERGE_COMMIT_MESSAGE_TEMPLATE; + } else { + return messageTemplate; + } + } + + private Person determineAuthor() { + if (author == null) { + Subject subject = SecurityUtils.getSubject(); + User user = subject.getPrincipals().oneByType(User.class); + String name = user.getDisplayName(); + String email = user.getMail(); + logger.debug("no author set; using logged in user: {} <{}>", name, email); + return new Person(name, email); + } else { + return author; + } + } + + private void push() { + try { + clone.push().call(); + } catch (GitAPIException e) { + throw new InternalRepositoryException(context.getRepository(), "could not push merged branch " + toMerge + " to origin", e); + } + logger.debug("pushed merged branch {}", target); + } + + private MergeCommandResult analyseFailure(MergeResult result) { + logger.info("could not merged branch {} into {} due to conflict in paths {}", toMerge, target, result.getConflicts().keySet()); + return MergeCommandResult.failure(result.getConflicts().keySet()); + } + + private ObjectId resolveRevision(String branchToMerge) throws IOException { + ObjectId resolved = clone.getRepository().resolve(branchToMerge); + if (resolved == null) { + return clone.getRepository().resolve("origin/" + branchToMerge); + } else { + return resolved; + } + } + } +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModificationsCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModificationsCommand.java index 2b35ba74f6..5040069c12 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModificationsCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModificationsCommand.java @@ -17,6 +17,8 @@ import java.io.IOException; import java.text.MessageFormat; import java.util.List; +import static sonia.scm.ContextEntry.ContextBuilder.entity; + @Slf4j public class GitModificationsCommand extends AbstractGitCommand implements ModificationsCommand { @@ -26,7 +28,7 @@ public class GitModificationsCommand extends AbstractGitCommand implements Modif } private Modifications createModifications(TreeWalk treeWalk, RevCommit commit, RevWalk revWalk, String revision) - throws IOException, UnsupportedModificationTypeException { + throws IOException { treeWalk.reset(); treeWalk.setRecursive(true); if (commit.getParentCount() > 0) { @@ -73,12 +75,7 @@ public class GitModificationsCommand extends AbstractGitCommand implements Modif } } catch (IOException ex) { log.error("could not open repository", ex); - throw new InternalRepositoryException(ex); - - } catch (UnsupportedModificationTypeException ex) { - log.error("Unsupported modification type", ex); - throw new InternalRepositoryException(ex); - + throw new InternalRepositoryException(entity(repository), "could not open repository", ex); } finally { GitUtil.release(revWalk); GitUtil.close(gitRepository); @@ -100,7 +97,7 @@ public class GitModificationsCommand extends AbstractGitCommand implements Modif } else if (type == DiffEntry.ChangeType.DELETE) { modifications.getRemoved().add(entry.getOldPath()); } else { - throw new UnsupportedModificationTypeException(MessageFormat.format("The modification type: {0} is not supported.", type)); + throw new UnsupportedModificationTypeException(entity(repository), MessageFormat.format("The modification type: {0} is not supported.", type)); } } } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitOutgoingCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitOutgoingCommand.java index 2a1f805cf6..6bdd8b793a 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitOutgoingCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitOutgoingCommand.java @@ -77,7 +77,6 @@ public class GitOutgoingCommand extends AbstractGitIncomingOutgoingCommand * @return * * @throws IOException - * @throws RepositoryException */ @Override public ChangesetPagingResult getOutgoingChangesets( diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitPullCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitPullCommand.java index a7b341ff5d..7a829355bf 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitPullCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitPullCommand.java @@ -101,7 +101,6 @@ public class GitPullCommand extends AbstractGitPushOrPullCommand * @return * * @throws IOException - * @throws RepositoryException */ @Override public PullResponse pull(PullCommandRequest request) @@ -249,7 +248,7 @@ public class GitPullCommand extends AbstractGitPushOrPullCommand } catch (GitAPIException ex) { - throw new InternalRepositoryException("error durring pull", ex); + throw new InternalRepositoryException(repository, "error during pull", ex); } return response; diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitPushCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitPushCommand.java index 193d68d7a5..3963366aa7 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitPushCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitPushCommand.java @@ -85,7 +85,6 @@ public class GitPushCommand extends AbstractGitPushOrPullCommand * @return * * @throws IOException - * @throws RepositoryException */ @Override public PushResponse push(PushCommandRequest request) diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitRepositoryServiceProvider.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitRepositoryServiceProvider.java index d60abd424d..bda0d87b21 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitRepositoryServiceProvider.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitRepositoryServiceProvider.java @@ -63,7 +63,8 @@ public class GitRepositoryServiceProvider extends RepositoryServiceProvider Command.INCOMING, Command.OUTGOING, Command.PUSH, - Command.PULL + Command.PULL, + Command.MERGE ); //J+ @@ -72,7 +73,7 @@ public class GitRepositoryServiceProvider extends RepositoryServiceProvider public GitRepositoryServiceProvider(GitRepositoryHandler handler, Repository repository) { this.handler = handler; this.repository = repository; - this.context = new GitContext(handler.getDirectory(repository)); + this.context = new GitContext(handler.getDirectory(repository), repository); } //~--- methods -------------------------------------------------------------- @@ -240,7 +241,12 @@ public class GitRepositoryServiceProvider extends RepositoryServiceProvider return new GitTagsCommand(context, repository); } - //~--- fields --------------------------------------------------------------- + @Override + public MergeCommand getMergeCommand() { + return new GitMergeCommand(context, repository, handler.getWorkdirFactory()); + } + +//~--- fields --------------------------------------------------------------- /** Field description */ private GitContext context; diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitTagsCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitTagsCommand.java index 02fee3cef0..807ec807e6 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitTagsCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitTagsCommand.java @@ -95,7 +95,7 @@ public class GitTagsCommand extends AbstractGitCommand implements TagsCommand } catch (GitAPIException ex) { - throw new InternalRepositoryException("could not read tags from repository", ex); + throw new InternalRepositoryException(repository, "could not read tags from repository", ex); } finally { diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/SimpleGitWorkdirFactory.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/SimpleGitWorkdirFactory.java new file mode 100644 index 0000000000..22fce5f330 --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/SimpleGitWorkdirFactory.java @@ -0,0 +1,62 @@ +package sonia.scm.repository.spi; + +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.util.FileUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import sonia.scm.repository.GitWorkdirFactory; +import sonia.scm.repository.InternalRepositoryException; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; + +public class SimpleGitWorkdirFactory implements GitWorkdirFactory { + + private static final Logger logger = LoggerFactory.getLogger(SimpleGitWorkdirFactory.class); + + private final File poolDirectory; + + public SimpleGitWorkdirFactory() { + this(new File(System.getProperty("java.io.tmpdir"), "scmm-git-pool")); + } + + public SimpleGitWorkdirFactory(File poolDirectory) { + this.poolDirectory = poolDirectory; + poolDirectory.mkdirs(); + } + + public WorkingCopy createWorkingCopy(GitContext gitContext) { + try { + Repository clone = cloneRepository(gitContext.getDirectory(), createNewWorkdir()); + return new WorkingCopy(clone, this::close); + } catch (GitAPIException e) { + throw new InternalRepositoryException(gitContext.getRepository(), "could not clone working copy of repository", e); + } catch (IOException e) { + throw new InternalRepositoryException(gitContext.getRepository(), "could not create temporary directory for clone of repository", e); + } + } + + private File createNewWorkdir() throws IOException { + return Files.createTempDirectory(poolDirectory.toPath(),"workdir").toFile(); + } + + protected Repository cloneRepository(File bareRepository, File target) throws GitAPIException { + return Git.cloneRepository() + .setURI(bareRepository.getAbsolutePath()) + .setDirectory(target) + .call() + .getRepository(); + } + + private void close(Repository repository) { + repository.close(); + try { + FileUtils.delete(repository.getWorkTree(), FileUtils.RECURSIVE); + } catch (IOException e) { + logger.warn("could not delete temporary git workdir '{}'", repository.getWorkTree(), e); + } + } +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/UnsupportedModificationTypeException.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/UnsupportedModificationTypeException.java index 5081a29d21..85119a3e9f 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/UnsupportedModificationTypeException.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/UnsupportedModificationTypeException.java @@ -1,9 +1,10 @@ package sonia.scm.repository.spi; +import sonia.scm.ContextEntry; import sonia.scm.repository.InternalRepositoryException; public class UnsupportedModificationTypeException extends InternalRepositoryException { - public UnsupportedModificationTypeException(String message) { - super(message); + public UnsupportedModificationTypeException(ContextEntry.ContextBuilder entity, String message) { + super(entity, message); } } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/WorkingCopy.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/WorkingCopy.java new file mode 100644 index 0000000000..fd0cba510b --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/WorkingCopy.java @@ -0,0 +1,12 @@ +package sonia.scm.repository.spi; + +import org.eclipse.jgit.lib.Repository; +import sonia.scm.repository.CloseableWrapper; + +import java.util.function.Consumer; + +public class WorkingCopy extends CloseableWrapper { + WorkingCopy(Repository wrapped, Consumer cleanup) { + super(wrapped, cleanup); + } +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitRepositoryViewer.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitRepositoryViewer.java index 773e09aada..82e19c77b7 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitRepositoryViewer.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitRepositoryViewer.java @@ -151,7 +151,6 @@ public class GitRepositoryViewer * @return * * @throws IOException - * @throws RepositoryException */ private BranchesModel createBranchesModel(Repository repository) throws IOException diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitServletModule.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitServletModule.java index e731e01a62..a3dac0e7d1 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitServletModule.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitServletModule.java @@ -41,6 +41,8 @@ import org.mapstruct.factory.Mappers; import sonia.scm.api.v2.resources.GitConfigDtoToGitConfigMapper; import sonia.scm.api.v2.resources.GitConfigToGitConfigDtoMapper; import sonia.scm.plugin.Extension; +import sonia.scm.repository.GitWorkdirFactory; +import sonia.scm.repository.spi.SimpleGitWorkdirFactory; import sonia.scm.web.lfs.LfsBlobStoreFactory; /** @@ -63,5 +65,7 @@ public class GitServletModule extends ServletModule bind(GitConfigDtoToGitConfigMapper.class).to(Mappers.getMapper(GitConfigDtoToGitConfigMapper.class).getClass()); bind(GitConfigToGitConfigDtoMapper.class).to(Mappers.getMapper(GitConfigToGitConfigDtoMapper.class).getClass()); + + bind(GitWorkdirFactory.class).to(SimpleGitWorkdirFactory.class); } } diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/CloseableWrapperTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/CloseableWrapperTest.java new file mode 100644 index 0000000000..e92ee7abb5 --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/CloseableWrapperTest.java @@ -0,0 +1,29 @@ +package sonia.scm.repository; + +import org.junit.Test; + +import java.util.function.Consumer; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +public class CloseableWrapperTest { + + @Test + public void shouldExecuteGivenMethodAtClose() { + Consumer wrapped = new Consumer() { + // no this cannot be replaced with a lambda because otherwise we could not use Mockito#spy + @Override + public void accept(String s) { + } + }; + + Consumer closer = spy(wrapped); + + try (CloseableWrapper wrapper = new CloseableWrapper<>("test", closer)) { + // nothing to do here + } + + verify(closer).accept("test"); + } +} diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitRepositoryHandlerTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitRepositoryHandlerTest.java index a9969f27f8..ad731280c5 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitRepositoryHandlerTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitRepositoryHandlerTest.java @@ -61,6 +61,9 @@ public class GitRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase { @Mock private ConfigurationStoreFactory factory; + @Mock + private GitWorkdirFactory gitWorkdirFactory; + @Override protected void checkDirectory(File directory) { File head = new File(directory, "HEAD"); @@ -84,7 +87,7 @@ public class GitRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase { protected RepositoryHandler createRepositoryHandler(ConfigurationStoreFactory factory, File directory) { GitRepositoryHandler repositoryHandler = new GitRepositoryHandler(factory, - new DefaultFileSystem(), scheduler); + new DefaultFileSystem(), scheduler, gitWorkdirFactory); repositoryHandler.init(contextProvider); @@ -100,7 +103,7 @@ public class GitRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase { @Test public void getDirectory() { GitRepositoryHandler repositoryHandler = new GitRepositoryHandler(factory, - new DefaultFileSystem(), scheduler); + new DefaultFileSystem(), scheduler, gitWorkdirFactory); GitConfig gitConfig = new GitConfig(); gitConfig.setRepositoryDirectory(new File("/path")); diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/AbstractGitCommandTestBase.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/AbstractGitCommandTestBase.java index 496b71e656..0b3c1d6e9d 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/AbstractGitCommandTestBase.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/AbstractGitCommandTestBase.java @@ -50,7 +50,9 @@ public class AbstractGitCommandTestBase extends ZippedRepositoryTestBase @After public void close() { - context.close(); + if (context != null) { + context.close(); + } } /** @@ -63,7 +65,7 @@ public class AbstractGitCommandTestBase extends ZippedRepositoryTestBase { if (context == null) { - context = new GitContext(repositoryDirectory); + context = new GitContext(repositoryDirectory, repository); } return context; diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBlameCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBlameCommandTest.java index d0fd627046..5757cd5d5e 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBlameCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBlameCommandTest.java @@ -85,7 +85,6 @@ public class GitBlameCommandTest extends AbstractGitCommandTestBase * * * @throws IOException - * @throws RepositoryException */ @Test public void testGetBlameResult() throws IOException @@ -119,7 +118,6 @@ public class GitBlameCommandTest extends AbstractGitCommandTestBase * * * @throws IOException - * @throws RepositoryException */ @Test public void testGetBlameResultWithRevision() diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBrowseCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBrowseCommandTest.java index 92b7ff69a9..5e63adfb70 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBrowseCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBrowseCommandTest.java @@ -32,7 +32,6 @@ package sonia.scm.repository.spi; import org.junit.Test; -import sonia.scm.NotFoundException; import sonia.scm.repository.BrowserResult; import sonia.scm.repository.FileObject; import sonia.scm.repository.GitConstants; @@ -54,7 +53,7 @@ import static org.junit.Assert.assertTrue; public class GitBrowseCommandTest extends AbstractGitCommandTestBase { @Test - public void testGetFile() throws IOException, NotFoundException { + public void testDefaultBranch() throws IOException { BrowseCommandRequest request = new BrowseCommandRequest(); request.setPath("a.txt"); BrowserResult result = createCommand().getBrowserResult(request); @@ -63,7 +62,7 @@ public class GitBrowseCommandTest extends AbstractGitCommandTestBase { } @Test - public void testDefaultDefaultBranch() throws IOException, NotFoundException { + public void testDefaultDefaultBranch() throws IOException { // without default branch, the repository head should be used FileObject root = createCommand().getBrowserResult(new BrowseCommandRequest()).getFile(); assertNotNull(root); @@ -78,7 +77,7 @@ public class GitBrowseCommandTest extends AbstractGitCommandTestBase { } @Test - public void testExplicitDefaultBranch() throws IOException, NotFoundException { + public void testExplicitDefaultBranch() throws IOException { repository.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "test-branch"); FileObject root = createCommand().getBrowserResult(new BrowseCommandRequest()).getFile(); @@ -91,7 +90,7 @@ public class GitBrowseCommandTest extends AbstractGitCommandTestBase { } @Test - public void testBrowse() throws IOException, NotFoundException { + public void testBrowse() throws IOException { FileObject root = createCommand().getBrowserResult(new BrowseCommandRequest()).getFile(); assertNotNull(root); @@ -113,7 +112,7 @@ public class GitBrowseCommandTest extends AbstractGitCommandTestBase { } @Test - public void testBrowseSubDirectory() throws IOException, NotFoundException { + public void testBrowseSubDirectory() throws IOException { BrowseCommandRequest request = new BrowseCommandRequest(); request.setPath("c"); @@ -143,7 +142,7 @@ public class GitBrowseCommandTest extends AbstractGitCommandTestBase { } @Test - public void testRecursive() throws IOException, NotFoundException { + public void testRecusive() throws IOException { BrowseCommandRequest request = new BrowseCommandRequest(); request.setRecursive(true); diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitCatCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitCatCommandTest.java index 3611c9c636..079fcac1da 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitCatCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitCatCommandTest.java @@ -32,10 +32,13 @@ package sonia.scm.repository.spi; +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; +import sonia.scm.NotFoundException; import sonia.scm.repository.GitConstants; -import sonia.scm.repository.PathNotFoundException; -import sonia.scm.repository.RevisionNotFoundException; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -51,9 +54,12 @@ import static org.junit.Assert.assertEquals; * @author Sebastian Sdorra */ public class GitCatCommandTest extends AbstractGitCommandTestBase { - + + @Rule + public final ExpectedException expectedException = ExpectedException.none(); + @Test - public void testDefaultBranch() throws IOException, PathNotFoundException, RevisionNotFoundException { + public void testDefaultBranch() throws IOException { // without default branch, the repository head should be used CatCommandRequest request = new CatCommandRequest(); request.setPath("a.txt"); @@ -66,7 +72,7 @@ public class GitCatCommandTest extends AbstractGitCommandTestBase { } @Test - public void testCat() throws IOException, PathNotFoundException, RevisionNotFoundException { + public void testCat() throws IOException { CatCommandRequest request = new CatCommandRequest(); request.setPath("a.txt"); @@ -75,32 +81,58 @@ public class GitCatCommandTest extends AbstractGitCommandTestBase { } @Test - public void testSimpleCat() throws IOException, PathNotFoundException, RevisionNotFoundException { + public void testSimpleCat() throws IOException { CatCommandRequest request = new CatCommandRequest(); request.setPath("b.txt"); assertEquals("b", execute(request)); } - @Test(expected = PathNotFoundException.class) - public void testUnknownFile() throws IOException, PathNotFoundException, RevisionNotFoundException { + @Test + public void testUnknownFile() throws IOException { CatCommandRequest request = new CatCommandRequest(); request.setPath("unknown"); - execute(request); - } - @Test(expected = RevisionNotFoundException.class) - public void testUnknownRevision() throws IOException, PathNotFoundException, RevisionNotFoundException { - CatCommandRequest request = new CatCommandRequest(); + expectedException.expect(new BaseMatcher() { + @Override + public void describeTo(Description description) { + description.appendText("expected NotFoundException for path"); + } + + @Override + public boolean matches(Object item) { + return "Path".equals(((NotFoundException)item).getContext().get(0).getType()); + } + }); - request.setRevision("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); - request.setPath("a.txt"); execute(request); } @Test - public void testSimpleStream() throws IOException, PathNotFoundException, RevisionNotFoundException { + public void testUnknownRevision() throws IOException { + CatCommandRequest request = new CatCommandRequest(); + + request.setRevision("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + request.setPath("a.txt"); + + expectedException.expect(new BaseMatcher() { + @Override + public void describeTo(Description description) { + description.appendText("expected NotFoundException for revision"); + } + + @Override + public boolean matches(Object item) { + return "Revision".equals(((NotFoundException)item).getContext().get(0).getType()); + } + }); + + execute(request); + } + + @Test + public void testSimpleStream() throws IOException { CatCommandRequest request = new CatCommandRequest(); request.setPath("b.txt"); @@ -113,7 +145,7 @@ public class GitCatCommandTest extends AbstractGitCommandTestBase { catResultStream.close(); } - private String execute(CatCommandRequest request) throws IOException, PathNotFoundException, RevisionNotFoundException { + private String execute(CatCommandRequest request) throws IOException { String content = null; ByteArrayOutputStream baos = new ByteArrayOutputStream(); diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitIncomingCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitIncomingCommandTest.java index e3d36601e7..acf0b0f820 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitIncomingCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitIncomingCommandTest.java @@ -61,7 +61,6 @@ public class GitIncomingCommandTest * * @throws GitAPIException * @throws IOException - * @throws RepositoryException */ @Test public void testGetIncomingChangesets() @@ -95,7 +94,6 @@ public class GitIncomingCommandTest * * @throws GitAPIException * @throws IOException - * @throws RepositoryException */ @Test public void testGetIncomingChangesetsWithAllreadyPullChangesets() @@ -105,7 +103,7 @@ public class GitIncomingCommandTest commit(outgoing, "added a"); - GitPullCommand pull = new GitPullCommand(handler, new GitContext(incomingDirectory), incomingRepository); + GitPullCommand pull = new GitPullCommand(handler, new GitContext(incomingDirectory, null), incomingRepository); PullCommandRequest req = new PullCommandRequest(); req.setRemoteRepository(outgoingRepository); pull.pull(req); @@ -132,7 +130,6 @@ public class GitIncomingCommandTest * * * @throws IOException - * @throws RepositoryException */ @Test public void testGetIncomingChangesetsWithEmptyRepository() @@ -156,7 +153,6 @@ public class GitIncomingCommandTest * * @throws GitAPIException * @throws IOException - * @throws RepositoryException */ @Test @Ignore @@ -191,7 +187,7 @@ public class GitIncomingCommandTest */ private GitIncomingCommand createCommand() { - return new GitIncomingCommand(handler, new GitContext(incomingDirectory), + return new GitIncomingCommand(handler, new GitContext(incomingDirectory, null), incomingRepository); } } diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMergeCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMergeCommandTest.java new file mode 100644 index 0000000000..1fca7814ed --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMergeCommandTest.java @@ -0,0 +1,139 @@ +package sonia.scm.repository.spi; + +import com.github.sdorra.shiro.ShiroRule; +import com.github.sdorra.shiro.SubjectAware; +import org.apache.shiro.subject.SimplePrincipalCollection; +import org.apache.shiro.subject.Subject; +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.lib.PersonIdent; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.RevCommit; +import org.junit.Rule; +import org.junit.Test; +import sonia.scm.repository.Person; +import sonia.scm.repository.api.MergeCommandResult; +import sonia.scm.user.User; + +import java.io.IOException; + +import static org.assertj.core.api.Assertions.assertThat; + +@SubjectAware(configuration = "classpath:sonia/scm/configuration/shiro.ini") +public class GitMergeCommandTest extends AbstractGitCommandTestBase { + + private static final String REALM = "AdminRealm"; + + @Rule + public ShiroRule shiro = new ShiroRule(); + + @Test + public void shouldDetectMergeableBranches() { + GitMergeCommand command = createCommand(); + MergeCommandRequest request = new MergeCommandRequest(); + request.setBranchToMerge("mergeable"); + request.setTargetBranch("master"); + + boolean mergeable = command.dryRun(request).isMergeable(); + + assertThat(mergeable).isTrue(); + } + + @Test + public void shouldDetectNotMergeableBranches() { + GitMergeCommand command = createCommand(); + MergeCommandRequest request = new MergeCommandRequest(); + request.setBranchToMerge("test-branch"); + request.setTargetBranch("master"); + + boolean mergeable = command.dryRun(request).isMergeable(); + + assertThat(mergeable).isFalse(); + } + + @Test + public void shouldMergeMergeableBranches() throws IOException, GitAPIException { + GitMergeCommand command = createCommand(); + MergeCommandRequest request = new MergeCommandRequest(); + request.setTargetBranch("master"); + request.setBranchToMerge("mergeable"); + request.setAuthor(new Person("Dirk Gently", "dirk@holistic.det")); + + MergeCommandResult mergeCommandResult = command.merge(request); + + assertThat(mergeCommandResult.isSuccess()).isTrue(); + + Repository repository = createContext().open(); + Iterable commits = new Git(repository).log().add(repository.resolve("master")).setMaxCount(1).call(); + RevCommit mergeCommit = commits.iterator().next(); + PersonIdent mergeAuthor = mergeCommit.getAuthorIdent(); + String message = mergeCommit.getFullMessage(); + assertThat(mergeAuthor.getName()).isEqualTo("Dirk Gently"); + assertThat(mergeAuthor.getEmailAddress()).isEqualTo("dirk@holistic.det"); + assertThat(message).contains("master", "mergeable"); + // We expect the merge result of file b.txt here by looking up the sha hash of its content. + // If the file is missing (aka not merged correctly) this will throw a MissingObjectException: + byte[] contentOfFileB = repository.open(repository.resolve("9513e9c76e73f3e562fd8e4c909d0607113c77c6")).getBytes(); + assertThat(new String(contentOfFileB)).isEqualTo("b\ncontent from branch\n"); + } + + @Test + public void shouldUseConfiguredCommitMessageTemplate() throws IOException, GitAPIException { + GitMergeCommand command = createCommand(); + MergeCommandRequest request = new MergeCommandRequest(); + request.setTargetBranch("master"); + request.setBranchToMerge("mergeable"); + request.setAuthor(new Person("Dirk Gently", "dirk@holistic.det")); + request.setMessageTemplate("simple"); + + MergeCommandResult mergeCommandResult = command.merge(request); + + assertThat(mergeCommandResult.isSuccess()).isTrue(); + + Repository repository = createContext().open(); + Iterable commits = new Git(repository).log().add(repository.resolve("master")).setMaxCount(1).call(); + RevCommit mergeCommit = commits.iterator().next(); + String message = mergeCommit.getFullMessage(); + assertThat(message).isEqualTo("simple"); + } + + @Test + public void shouldNotMergeConflictingBranches() { + GitMergeCommand command = createCommand(); + MergeCommandRequest request = new MergeCommandRequest(); + request.setBranchToMerge("test-branch"); + request.setTargetBranch("master"); + + MergeCommandResult mergeCommandResult = command.merge(request); + + assertThat(mergeCommandResult.isSuccess()).isFalse(); + assertThat(mergeCommandResult.getFilesWithConflict()).containsExactly("a.txt"); + } + + @Test + @SubjectAware(username = "admin", password = "secret") + public void shouldTakeAuthorFromSubjectIfNotSet() throws IOException, GitAPIException { + shiro.setSubject( + new Subject.Builder() + .principals(new SimplePrincipalCollection(new User("dirk", "Dirk Gently", "dirk@holistic.det"), REALM)) + .buildSubject()); + GitMergeCommand command = createCommand(); + MergeCommandRequest request = new MergeCommandRequest(); + request.setTargetBranch("master"); + request.setBranchToMerge("mergeable"); + + MergeCommandResult mergeCommandResult = command.merge(request); + + assertThat(mergeCommandResult.isSuccess()).isTrue(); + + Repository repository = createContext().open(); + Iterable mergeCommit = new Git(repository).log().add(repository.resolve("master")).setMaxCount(1).call(); + PersonIdent mergeAuthor = mergeCommit.iterator().next().getAuthorIdent(); + assertThat(mergeAuthor.getName()).isEqualTo("Dirk Gently"); + assertThat(mergeAuthor.getEmailAddress()).isEqualTo("dirk@holistic.det"); + } + + private GitMergeCommand createCommand() { + return new GitMergeCommand(createContext(), repository, new SimpleGitWorkdirFactory()); + } +} diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModificationsCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModificationsCommandTest.java index fb982f6f0c..41a516a124 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModificationsCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModificationsCommandTest.java @@ -18,8 +18,8 @@ public class GitModificationsCommandTest extends AbstractRemoteCommandTestBase { @Before public void init() { - incomingModificationsCommand = new GitModificationsCommand(new GitContext(incomingDirectory), incomingRepository); - outgoingModificationsCommand = new GitModificationsCommand(new GitContext(outgoingDirectory), outgoingRepository); + incomingModificationsCommand = new GitModificationsCommand(new GitContext(incomingDirectory, null), incomingRepository); + outgoingModificationsCommand = new GitModificationsCommand(new GitContext(outgoingDirectory, null), outgoingRepository); } @Test @@ -63,12 +63,12 @@ public class GitModificationsCommandTest extends AbstractRemoteCommandTestBase { } void pushOutgoingAndPullIncoming() throws IOException { - GitPushCommand cmd = new GitPushCommand(handler, new GitContext(outgoingDirectory), + GitPushCommand cmd = new GitPushCommand(handler, new GitContext(outgoingDirectory, null), outgoingRepository); PushCommandRequest request = new PushCommandRequest(); request.setRemoteRepository(incomingRepository); cmd.push(request); - GitPullCommand pullCommand = new GitPullCommand(handler, new GitContext(incomingDirectory), + GitPullCommand pullCommand = new GitPullCommand(handler, new GitContext(incomingDirectory, null), incomingRepository); PullCommandRequest pullRequest = new PullCommandRequest(); pullRequest.setRemoteRepository(incomingRepository); diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitOutgoingCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitOutgoingCommandTest.java index 3510650fb4..65592cf7e4 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitOutgoingCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitOutgoingCommandTest.java @@ -61,7 +61,6 @@ public class GitOutgoingCommandTest extends AbstractRemoteCommandTestBase * * @throws GitAPIException * @throws IOException - * @throws RepositoryException */ @Test public void testGetOutgoingChangesets() @@ -95,7 +94,6 @@ public class GitOutgoingCommandTest extends AbstractRemoteCommandTestBase * * @throws GitAPIException * @throws IOException - * @throws RepositoryException */ @Test public void testGetOutgoingChangesetsWithAlreadyPushedChanges() @@ -106,7 +104,7 @@ public class GitOutgoingCommandTest extends AbstractRemoteCommandTestBase commit(outgoing, "added a"); GitPushCommand push = new GitPushCommand(handler, - new GitContext(outgoingDirectory), + new GitContext(outgoingDirectory, null), outgoingRepository); PushCommandRequest req = new PushCommandRequest(); @@ -135,7 +133,6 @@ public class GitOutgoingCommandTest extends AbstractRemoteCommandTestBase * * * @throws IOException - * @throws RepositoryException */ @Test public void testGetOutgoingChangesetsWithEmptyRepository() @@ -161,7 +158,7 @@ public class GitOutgoingCommandTest extends AbstractRemoteCommandTestBase */ private GitOutgoingCommand createCommand() { - return new GitOutgoingCommand(handler, new GitContext(outgoingDirectory), + return new GitOutgoingCommand(handler, new GitContext(outgoingDirectory, null), outgoingRepository); } } diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitPushCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitPushCommandTest.java index 4f3d7e933d..70212ba233 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitPushCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitPushCommandTest.java @@ -61,7 +61,6 @@ public class GitPushCommandTest extends AbstractRemoteCommandTestBase * * @throws GitAPIException * @throws IOException - * @throws RepositoryException */ @Test public void testPush() @@ -99,7 +98,7 @@ public class GitPushCommandTest extends AbstractRemoteCommandTestBase */ private GitPushCommand createCommand() { - return new GitPushCommand(handler, new GitContext(outgoingDirectory), + return new GitPushCommand(handler, new GitContext(outgoingDirectory, null), outgoingRepository); } } diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/SimpleGitWorkdirFactoryTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/SimpleGitWorkdirFactoryTest.java new file mode 100644 index 0000000000..0c39a1deb0 --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/SimpleGitWorkdirFactoryTest.java @@ -0,0 +1,87 @@ +package sonia.scm.repository.spi; + +import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.lib.Repository; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.File; +import java.io.IOException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +public class SimpleGitWorkdirFactoryTest extends AbstractGitCommandTestBase { + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @Test + public void emptyPoolShouldCreateNewWorkdir() throws IOException { + SimpleGitWorkdirFactory factory = new SimpleGitWorkdirFactory(temporaryFolder.newFolder()); + File masterRepo = createRepositoryDirectory(); + + try (WorkingCopy workingCopy = factory.createWorkingCopy(createContext())) { + + assertThat(workingCopy.get().getDirectory()) + .exists() + .isNotEqualTo(masterRepo) + .isDirectory(); + assertThat(new File(workingCopy.get().getWorkTree(), "a.txt")) + .exists() + .isFile() + .hasContent("a\nline for blame"); + } + } + + @Test + public void cloneFromPoolShouldBeClosed() throws IOException { + PoolWithSpy factory = new PoolWithSpy(temporaryFolder.newFolder()); + + try (WorkingCopy workingCopy = factory.createWorkingCopy(createContext())) { + assertThat(workingCopy).isNotNull(); + } + verify(factory.createdClone).close(); + } + + @Test + public void cloneFromPoolShouldNotBeReused() throws IOException { + SimpleGitWorkdirFactory factory = new SimpleGitWorkdirFactory(temporaryFolder.newFolder()); + + File firstDirectory; + try (WorkingCopy workingCopy = factory.createWorkingCopy(createContext())) { + firstDirectory = workingCopy.get().getDirectory(); + } + try (WorkingCopy workingCopy = factory.createWorkingCopy(createContext())) { + File secondDirectory = workingCopy.get().getDirectory(); + assertThat(secondDirectory).isNotEqualTo(firstDirectory); + } + } + + @Test + public void cloneFromPoolShouldBeDeletedOnClose() throws IOException { + SimpleGitWorkdirFactory factory = new SimpleGitWorkdirFactory(temporaryFolder.newFolder()); + + File directory; + try (WorkingCopy workingCopy = factory.createWorkingCopy(createContext())) { + directory = workingCopy.get().getWorkTree(); + } + assertThat(directory).doesNotExist(); + } + + private static class PoolWithSpy extends SimpleGitWorkdirFactory { + PoolWithSpy(File poolDirectory) { + super(poolDirectory); + } + + Repository createdClone; + + @Override + protected Repository cloneRepository(File bareRepository, File destination) throws GitAPIException { + createdClone = spy(super.cloneRepository(bareRepository, destination)); + return createdClone; + } + } +} diff --git a/scm-plugins/scm-git-plugin/src/test/resources/sonia/scm/repository/spi/scm-git-spi-test.zip b/scm-plugins/scm-git-plugin/src/test/resources/sonia/scm/repository/spi/scm-git-spi-test.zip index 3fbab0be38..8f689e9664 100644 Binary files a/scm-plugins/scm-git-plugin/src/test/resources/sonia/scm/repository/spi/scm-git-spi-test.zip and b/scm-plugins/scm-git-plugin/src/test/resources/sonia/scm/repository/spi/scm-git-spi-test.zip differ diff --git a/scm-plugins/scm-hg-plugin/pom.xml b/scm-plugins/scm-hg-plugin/pom.xml index 8dcc653642..6b7664c140 100644 --- a/scm-plugins/scm-hg-plugin/pom.xml +++ b/scm-plugins/scm-hg-plugin/pom.xml @@ -1,6 +1,6 @@ - + 4.0.0 @@ -16,7 +16,7 @@ Plugin for the version control system Mercurial - + com.aragost.javahg javahg @@ -30,10 +30,10 @@ - + - + com.mycila.maven-license-plugin maven-license-plugin @@ -59,6 +59,10 @@ true true + + @scm-manager/ui-types + @scm-manager/ui-components + @@ -80,15 +84,15 @@ - + - + maven.scm-manager.org scm-manager release repository http://maven.scm-manager.org/nexus/content/groups/public - + false @@ -101,7 +105,7 @@ default https://oss.sonatype.org/content/groups/public/ - + - + diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/rest/resources/HgConfigResource.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/rest/resources/HgConfigResource.java deleted file mode 100644 index 6fc4f4e01a..0000000000 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/rest/resources/HgConfigResource.java +++ /dev/null @@ -1,348 +0,0 @@ -/** - * Copyright (c) 2010, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ - - - -package sonia.scm.api.rest.resources; - -//~--- non-JDK imports -------------------------------------------------------- - -import com.google.inject.Inject; -import com.google.inject.Singleton; - -import sonia.scm.SCMContext; -import sonia.scm.installer.HgInstallerFactory; -import sonia.scm.installer.HgPackage; -import sonia.scm.installer.HgPackageReader; -import sonia.scm.installer.HgPackages; -import sonia.scm.net.ahc.AdvancedHttpClient; -import sonia.scm.repository.HgConfig; -import sonia.scm.repository.HgRepositoryHandler; - -//~--- JDK imports ------------------------------------------------------------ - -import java.io.IOException; - -import java.util.List; - -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; - -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; - -/** - * - * @author Sebastian Sdorra - */ -@Singleton -@Path("config/repositories/hg") -public class HgConfigResource -{ - - /** - * Constructs ... - * - * - * - * - * @param client - * @param handler - * @param pkgReader - */ - @Inject - public HgConfigResource(AdvancedHttpClient client, - HgRepositoryHandler handler, HgPackageReader pkgReader) - { - this.client = client; - this.handler = handler; - this.pkgReader = pkgReader; - } - - //~--- methods -------------------------------------------------------------- - - /** - * Method description - * - * - * @param uriInfo - * - * @return - */ - @POST - @Path("auto-configuration") - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public HgConfig autoConfiguration(@Context UriInfo uriInfo) - { - return autoConfiguration(uriInfo, null); - } - - /** - * Method description - * - * - * @param uriInfo - * @param config - * - * @return - */ - @POST - @Path("auto-configuration") - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public HgConfig autoConfiguration(@Context UriInfo uriInfo, HgConfig config) - { - if (config == null) - { - config = new HgConfig(); - } - - handler.doAutoConfiguration(config); - - return handler.getConfig(); - } - - /** - * Method description - * - * - * - * @param id - * @return - */ - @POST - @Path("packages/{pkgId}") - public Response installPackage(@PathParam("pkgId") String id) - { - Response response = null; - HgPackage pkg = pkgReader.getPackage(id); - - if (pkg != null) - { - if (HgInstallerFactory.createInstaller().installPackage(client, handler, - SCMContext.getContext().getBaseDirectory(), pkg)) - { - response = Response.noContent().build(); - } - else - { - response = - Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); - } - } - else - { - response = Response.status(Response.Status.NOT_FOUND).build(); - } - - return response; - } - - //~--- get methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @return - */ - @GET - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public HgConfig getConfig() - { - HgConfig config = handler.getConfig(); - - if (config == null) - { - config = new HgConfig(); - } - - return config; - } - - /** - * Method description - * - * - * @return - */ - @GET - @Path("installations/hg") - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public InstallationsResponse getHgInstallations() - { - List installations = - HgInstallerFactory.createInstaller().getHgInstallations(); - - return new InstallationsResponse(installations); - } - - /** - * Method description - * - * - * @return - */ - @GET - @Path("packages") - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public HgPackages getPackages() - { - return pkgReader.getPackages(); - } - - /** - * Method description - * - * - * @return - */ - @GET - @Path("installations/python") - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public InstallationsResponse getPythonInstallations() - { - List installations = - HgInstallerFactory.createInstaller().getPythonInstallations(); - - return new InstallationsResponse(installations); - } - - //~--- set methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @param uriInfo - * @param config - * - * @return - * - * @throws IOException - */ - @POST - @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response setConfig(@Context UriInfo uriInfo, HgConfig config) - throws IOException - { - handler.setConfig(config); - handler.storeConfig(); - - return Response.created(uriInfo.getRequestUri()).build(); - } - - //~--- inner classes -------------------------------------------------------- - - /** - * Class description - * - * - * @version Enter version here..., 11/04/25 - * @author Enter your name here... - */ - @XmlAccessorType(XmlAccessType.FIELD) - @XmlRootElement(name = "installations") - public static class InstallationsResponse - { - - /** - * Constructs ... - * - */ - public InstallationsResponse() {} - - /** - * Constructs ... - * - * - * @param paths - */ - public InstallationsResponse(List paths) - { - this.paths = paths; - } - - //~--- get methods -------------------------------------------------------- - - /** - * Method description - * - * - * @return - */ - public List getPaths() - { - return paths; - } - - //~--- set methods -------------------------------------------------------- - - /** - * Method description - * - * - * @param paths - */ - public void setPaths(List paths) - { - this.paths = paths; - } - - //~--- fields ------------------------------------------------------------- - - /** Field description */ - @XmlElement(name = "path") - private List paths; - } - - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private AdvancedHttpClient client; - - /** Field description */ - private HgRepositoryHandler handler; - - /** Field description */ - private HgPackageReader pkgReader; -} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/AbstractHgHandler.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/AbstractHgHandler.java index 19cfd37665..4115a514a2 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/AbstractHgHandler.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/AbstractHgHandler.java @@ -272,7 +272,7 @@ public class AbstractHgHandler } catch (JAXBException ex) { logger.error("could not parse result", ex); - throw new InternalRepositoryException("could not parse result", ex); + throw new InternalRepositoryException(repository, "could not parse result", ex); } } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgCatCommand.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgCatCommand.java index 554d6aaf05..6309720f75 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgCatCommand.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgCatCommand.java @@ -36,6 +36,9 @@ package sonia.scm.repository.spi; import com.aragost.javahg.commands.ExecutionException; import com.google.common.io.ByteStreams; import com.google.common.io.Closeables; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import sonia.scm.ContextEntry; import sonia.scm.repository.InternalRepositoryException; import sonia.scm.repository.Repository; import sonia.scm.web.HgUtil; @@ -46,6 +49,8 @@ import java.io.OutputStream; public class HgCatCommand extends AbstractCommand implements CatCommand { + private static final Logger log = LoggerFactory.getLogger(HgCatCommand.class); + HgCatCommand(HgCommandContext context, Repository repository) { super(context, repository); } @@ -70,7 +75,8 @@ public class HgCatCommand extends AbstractCommand implements CatCommand { try { return cmd.execute(request.getPath()); } catch (ExecutionException e) { - throw new InternalRepositoryException(e); + log.error("could not execute cat command", e); + throw new InternalRepositoryException(ContextEntry.ContextBuilder.entity(getRepository()), "could not execute cat command", e); } } } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgIncomingCommand.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgIncomingCommand.java index 55cf0fbe01..c60f2c5712 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgIncomingCommand.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgIncomingCommand.java @@ -103,7 +103,7 @@ public class HgIncomingCommand extends AbstractCommand } else { - throw new InternalRepositoryException("could not execute incoming command", ex); + throw new InternalRepositoryException(getRepository(), "could not execute incoming command", ex); } } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgOutgoingCommand.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgOutgoingCommand.java index b476b6c2ab..81bee6b9ff 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgOutgoingCommand.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgOutgoingCommand.java @@ -103,7 +103,7 @@ public class HgOutgoingCommand extends AbstractCommand } else { - throw new InternalRepositoryException("could not execute outgoing command", ex); + throw new InternalRepositoryException(getRepository(), "could not execute outgoing command", ex); } } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgPullCommand.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgPullCommand.java index 0eb23ef4e7..1d130a0f79 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgPullCommand.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgPullCommand.java @@ -97,7 +97,7 @@ public class HgPullCommand extends AbstractHgPushOrPullCommand } catch (ExecutionException ex) { - throw new InternalRepositoryException("could not execute push command", ex); + throw new InternalRepositoryException(getRepository(), "could not execute push command", ex); } return new PullResponse(result.size()); diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgPushCommand.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgPushCommand.java index fb0a1a1c73..b1c12037db 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgPushCommand.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgPushCommand.java @@ -97,7 +97,7 @@ public class HgPushCommand extends AbstractHgPushOrPullCommand } catch (ExecutionException ex) { - throw new InternalRepositoryException("could not execute push command", ex); + throw new InternalRepositoryException(getRepository(), "could not execute push command", ex); } return new PushResponse(result.size()); diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgHookCallbackServlet.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgHookCallbackServlet.java index 2734996686..25a368d25b 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgHookCallbackServlet.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgHookCallbackServlet.java @@ -44,11 +44,11 @@ import org.apache.shiro.SecurityUtils; import org.apache.shiro.subject.Subject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import sonia.scm.NotFoundException; import sonia.scm.repository.HgContext; import sonia.scm.repository.HgHookManager; import sonia.scm.repository.HgRepositoryHandler; import sonia.scm.repository.RepositoryHookType; -import sonia.scm.repository.RepositoryNotFoundException; import sonia.scm.repository.RepositoryUtil; import sonia.scm.repository.api.HgHookMessage; import sonia.scm.repository.api.HgHookMessage.Severity; @@ -275,17 +275,11 @@ public class HgHookCallbackServlet extends HttpServlet printMessages(response, context); } - catch (RepositoryNotFoundException ex) + catch (NotFoundException ex) { - if (logger.isErrorEnabled()) - { - logger.error("could not find repository with id {}", id); + logger.error(ex.getMessage()); - if (logger.isTraceEnabled()) - { - logger.trace("repository not found", ex); - } - } + logger.trace("repository not found", ex); response.sendError(HttpServletResponse.SC_NOT_FOUND); } diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgLogCommandTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgLogCommandTest.java index 29fc46ed57..1daa395e07 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgLogCommandTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgLogCommandTest.java @@ -39,7 +39,6 @@ import org.junit.Test; import sonia.scm.repository.Changeset; import sonia.scm.repository.ChangesetPagingResult; import sonia.scm.repository.Modifications; -import sonia.scm.repository.RevisionNotFoundException; import java.io.IOException; @@ -151,7 +150,7 @@ public class HgLogCommandTest extends AbstractHgCommandTestBase } @Test - public void testGetCommit() throws IOException, RevisionNotFoundException { + public void testGetCommit() throws IOException { HgLogCommand command = createComamnd(); String revision = "a9bacaf1b7fa0cebfca71fed4e59ed69a6319427"; Changeset c = diff --git a/scm-plugins/scm-svn-plugin/pom.xml b/scm-plugins/scm-svn-plugin/pom.xml index 395def8c27..4386efde5b 100644 --- a/scm-plugins/scm-svn-plugin/pom.xml +++ b/scm-plugins/scm-svn-plugin/pom.xml @@ -2,7 +2,7 @@ 4.0.0 - + scm-plugins sonia.scm.plugins @@ -28,7 +28,7 @@ - + sonia.svnkit svnkit-dav @@ -36,7 +36,7 @@ - + @@ -45,6 +45,10 @@ smp-maven-plugin true + + @scm-manager/ui-types + @scm-manager/ui-components + @@ -67,19 +71,19 @@ - + maven.tmatesoft.com tmatesoft release repository https://maven.tmatesoft.com/content/repositories/releases - + maven.scm-manager.org scm-manager release repository http://maven.scm-manager.org/nexus/content/groups/public - + diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/api/rest/resources/SvnConfigResource.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/api/rest/resources/SvnConfigResource.java deleted file mode 100644 index a1266ab4a4..0000000000 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/api/rest/resources/SvnConfigResource.java +++ /dev/null @@ -1,125 +0,0 @@ -/** - * Copyright (c) 2010, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ - -package sonia.scm.api.rest.resources; - -//~--- non-JDK imports -------------------------------------------------------- - -import com.google.inject.Inject; -import com.google.inject.Singleton; - -import sonia.scm.repository.RepositoryManager; -import sonia.scm.repository.SvnConfig; -import sonia.scm.repository.SvnRepositoryHandler; - -//~--- JDK imports ------------------------------------------------------------ - -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; - -/** - * - * @author Sebastian Sdorra - */ -@Singleton -@Path("config/repositories/svn") -@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) -public class SvnConfigResource -{ - - /** - * Constructs ... - * - * - * @param repositoryManager - */ - @Inject - public SvnConfigResource(RepositoryManager repositoryManager) - { - repositoryHandler = (SvnRepositoryHandler) repositoryManager.getHandler( - SvnRepositoryHandler.TYPE_NAME); - } - - //~--- get methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @return - */ - @GET - public SvnConfig getConfig() - { - SvnConfig config = repositoryHandler.getConfig(); - - if (config == null) - { - config = new SvnConfig(); - repositoryHandler.setConfig(config); - } - - return config; - } - - //~--- set methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @param uriInfo - * @param config - * - * @return - */ - @POST - @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response setConfig(@Context UriInfo uriInfo, SvnConfig config) - { - repositoryHandler.setConfig(config); - repositoryHandler.storeConfig(); - - return Response.created(uriInfo.getRequestUri()).build(); - } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private SvnRepositoryHandler repositoryHandler; -} diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnRepositoryHandler.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnRepositoryHandler.java index 7d5c9695c9..cd242faa2d 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnRepositoryHandler.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnRepositoryHandler.java @@ -56,6 +56,8 @@ import sonia.scm.util.Util; import java.io.File; +import static sonia.scm.ContextEntry.ContextBuilder.entity; + //~--- JDK imports ------------------------------------------------------------ /** @@ -173,7 +175,8 @@ public class SvnRepositoryHandler } catch (SVNException ex) { - throw new InternalRepositoryException(ex); + logger.error("could not create svn repository", ex); + throw new InternalRepositoryException(entity(repository), "could not create repository", ex); } finally { diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnUtil.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnUtil.java index 480026c27a..b4be68a51f 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnUtil.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnUtil.java @@ -59,6 +59,9 @@ import java.io.PrintWriter; import java.util.List; import java.util.Map; +import static sonia.scm.ContextEntry.ContextBuilder.entity; +import static sonia.scm.NotFoundException.notFound; + //~--- JDK imports ------------------------------------------------------------ /** @@ -102,7 +105,7 @@ public final class SvnUtil //~--- methods -------------------------------------------------------------- - public static long parseRevision(String v) throws RevisionNotFoundException { + public static long parseRevision(String v, Repository repository) { long result = -1l; if (!Strings.isNullOrEmpty(v)) @@ -113,7 +116,7 @@ public final class SvnUtil } catch (NumberFormatException ex) { - throw new RevisionNotFoundException(v); + throw notFound(entity("Revision", v).in(repository)); } } @@ -339,7 +342,7 @@ public final class SvnUtil } } - public static long getRevisionNumber(String revision) throws RevisionNotFoundException { + public static long getRevisionNumber(String revision, Repository repository) { // REVIEW Bei SVN wird ohne Revision die -1 genommen, was zu einem Fehler führt long revisionNumber = -1; @@ -351,7 +354,7 @@ public final class SvnUtil } catch (NumberFormatException ex) { - throw new RevisionNotFoundException(revision); + throw notFound(entity("Revision", revision).in(repository)); } } diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnBlameCommand.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnBlameCommand.java index fe9a72ffce..b589734d03 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnBlameCommand.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnBlameCommand.java @@ -98,7 +98,7 @@ public class SvnBlameCommand extends AbstractSvnCommand implements BlameCommand } catch (SVNException ex) { - throw new InternalRepositoryException("could not create blame result", ex); + throw new InternalRepositoryException(repository, "could not create blame result", ex); } return new BlameResult(blameLines.size(), blameLines); diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnBrowseCommand.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnBrowseCommand.java index 43cc1c3c70..e2f58b593b 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnBrowseCommand.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnBrowseCommand.java @@ -48,7 +48,6 @@ import org.tmatesoft.svn.core.io.SVNRepository; import sonia.scm.repository.BrowserResult; import sonia.scm.repository.FileObject; import sonia.scm.repository.Repository; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.repository.SubRepository; import sonia.scm.repository.SvnUtil; import sonia.scm.util.Util; @@ -79,9 +78,9 @@ public class SvnBrowseCommand extends AbstractSvnCommand @Override @SuppressWarnings("unchecked") - public BrowserResult getBrowserResult(BrowseCommandRequest request) throws RevisionNotFoundException { + public BrowserResult getBrowserResult(BrowseCommandRequest request) { String path = Strings.nullToEmpty(request.getPath()); - long revisionNumber = SvnUtil.getRevisionNumber(request.getRevision()); + long revisionNumber = SvnUtil.getRevisionNumber(request.getRevision(), repository); if (logger.isDebugEnabled()) { logger.debug("browser repository {} in path \"{}\" at revision {}", repository.getName(), path, revisionNumber); diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnCatCommand.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnCatCommand.java index 4936a16a8c..9ee43b2259 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnCatCommand.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnCatCommand.java @@ -44,9 +44,7 @@ import org.tmatesoft.svn.core.io.SVNRepository; import org.tmatesoft.svn.core.wc.SVNClientManager; import org.tmatesoft.svn.core.wc.admin.SVNLookClient; import sonia.scm.repository.InternalRepositoryException; -import sonia.scm.repository.PathNotFoundException; import sonia.scm.repository.Repository; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.repository.SvnUtil; import java.io.ByteArrayInputStream; @@ -54,6 +52,9 @@ import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.io.OutputStream; +import static sonia.scm.ContextEntry.ContextBuilder.entity; +import static sonia.scm.NotFoundException.notFound; + //~--- JDK imports ------------------------------------------------------------ /** @@ -79,7 +80,7 @@ public class SvnCatCommand extends AbstractSvnCommand implements CatCommand //~--- get methods ---------------------------------------------------------- @Override - public void getCatResult(CatCommandRequest request, OutputStream output) throws RevisionNotFoundException, PathNotFoundException { + public void getCatResult(CatCommandRequest request, OutputStream output) { if (logger.isDebugEnabled()) { logger.debug("try to get content for {}", request); @@ -96,14 +97,14 @@ public class SvnCatCommand extends AbstractSvnCommand implements CatCommand else { - long revisionNumber = SvnUtil.getRevisionNumber(revision); + long revisionNumber = SvnUtil.getRevisionNumber(revision, repository); getCatFromRevision(request, output, revisionNumber); } } @Override - public InputStream getCatResultStream(CatCommandRequest request) throws RevisionNotFoundException, PathNotFoundException { + public InputStream getCatResultStream(CatCommandRequest request) { // There seems to be no method creating an input stream as a result, so // we have no other possibility then to copy the content into a buffer and // stream it from there. @@ -112,7 +113,7 @@ public class SvnCatCommand extends AbstractSvnCommand implements CatCommand return new ByteArrayInputStream(output.toByteArray()); } - private void getCatFromRevision(CatCommandRequest request, OutputStream output, long revision) throws PathNotFoundException, RevisionNotFoundException { + private void getCatFromRevision(CatCommandRequest request, OutputStream output, long revision) { logger.debug("try to read content from revision {} and path {}", revision, request.getPath()); @@ -129,14 +130,14 @@ public class SvnCatCommand extends AbstractSvnCommand implements CatCommand } } - private void handleSvnException(CatCommandRequest request, SVNException ex) throws PathNotFoundException, RevisionNotFoundException { + private void handleSvnException(CatCommandRequest request, SVNException ex) { int svnErrorCode = ex.getErrorMessage().getErrorCode().getCode(); if (SVNErrorCode.FS_NOT_FOUND.getCode() == svnErrorCode) { - throw new PathNotFoundException(request.getPath()); + throw notFound(entity("Path", request.getPath()).in("Revision", request.getRevision()).in(repository)); } else if (SVNErrorCode.FS_NO_SUCH_REVISION.getCode() == svnErrorCode) { - throw new RevisionNotFoundException(request.getRevision()); + throw notFound(entity("Revision", request.getRevision()).in(repository)); } else { - throw new InternalRepositoryException("could not get content from revision", ex); + throw new InternalRepositoryException(repository, "could not get content from revision", ex); } } @@ -156,7 +157,7 @@ public class SvnCatCommand extends AbstractSvnCommand implements CatCommand } catch (SVNException ex) { - throw new InternalRepositoryException("could not get content from transaction", ex); + throw new InternalRepositoryException(repository, "could not get content from transaction", ex); } finally { diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnDiffCommand.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnDiffCommand.java index d05caa1945..4aaa12e28f 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnDiffCommand.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnDiffCommand.java @@ -86,7 +86,7 @@ public class SvnDiffCommand extends AbstractSvnCommand implements DiffCommand { SVNDiffClient diffClient = clientManager.getDiffClient(); diffClient.setDiffGenerator(new SvnNewDiffGenerator(new SCMSvnDiffGenerator())); - long currentRev = SvnUtil.getRevisionNumber(request.getRevision()); + long currentRev = SvnUtil.getRevisionNumber(request.getRevision(), repository); diffClient.setGitDiffFormat(request.getFormat() == DiffFormat.GIT); @@ -94,7 +94,7 @@ public class SvnDiffCommand extends AbstractSvnCommand implements DiffCommand { SVNRevision.create(currentRev - 1), SVNRevision.create(currentRev), SVNDepth.INFINITY, false, output); } catch (SVNException ex) { - throw new InternalRepositoryException("could not create diff", ex); + throw new InternalRepositoryException(repository, "could not create diff", ex); } finally { SvnUtil.dispose(clientManager); } diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnLogCommand.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnLogCommand.java index 332dcb55a6..be102be1bd 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnLogCommand.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnLogCommand.java @@ -47,7 +47,6 @@ import sonia.scm.repository.Changeset; import sonia.scm.repository.ChangesetPagingResult; import sonia.scm.repository.InternalRepositoryException; import sonia.scm.repository.Repository; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.repository.SvnUtil; import sonia.scm.util.Util; @@ -76,7 +75,7 @@ public class SvnLogCommand extends AbstractSvnCommand implements LogCommand @Override @SuppressWarnings("unchecked") - public Changeset getChangeset(String revision) throws RevisionNotFoundException { + public Changeset getChangeset(String revision) { Changeset changeset = null; if (logger.isDebugEnabled()) @@ -86,7 +85,7 @@ public class SvnLogCommand extends AbstractSvnCommand implements LogCommand try { - long revisioNumber = parseRevision(revision); + long revisioNumber = parseRevision(revision, repository); SVNRepository repo = open(); Collection entries = repo.log(null, null, revisioNumber, revisioNumber, true, true); @@ -98,7 +97,7 @@ public class SvnLogCommand extends AbstractSvnCommand implements LogCommand } catch (SVNException ex) { - throw new InternalRepositoryException("could not open repository", ex); + throw new InternalRepositoryException(repository, "could not open repository", ex); } return changeset; @@ -106,7 +105,7 @@ public class SvnLogCommand extends AbstractSvnCommand implements LogCommand @Override @SuppressWarnings("unchecked") - public ChangesetPagingResult getChangesets(LogCommandRequest request) throws RevisionNotFoundException { + public ChangesetPagingResult getChangesets(LogCommandRequest request) { if (logger.isDebugEnabled()) { logger.debug("fetch changesets for {}", request); @@ -115,8 +114,8 @@ public class SvnLogCommand extends AbstractSvnCommand implements LogCommand ChangesetPagingResult changesets = null; int start = request.getPagingStart(); int limit = request.getPagingLimit(); - long startRevision = parseRevision(request.getStartChangeset()); - long endRevision = parseRevision(request.getEndChangeset()); + long startRevision = parseRevision(request.getStartChangeset(), repository); + long endRevision = parseRevision(request.getEndChangeset(), repository); String[] pathArray = null; if (!Strings.isNullOrEmpty(request.getPath())) @@ -140,7 +139,7 @@ public class SvnLogCommand extends AbstractSvnCommand implements LogCommand } catch (SVNException ex) { - throw new InternalRepositoryException("could not open repository", ex); + throw new InternalRepositoryException(repository, "could not open repository", ex); } return changesets; diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnModificationsCommand.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnModificationsCommand.java index e6cedc8ebf..4b4f655b12 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnModificationsCommand.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnModificationsCommand.java @@ -7,11 +7,9 @@ import org.tmatesoft.svn.core.io.SVNRepository; import sonia.scm.repository.InternalRepositoryException; import sonia.scm.repository.Modifications; import sonia.scm.repository.Repository; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.repository.SvnUtil; import sonia.scm.util.Util; -import java.io.IOException; import java.util.Collection; @Slf4j @@ -24,11 +22,11 @@ public class SvnModificationsCommand extends AbstractSvnCommand implements Modif @Override @SuppressWarnings("unchecked") - public Modifications getModifications(String revision) throws IOException, RevisionNotFoundException { + public Modifications getModifications(String revision) { Modifications modifications = null; log.debug("get modifications {}", revision); try { - long revisionNumber = SvnUtil.parseRevision(revision); + long revisionNumber = SvnUtil.parseRevision(revision, repository); SVNRepository repo = open(); Collection entries = repo.log(null, null, revisionNumber, revisionNumber, true, true); @@ -36,13 +34,13 @@ public class SvnModificationsCommand extends AbstractSvnCommand implements Modif modifications = SvnUtil.createModifications(entries.iterator().next(), revision); } } catch (SVNException ex) { - throw new InternalRepositoryException("could not open repository", ex); + throw new InternalRepositoryException(repository, "could not open repository", ex); } return modifications; } @Override - public Modifications getModifications(ModificationsCommandRequest request) throws IOException, RevisionNotFoundException { + public Modifications getModifications(ModificationsCommandRequest request) { return getModifications(request.getRevision()); } diff --git a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/spi/SvnBrowseCommandTest.java b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/spi/SvnBrowseCommandTest.java index bcf5d2ec55..d3e6a98558 100644 --- a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/spi/SvnBrowseCommandTest.java +++ b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/spi/SvnBrowseCommandTest.java @@ -36,7 +36,6 @@ package sonia.scm.repository.spi; import org.junit.Test; import sonia.scm.repository.BrowserResult; import sonia.scm.repository.FileObject; -import sonia.scm.repository.RevisionNotFoundException; import java.io.IOException; import java.util.Collection; @@ -55,7 +54,7 @@ public class SvnBrowseCommandTest extends AbstractSvnCommandTestBase { @Test - public void testBrowseWithFilePath() throws RevisionNotFoundException { + public void testBrowseWithFilePath() { BrowseCommandRequest request = new BrowseCommandRequest(); request.setPath("a.txt"); FileObject file = createCommand().getBrowserResult(request).getFile(); @@ -65,7 +64,7 @@ public class SvnBrowseCommandTest extends AbstractSvnCommandTestBase } @Test - public void testBrowse() throws RevisionNotFoundException { + public void testBrowse() { Collection foList = getRootFromTip(new BrowseCommandRequest()); FileObject a = getFileObject(foList, "a.txt"); @@ -89,7 +88,7 @@ public class SvnBrowseCommandTest extends AbstractSvnCommandTestBase * @throws IOException */ @Test - public void testBrowseSubDirectory() throws RevisionNotFoundException { + public void testBrowseSubDirectory() { BrowseCommandRequest request = new BrowseCommandRequest(); request.setPath("c"); @@ -136,7 +135,7 @@ public class SvnBrowseCommandTest extends AbstractSvnCommandTestBase } @Test - public void testDisableLastCommit() throws RevisionNotFoundException { + public void testDisableLastCommit() { BrowseCommandRequest request = new BrowseCommandRequest(); request.setDisableLastCommit(true); @@ -150,7 +149,7 @@ public class SvnBrowseCommandTest extends AbstractSvnCommandTestBase } @Test - public void testRecursive() throws RevisionNotFoundException { + public void testRecursive() { BrowseCommandRequest request = new BrowseCommandRequest(); request.setRecursive(true); BrowserResult result = createCommand().getBrowserResult(request); @@ -199,7 +198,7 @@ public class SvnBrowseCommandTest extends AbstractSvnCommandTestBase .orElseThrow(() -> new AssertionError("file " + name + " not found")); } - private Collection getRootFromTip(BrowseCommandRequest request) throws RevisionNotFoundException { + private Collection getRootFromTip(BrowseCommandRequest request) { BrowserResult result = createCommand().getBrowserResult(request); assertNotNull(result); diff --git a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/spi/SvnCatCommandTest.java b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/spi/SvnCatCommandTest.java index 3980b3e558..e899c63b19 100644 --- a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/spi/SvnCatCommandTest.java +++ b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/spi/SvnCatCommandTest.java @@ -32,9 +32,12 @@ package sonia.scm.repository.spi; +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.junit.Rule; import org.junit.Test; -import sonia.scm.repository.PathNotFoundException; -import sonia.scm.repository.RevisionNotFoundException; +import org.junit.rules.ExpectedException; +import sonia.scm.NotFoundException; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -46,8 +49,11 @@ import static org.junit.Assert.assertEquals; public class SvnCatCommandTest extends AbstractSvnCommandTestBase { + @Rule + public final ExpectedException expectedException = ExpectedException.none(); + @Test - public void testCat() throws PathNotFoundException, RevisionNotFoundException { + public void testCat() { CatCommandRequest request = new CatCommandRequest(); request.setPath("a.txt"); @@ -56,35 +62,59 @@ public class SvnCatCommandTest extends AbstractSvnCommandTestBase { } @Test - public void testSimpleCat() throws PathNotFoundException, RevisionNotFoundException { + public void testSimpleCat() { CatCommandRequest request = new CatCommandRequest(); request.setPath("c/d.txt"); assertEquals("d", execute(request)); } - @Test(expected = PathNotFoundException.class) - public void testUnknownFile() throws PathNotFoundException, RevisionNotFoundException { + @Test + public void testUnknownFile() { CatCommandRequest request = new CatCommandRequest(); request.setPath("unknown"); request.setRevision("1"); - execute(request); - } + expectedException.expect(new BaseMatcher() { + @Override + public void describeTo(Description description) { + description.appendText("expected NotFoundException for path"); + } - @Test(expected = RevisionNotFoundException.class) - public void testUnknownRevision() throws PathNotFoundException, RevisionNotFoundException { - CatCommandRequest request = new CatCommandRequest(); - - request.setPath("a.txt"); - request.setRevision("42"); + @Override + public boolean matches(Object item) { + return "Path".equals(((NotFoundException)item).getContext().get(0).getType()); + } + }); execute(request); } @Test - public void testSimpleStream() throws IOException, PathNotFoundException, RevisionNotFoundException { + public void testUnknownRevision() { + CatCommandRequest request = new CatCommandRequest(); + + request.setPath("a.txt"); + request.setRevision("42"); + + expectedException.expect(new BaseMatcher() { + @Override + public void describeTo(Description description) { + description.appendText("expected NotFoundException for revision"); + } + + @Override + public boolean matches(Object item) { + return "Revision".equals(((NotFoundException)item).getContext().get(0).getType()); + } + }); + + execute(request); + } + + @Test + public void testSimpleStream() throws IOException { CatCommandRequest request = new CatCommandRequest(); request.setPath("a.txt"); request.setRevision("1"); @@ -98,7 +128,7 @@ public class SvnCatCommandTest extends AbstractSvnCommandTestBase { catResultStream.close(); } - private String execute(CatCommandRequest request) throws PathNotFoundException, RevisionNotFoundException { + private String execute(CatCommandRequest request) { String content = null; ByteArrayOutputStream baos = new ByteArrayOutputStream(); diff --git a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/spi/SvnLogCommandTest.java b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/spi/SvnLogCommandTest.java index a55138f151..f2511a9ad9 100644 --- a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/spi/SvnLogCommandTest.java +++ b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/spi/SvnLogCommandTest.java @@ -38,7 +38,6 @@ import org.junit.Test; import sonia.scm.repository.Changeset; import sonia.scm.repository.ChangesetPagingResult; import sonia.scm.repository.Modifications; -import sonia.scm.repository.RevisionNotFoundException; import java.io.IOException; @@ -57,7 +56,7 @@ public class SvnLogCommandTest extends AbstractSvnCommandTestBase { @Test - public void testGetAll() throws RevisionNotFoundException { + public void testGetAll() { ChangesetPagingResult result = createCommand().getChangesets(new LogCommandRequest()); @@ -67,7 +66,7 @@ public class SvnLogCommandTest extends AbstractSvnCommandTestBase } @Test - public void testGetAllByPath() throws RevisionNotFoundException { + public void testGetAllByPath() { LogCommandRequest request = new LogCommandRequest(); request.setPath("a.txt"); @@ -83,7 +82,7 @@ public class SvnLogCommandTest extends AbstractSvnCommandTestBase } @Test - public void testGetAllWithLimit() throws RevisionNotFoundException { + public void testGetAllWithLimit() { LogCommandRequest request = new LogCommandRequest(); request.setPagingLimit(2); @@ -106,7 +105,7 @@ public class SvnLogCommandTest extends AbstractSvnCommandTestBase } @Test - public void testGetAllWithPaging() throws RevisionNotFoundException { + public void testGetAllWithPaging() { LogCommandRequest request = new LogCommandRequest(); request.setPagingStart(1); @@ -130,7 +129,7 @@ public class SvnLogCommandTest extends AbstractSvnCommandTestBase } @Test - public void testGetCommit() throws RevisionNotFoundException, IOException { + public void testGetCommit() { Changeset c = createCommand().getChangeset("3"); assertNotNull(c); @@ -151,7 +150,7 @@ public class SvnLogCommandTest extends AbstractSvnCommandTestBase } @Test - public void testGetRange() throws RevisionNotFoundException { + public void testGetRange() { LogCommandRequest request = new LogCommandRequest(); request.setStartChangeset("2"); diff --git a/scm-test/src/main/java/sonia/scm/repository/DummyRepositoryHandler.java b/scm-test/src/main/java/sonia/scm/repository/DummyRepositoryHandler.java index 5a1dc605bb..df974ab566 100644 --- a/scm-test/src/main/java/sonia/scm/repository/DummyRepositoryHandler.java +++ b/scm-test/src/main/java/sonia/scm/repository/DummyRepositoryHandler.java @@ -71,10 +71,10 @@ public class DummyRepositoryHandler @Override - protected void create(Repository repository, File directory) throws AlreadyExistsException { + protected void create(Repository repository, File directory) { String key = repository.getNamespace() + "/" + repository.getName(); if (existingRepoNames.contains(key)) { - throw new AlreadyExistsException(); + throw new AlreadyExistsException(repository); } else { existingRepoNames.add(key); } diff --git a/scm-test/src/main/java/sonia/scm/repository/SimpleRepositoryHandlerTestBase.java b/scm-test/src/main/java/sonia/scm/repository/SimpleRepositoryHandlerTestBase.java index 999ef1dc45..d9985d3332 100644 --- a/scm-test/src/main/java/sonia/scm/repository/SimpleRepositoryHandlerTestBase.java +++ b/scm-test/src/main/java/sonia/scm/repository/SimpleRepositoryHandlerTestBase.java @@ -35,7 +35,6 @@ package sonia.scm.repository; import org.junit.Test; import sonia.scm.AbstractTestBase; -import sonia.scm.AlreadyExistsException; import sonia.scm.store.ConfigurationStoreFactory; import sonia.scm.store.InMemoryConfigurationStoreFactory; import sonia.scm.util.IOUtil; @@ -61,12 +60,12 @@ public abstract class SimpleRepositoryHandlerTestBase extends AbstractTestBase { ConfigurationStoreFactory factory, File directory); @Test - public void testCreate() throws AlreadyExistsException { + public void testCreate() { createRepository(); } @Test - public void testCreateResourcePath() throws AlreadyExistsException { + public void testCreateResourcePath() { Repository repository = createRepository(); String path = handler.createResourcePath(repository); @@ -76,7 +75,7 @@ public abstract class SimpleRepositoryHandlerTestBase extends AbstractTestBase { } @Test - public void testDelete() throws Exception { + public void testDelete() { Repository repository = createRepository(); handler.delete(repository); @@ -101,7 +100,7 @@ public abstract class SimpleRepositoryHandlerTestBase extends AbstractTestBase { } } - private Repository createRepository() throws AlreadyExistsException { + private Repository createRepository() { Repository repository = RepositoryTestData.createHeartOfGold(); handler.create(repository); diff --git a/scm-test/src/main/java/sonia/scm/user/UserManagerTestBase.java b/scm-test/src/main/java/sonia/scm/user/UserManagerTestBase.java index dddce344ce..3c684f58c9 100644 --- a/scm-test/src/main/java/sonia/scm/user/UserManagerTestBase.java +++ b/scm-test/src/main/java/sonia/scm/user/UserManagerTestBase.java @@ -39,12 +39,10 @@ import org.apache.shiro.SecurityUtils; import org.apache.shiro.subject.Subject; import org.junit.Test; import sonia.scm.AlreadyExistsException; -import sonia.scm.ConcurrentModificationException; import sonia.scm.Manager; import sonia.scm.ManagerTestBase; import sonia.scm.NotFoundException; -import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -63,7 +61,7 @@ public abstract class UserManagerTestBase extends ManagerTestBase { public static final int THREAD_COUNT = 10; @Test - public void testCreate() throws AlreadyExistsException { + public void testCreate() { User zaphod = UserTestData.createZaphod(); manager.create(zaphod); @@ -75,7 +73,7 @@ public abstract class UserManagerTestBase extends ManagerTestBase { } @Test(expected = AlreadyExistsException.class) - public void testCreateExisting() throws AlreadyExistsException { + public void testCreateExisting() { User zaphod = UserTestData.createZaphod(); manager.create(zaphod); @@ -87,7 +85,7 @@ public abstract class UserManagerTestBase extends ManagerTestBase { } @Test - public void testDelete() throws Exception { + public void testDelete() { User zaphod = UserTestData.createZaphod(); manager.create(zaphod); @@ -97,12 +95,12 @@ public abstract class UserManagerTestBase extends ManagerTestBase { } @Test(expected = NotFoundException.class) - public void testDeleteNotFound() throws Exception { + public void testDeleteNotFound() { manager.delete(UserTestData.createDent()); } @Test - public void testGet() throws AlreadyExistsException { + public void testGet() { User zaphod = UserTestData.createZaphod(); manager.create(zaphod); @@ -116,7 +114,7 @@ public abstract class UserManagerTestBase extends ManagerTestBase { } @Test - public void testGetAll() throws AlreadyExistsException { + public void testGetAll() { User zaphod = UserTestData.createZaphod(); manager.create(zaphod); @@ -181,7 +179,7 @@ public abstract class UserManagerTestBase extends ManagerTestBase { } @Test - public void testModify() throws AlreadyExistsException, NotFoundException, ConcurrentModificationException { + public void testModify() { User zaphod = UserTestData.createZaphod(); manager.create(zaphod); @@ -238,7 +236,7 @@ public abstract class UserManagerTestBase extends ManagerTestBase { } @Test - public void testRefresh() throws AlreadyExistsException, NotFoundException { + public void testRefresh() { User zaphod = UserTestData.createZaphod(); manager.create(zaphod); @@ -289,7 +287,7 @@ public abstract class UserManagerTestBase extends ManagerTestBase { finished = true; } - private User createUser() throws AlreadyExistsException { + private User createUser() { String id = UUID.randomUUID().toString(); User user = new User(id, id.concat(" displayName"), id.concat("@mail.com")); @@ -299,7 +297,7 @@ public abstract class UserManagerTestBase extends ManagerTestBase { return user; } - private void modifyAndDeleteUser(User user) throws IOException, NotFoundException, ConcurrentModificationException { + private void modifyAndDeleteUser(User user) { String name = user.getName(); String nd = name.concat(" new displayname"); diff --git a/scm-ui-components/package.json b/scm-ui-components/package.json index 527acbd9dc..bddbaba1c5 100644 --- a/scm-ui-components/package.json +++ b/scm-ui-components/package.json @@ -1,13 +1,16 @@ { "name": "scm-ui-components", + "version": "0.0.3", "description": "Lerna root for SCM-Manager UI Components", "private": true, "scripts": { "bootstrap": "lerna bootstrap", "link": "lerna exec -- yarn link", - "unlink": "lerna exec --no-bail -- yarn unlink || true" + "unlink": "lerna exec --no-bail -- yarn unlink || true", + "deploy": "node ./scripts/publish.js" }, "devDependencies": { - "lerna": "^3.2.1" + "lerna": "^3.4.3", + "xml2js": "^0.4.19" } } diff --git a/scm-ui-components/packages/ui-components/package.json b/scm-ui-components/packages/ui-components/package.json index e176a3d46d..823ca7143e 100644 --- a/scm-ui-components/packages/ui-components/package.json +++ b/scm-ui-components/packages/ui-components/package.json @@ -1,6 +1,6 @@ { "name": "@scm-manager/ui-components", - "version": "0.0.1", + "version": "2.0.0-SNAPSHOT", "description": "UI Components for SCM-Manager and its plugins", "main": "src/index.js", "files": [ @@ -26,7 +26,7 @@ }, "dependencies": { "@scm-manager/ui-extensions": "^0.1.1", - "@scm-manager/ui-types": "0.0.1", + "@scm-manager/ui-types": "2.0.0-SNAPSHOT", "classnames": "^2.2.6", "moment": "^2.22.2", "react": "^16.5.2", @@ -55,4 +55,4 @@ ] ] } -} +} \ No newline at end of file diff --git a/scm-ui-components/packages/ui-components/src/Help.js b/scm-ui-components/packages/ui-components/src/Help.js index 965d16f145..047f79244f 100644 --- a/scm-ui-components/packages/ui-components/src/Help.js +++ b/scm-ui-components/packages/ui-components/src/Help.js @@ -1,39 +1,33 @@ //@flow import React from "react"; import injectSheet from "react-jss"; -import classNames from "classnames"; +import Tooltip from './Tooltip'; +import HelpIcon from './HelpIcon'; const styles = { - img: { - display: "block" - }, - q: { - float: "left", - paddingLeft: "3px", - float: "right" + tooltip: { + display: "inline-block", + paddingLeft: "3px" } }; type Props = { message: string, classes: any -}; +} class Help extends React.Component { + render() { const { message, classes } = this.props; - const multiline = message.length > 60 ? "is-tooltip-multiline" : ""; return ( -
- -
+ + + ); } + } export default injectSheet(styles)(Help); + diff --git a/scm-ui-components/packages/ui-components/src/HelpIcon.js b/scm-ui-components/packages/ui-components/src/HelpIcon.js new file mode 100644 index 0000000000..fba8ead422 --- /dev/null +++ b/scm-ui-components/packages/ui-components/src/HelpIcon.js @@ -0,0 +1,14 @@ +//@flow +import React from "react"; +import classNames from "classnames"; + +type Props = { +}; + +class HelpIcon extends React.Component { + render() { + return + } +} + +export default HelpIcon; diff --git a/scm-ui-components/packages/ui-components/src/LabelWithHelpIcon.js b/scm-ui-components/packages/ui-components/src/LabelWithHelpIcon.js deleted file mode 100644 index b5d049e68d..0000000000 --- a/scm-ui-components/packages/ui-components/src/LabelWithHelpIcon.js +++ /dev/null @@ -1,46 +0,0 @@ -//@flow -import React from "react"; -import { Help } from "./index"; - -type Props = { - label: string, - helpText?: string -}; - -class LabelWithHelpIcon extends React.Component { - renderLabel = () => { - const label = this.props.label; - if (label) { - return ; - } - return ""; - }; - - renderHelp = () => { - const helpText = this.props.helpText; - if (helpText) { - return ( -
- -
- ); - } else return null; - }; - - renderLabelWithHelpIcon = () => { - if (this.props.label) { - return ( -
-
{this.renderLabel()}
- {this.renderHelp()} -
- ); - } else return null; - }; - - render() { - return this.renderLabelWithHelpIcon(); - } -} - -export default LabelWithHelpIcon; diff --git a/scm-ui-components/packages/ui-components/src/Loading.js b/scm-ui-components/packages/ui-components/src/Loading.js index 0a472ecb02..6b4817131f 100644 --- a/scm-ui-components/packages/ui-components/src/Loading.js +++ b/scm-ui-components/packages/ui-components/src/Loading.js @@ -1,10 +1,14 @@ //@flow + import React from "react"; import { translate } from "react-i18next"; import injectSheet from "react-jss"; import Image from "./Image"; const styles = { + minHeightContainer: { + minHeight: "256px" + }, wrapper: { position: "relative" }, @@ -34,14 +38,16 @@ class Loading extends React.Component { render() { const { message, t, classes } = this.props; return ( -
-
- {t("loading.alt")} -

{message}

+
+
+
+ {t("loading.alt")} +

{message}

+
); diff --git a/scm-ui-components/packages/ui-components/src/Tooltip.js b/scm-ui-components/packages/ui-components/src/Tooltip.js new file mode 100644 index 0000000000..d935b323c7 --- /dev/null +++ b/scm-ui-components/packages/ui-components/src/Tooltip.js @@ -0,0 +1,26 @@ +//@flow +import * as React from "react"; +import classNames from "classnames"; + +type Props = { + message: string, + className: string, + children: React.Node +}; + +class Tooltip extends React.Component { + render() { + const { className, message, children } = this.props; + const multiline = message.length > 60 ? "is-tooltip-multiline" : ""; + return ( +
+ {children} +
+ ); + } +} + +export default Tooltip; diff --git a/scm-ui-components/packages/ui-components/src/forms/Checkbox.js b/scm-ui-components/packages/ui-components/src/forms/Checkbox.js index 7db35958d5..93cd28c28f 100644 --- a/scm-ui-components/packages/ui-components/src/forms/Checkbox.js +++ b/scm-ui-components/packages/ui-components/src/forms/Checkbox.js @@ -10,7 +10,9 @@ type Props = { disabled?: boolean, helpText?: string }; + class Checkbox extends React.Component { + onCheckboxChange = (event: SyntheticInputEvent) => { if (this.props.onChange) { this.props.onChange(event.target.checked, this.props.name); @@ -20,12 +22,8 @@ class Checkbox extends React.Component { renderHelp = () => { const helpText = this.props.helpText; if (helpText) { - return ( -
- -
- ); - } else return null; + return ; + } }; render() { @@ -39,10 +37,11 @@ class Checkbox extends React.Component { onChange={this.onCheckboxChange} disabled={this.props.disabled} /> + {" "} {this.props.label} + {this.renderHelp()}
- {this.renderHelp()}
); } diff --git a/scm-ui-components/packages/ui-components/src/forms/InputField.js b/scm-ui-components/packages/ui-components/src/forms/InputField.js index 32c419f6c5..52ff9a369b 100644 --- a/scm-ui-components/packages/ui-components/src/forms/InputField.js +++ b/scm-ui-components/packages/ui-components/src/forms/InputField.js @@ -1,7 +1,7 @@ //@flow import React from "react"; import classNames from "classnames"; -import { LabelWithHelpIcon } from "../index"; +import LabelWithHelpIcon from "./LabelWithHelpIcon"; type Props = { label?: string, diff --git a/scm-ui-components/packages/ui-components/src/forms/LabelWithHelpIcon.js b/scm-ui-components/packages/ui-components/src/forms/LabelWithHelpIcon.js new file mode 100644 index 0000000000..8a917828ec --- /dev/null +++ b/scm-ui-components/packages/ui-components/src/forms/LabelWithHelpIcon.js @@ -0,0 +1,37 @@ +//@flow +import React from "react"; +import Help from '../Help'; + +type Props = { + label?: string, + helpText?: string +}; + +class LabelWithHelpIcon extends React.Component { + + renderHelp() { + const { helpText } = this.props; + if (helpText) { + return ( + + ); + } + } + + render() { + const {label } = this.props; + + if (label) { + const help = this.renderHelp(); + return ( + + ); + } + + return ""; + } +} + +export default LabelWithHelpIcon; diff --git a/scm-ui-components/packages/ui-components/src/forms/Select.js b/scm-ui-components/packages/ui-components/src/forms/Select.js index c3956ea8f3..ccb82e62da 100644 --- a/scm-ui-components/packages/ui-components/src/forms/Select.js +++ b/scm-ui-components/packages/ui-components/src/forms/Select.js @@ -1,7 +1,7 @@ //@flow import React from "react"; import classNames from "classnames"; -import { LabelWithHelpIcon } from "../index"; +import LabelWithHelpIcon from "./LabelWithHelpIcon"; export type SelectItem = { value: string, diff --git a/scm-ui-components/packages/ui-components/src/forms/Textarea.js b/scm-ui-components/packages/ui-components/src/forms/Textarea.js index 0bf386aca7..3db590122a 100644 --- a/scm-ui-components/packages/ui-components/src/forms/Textarea.js +++ b/scm-ui-components/packages/ui-components/src/forms/Textarea.js @@ -1,6 +1,6 @@ //@flow import React from "react"; -import { LabelWithHelpIcon } from "../index"; +import LabelWithHelpIcon from "./LabelWithHelpIcon"; export type SelectItem = { value: string, @@ -8,10 +8,11 @@ export type SelectItem = { }; type Props = { + name?: string, label?: string, placeholder?: SelectItem[], value?: string, - onChange: string => void, + onChange: (value: string, name?: string) => void, helpText?: string }; @@ -19,7 +20,7 @@ class Textarea extends React.Component { field: ?HTMLTextAreaElement; handleInput = (event: SyntheticInputEvent) => { - this.props.onChange(event.target.value); + this.props.onChange(event.target.value, this.props.name); }; render() { diff --git a/scm-ui-components/packages/ui-components/src/forms/index.js b/scm-ui-components/packages/ui-components/src/forms/index.js index 04f1a40aca..b1cf06740f 100644 --- a/scm-ui-components/packages/ui-components/src/forms/index.js +++ b/scm-ui-components/packages/ui-components/src/forms/index.js @@ -6,4 +6,5 @@ export { default as InputField } from "./InputField.js"; export { default as Select } from "./Select.js"; export { default as Textarea } from "./Textarea.js"; export { default as PasswordConfirmation } from "./PasswordConfirmation.js"; +export { default as LabelWithHelpIcon } from "./LabelWithHelpIcon"; diff --git a/scm-ui-components/packages/ui-components/src/index.js b/scm-ui-components/packages/ui-components/src/index.js index 2ebe57ec0c..41e385af8d 100644 --- a/scm-ui-components/packages/ui-components/src/index.js +++ b/scm-ui-components/packages/ui-components/src/index.js @@ -17,8 +17,9 @@ export { default as Notification } from "./Notification.js"; export { default as Paginator } from "./Paginator.js"; export { default as LinkPaginator } from "./LinkPaginator.js"; export { default as ProtectedRoute } from "./ProtectedRoute.js"; -export { default as Help } from "./Help.js"; -export { default as LabelWithHelpIcon } from "./LabelWithHelpIcon.js"; +export { default as Help } from "./Help"; +export { default as HelpIcon } from "./HelpIcon"; +export { default as Tooltip } from "./Tooltip"; export { getPageFromMatch } from "./urls"; export { apiClient, NOT_FOUND_ERROR, UNAUTHORIZED_ERROR } from "./apiclient.js"; diff --git a/scm-ui-components/packages/ui-types/package.json b/scm-ui-components/packages/ui-types/package.json index 3e61b9ab32..78452c2ef5 100644 --- a/scm-ui-components/packages/ui-types/package.json +++ b/scm-ui-components/packages/ui-types/package.json @@ -1,6 +1,6 @@ { "name": "@scm-manager/ui-types", - "version": "0.0.1", + "version": "2.0.0-SNAPSHOT", "description": "Flow types for SCM-Manager related Objects", "main": "src/index.js", "files": [ @@ -8,7 +8,7 @@ ], "repository": "https://bitbucket.org/sdorra/scm-manager", "author": "Sebastian Sdorra ", - "license" : "BSD-3-Clause", + "license": "BSD-3-Clause", "scripts": { "lint": "ui-bunder lint", "check": "flow check" @@ -21,10 +21,16 @@ [ "babelify", { - "plugins": ["@babel/plugin-proposal-class-properties"], - "presets": ["@babel/preset-env", "@babel/preset-flow", "@babel/preset-react"] + "plugins": [ + "@babel/plugin-proposal-class-properties" + ], + "presets": [ + "@babel/preset-env", + "@babel/preset-flow", + "@babel/preset-react" + ] } ] ] } -} +} \ No newline at end of file diff --git a/scm-ui-components/packages/ui-types/yarn-error.log b/scm-ui-components/packages/ui-types/yarn-error.log deleted file mode 100644 index b799a25089..0000000000 --- a/scm-ui-components/packages/ui-types/yarn-error.log +++ /dev/null @@ -1,52 +0,0 @@ -Arguments: - /usr/bin/node /home/ssdorra/.yarn/bin/yarn.js add --dev ui-bundler - -PATH: - /home/ssdorra/.yarn/bin:/usr/local/go/bin:/home/ssdorra/Projects/go/bin:/home/ssdorra/.local/bin:/usr/local/google-cloud-sdk/bin:/usr/local/go/bin:/home/ssdorra/.sdkman/candidates/maven/current/bin:/home/ssdorra/.sdkman/candidates/groovy/current/bin:/home/ssdorra/bin:/home/ssdorra/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/usr/lib/jvm/java-8-oracle/bin:/usr/lib/jvm/java-8-oracle/db/bin:/usr/lib/jvm/java-8-oracle/jre/bin:/home/ssdorra/Projects/go/bin - -Yarn version: - 1.9.2 - -Node version: - 8.11.4 - -Platform: - linux x64 - -Trace: - Error: https://registry.yarnpkg.com/ui-bundler: Not found - at Request.params.callback [as _callback] (/home/ssdorra/.yarn/lib/cli.js:64150:18) - at Request.self.callback (/home/ssdorra/.yarn/lib/cli.js:137416:22) - at emitTwo (events.js:126:13) - at Request.emit (events.js:214:7) - at Request. (/home/ssdorra/.yarn/lib/cli.js:138388:10) - at emitOne (events.js:116:13) - at Request.emit (events.js:211:7) - at IncomingMessage. (/home/ssdorra/.yarn/lib/cli.js:138310:12) - at Object.onceWrapper (events.js:313:30) - at emitNone (events.js:111:20) - -npm manifest: - { - "name": "@scm-manager/ui-types", - "version": "0.0.1", - "description": "Flow types for SCM-Manager related Objects", - "main": "src/index.js", - "files": [ - "src" - ], - "repository": "https://bitbucket.org/sdorra/scm-manager", - "author": "Sebastian Sdorra ", - "license": "MIT", - "scripts": { - "check": "flow check" - }, - "devDependencies": {} - } - -yarn manifest: - No manifest - -Lockfile: - # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. - # yarn lockfile v1 diff --git a/scm-ui-components/pom.xml b/scm-ui-components/pom.xml index f5544a9e24..3d4e31a50a 100644 --- a/scm-ui-components/pom.xml +++ b/scm-ui-components/pom.xml @@ -68,6 +68,16 @@ + + deploy + deploy + + run + + + + + diff --git a/scm-ui-components/scripts/publish.js b/scm-ui-components/scripts/publish.js new file mode 100644 index 0000000000..9eaae6fede --- /dev/null +++ b/scm-ui-components/scripts/publish.js @@ -0,0 +1,140 @@ +const fs = require("fs"); +const path = require("path"); +const {spawn} = require("child_process"); +const parseString = require('xml2js').parseString; + +function isSnapshot(version) { + return version.indexOf("SNAPSHOT") > 0; +} + +function createSnapshotVersion(version) { + const date = new Date(); + const year = date.getFullYear(); + const month = date.getMonth().toString().padStart(2, "0"); + const day = date.getDate().toString().padStart(2, "0"); + + const hours = date.getHours().toString().padStart(2, "0"); + const minutes = date.getMinutes().toString().padStart(2, "0"); + const seconds = date.getSeconds().toString().padStart(2, "0"); + + return version.replace("SNAPSHOT", `${year}${month}${day}-${hours}${minutes}${seconds}`); +} + +function createVersionForPublishing(version) { + if (isSnapshot(version)) { + return createSnapshotVersion(version); + } + return version; +} + +function publishPackages(version) { + console.log(`publish ${version} of all packages`); + return new Promise((resolve, reject) => { + const lerna = spawn("lerna", [ + "exec", "--", "yarn", "publish", "--new-version", version, "--access", "public" + ], { + stdio: [ + process.stdin, + process.stdout, + process.stderr + ] + }); + + lerna.on('close', (code) => { + if (code === 0) { + resolve(); + } else { + reject(new Error("publishing of packages failed with status code: " + code)); + } + }); + }); +} + +function setVersion(package, packages, newVersion) { + return new Promise((resolve, reject) => { + const packageJsonPath = path.join(package, "package.json"); + fs.readFile(packageJsonPath, "utf-8" , (err, content) => { + if (err) { + reject(err); + } else { + const packageJson = JSON.parse(content); + packageJson.version = newVersion; + + for (let dep in packageJson.dependencies) { + if (packages.indexOf(dep) >= 0) { + packageJson.dependencies[ dep ] = newVersion; + } + } + + fs.writeFile( packageJsonPath, JSON.stringify(packageJson, null, 2), (err) => { + if (err) { + reject(err) + } else { + console.log("modified", packageJsonPath); + resolve(); + } + }); + } + }); + }); +} + +function setVersions(newVersion) { + console.log("set versions of packages to", newVersion); + return new Promise((resolve, reject) => { + fs.readdir("packages", (err, packages) => { + if ( err ) { + reject(err); + } else { + const actions = []; + const packagesWithOrg = packages.map((name) => `@scm-manager/${name}`); + for (let pkg of packages) { + const action = setVersion(path.join("packages", pkg), packagesWithOrg, newVersion); + actions.push(action); + } + + resolve(Promise.all(actions)); + } + }); + }); +} + +function getVersion() { + return new Promise((resolve, reject) => { + fs.readFile("pom.xml", "utf8", (err, xml) => { + if (err) { + reject(err); + } else { + + parseString(xml, function (err, json) { + if (err) { + reject(err) + } else { + const project = json.project; + + let version = project.version; + if (!version) { + version = project.parent.version; + } + version = version[0]; + + resolve(version) + } + + }); + + } + }); + }); +} + +getVersion() + .then(version => { + const publishVersion = createVersionForPublishing(version); + return setVersions(publishVersion) + .then(() => publishPackages(publishVersion)) + .then(() => setVersions(version)); + }) + .catch((err) => { + throw err; + }); diff --git a/scm-ui-components/yarn.lock b/scm-ui-components/yarn.lock index eb7d40a3b3..2862a46547 100644 --- a/scm-ui-components/yarn.lock +++ b/scm-ui-components/yarn.lock @@ -5,6 +5,7 @@ "@lerna/add@^3.4.1": version "3.4.1" resolved "https://registry.yarnpkg.com/@lerna/add/-/add-3.4.1.tgz#d41068317e30f530df48220d256b5e79690b1877" + integrity sha512-Vf54B42jlD6G52qnv/cAGH70cVQIa+LX//lfsbkxHvzkhIqBl5J4KsnTOPkA9uq3R+zP58ayicCHB9ReiEWGJg== dependencies: "@lerna/bootstrap" "^3.4.1" "@lerna/command" "^3.3.0" @@ -20,6 +21,7 @@ "@lerna/batch-packages@^3.1.2": version "3.1.2" resolved "https://registry.yarnpkg.com/@lerna/batch-packages/-/batch-packages-3.1.2.tgz#74b5312a01a8916204cbc71237ffbe93144b99df" + integrity sha512-HAkpptrYeUVlBYbLScXgeCgk6BsNVXxDd53HVWgzzTWpXV4MHpbpeKrByyt7viXlNhW0w73jJbipb/QlFsHIhQ== dependencies: "@lerna/package-graph" "^3.1.2" "@lerna/validation-error" "^3.0.0" @@ -28,6 +30,7 @@ "@lerna/bootstrap@^3.4.1": version "3.4.1" resolved "https://registry.yarnpkg.com/@lerna/bootstrap/-/bootstrap-3.4.1.tgz#10635e9b547fb7d685949ac78e0923f73da2f52a" + integrity sha512-yZDJgNm/KDoRH2klzmQGmpWMg/XMzWgeWvauXkrfW/mj1wwmufOuh5pN4fBFxVmUUa/RFZdfMeaaJt3+W3PPBw== dependencies: "@lerna/batch-packages" "^3.1.2" "@lerna/command" "^3.3.0" @@ -56,6 +59,7 @@ "@lerna/changed@^3.4.1": version "3.4.1" resolved "https://registry.yarnpkg.com/@lerna/changed/-/changed-3.4.1.tgz#84a049359a53b8812c3a07a664bd41b1768f5938" + integrity sha512-gT7fhl4zQWyGETDO4Yy5wsFnqNlBSsezncS1nkMW1uO6jwnolwYqcr1KbrMR8HdmsZBn/00Y0mRnbtbpPPey8w== dependencies: "@lerna/collect-updates" "^3.3.2" "@lerna/command" "^3.3.0" @@ -66,6 +70,7 @@ "@lerna/check-working-tree@^3.3.0": version "3.3.0" resolved "https://registry.yarnpkg.com/@lerna/check-working-tree/-/check-working-tree-3.3.0.tgz#2118f301f28ccb530812e5b27a341b1e6b3c84e2" + integrity sha512-oeEP1dNhiiKUaO0pmcIi73YXJpaD0n5JczNctvVNZ8fGZmrALZtEnmC28o6Z7JgQaqq5nd2kO7xbnjoitrC51g== dependencies: "@lerna/describe-ref" "^3.3.0" "@lerna/validation-error" "^3.0.0" @@ -73,6 +78,7 @@ "@lerna/child-process@^3.3.0": version "3.3.0" resolved "https://registry.yarnpkg.com/@lerna/child-process/-/child-process-3.3.0.tgz#71184a763105b6c8ece27f43f166498d90fe680f" + integrity sha512-q2d/OPlNX/cBXB6Iz1932RFzOmOHq6ZzPjqebkINNaTojHWuuRpvJJY4Uz3NGpJ3kEtPDvBemkZqUBTSO5wb1g== dependencies: chalk "^2.3.1" execa "^1.0.0" @@ -81,6 +87,7 @@ "@lerna/clean@^3.3.2": version "3.3.2" resolved "https://registry.yarnpkg.com/@lerna/clean/-/clean-3.3.2.tgz#9a7e8a1e400e580de260fa124945b2939a025069" + integrity sha512-mvqusgSp2ou5SGqQgTEoTvGJpGfH4+L6XSeN+Ims+eNFGXuMazmKCf+rz2PZBMFufaHJ/Os+JF0vPCcWI1Fzqg== dependencies: "@lerna/command" "^3.3.0" "@lerna/filter-options" "^3.3.2" @@ -93,6 +100,7 @@ "@lerna/cli@^3.2.0": version "3.2.0" resolved "https://registry.yarnpkg.com/@lerna/cli/-/cli-3.2.0.tgz#3ed25bcbc0b8f0878bc6a102ee0296f01476cfdf" + integrity sha512-JdbLyTxHqxUlrkI+Ke+ltXbtyA+MPu9zR6kg/n8Fl6uaez/2fZWtReXzYi8MgLxfUFa7+1OHWJv4eAMZlByJ+Q== dependencies: "@lerna/global-options" "^3.1.3" dedent "^0.7.0" @@ -102,6 +110,7 @@ "@lerna/collect-updates@^3.3.2": version "3.3.2" resolved "https://registry.yarnpkg.com/@lerna/collect-updates/-/collect-updates-3.3.2.tgz#54df5ce59ca05e8aa04ff8a9299f89cc253a9304" + integrity sha512-9WyBJI2S5sYgEZEScu525Lbi6nknNrdBKop35sCDIC9y6AIGvH6Dr5tkTd+Kg3n1dE+kHwW/xjERkx3+h7th3w== dependencies: "@lerna/child-process" "^3.3.0" "@lerna/describe-ref" "^3.3.0" @@ -112,6 +121,7 @@ "@lerna/command@^3.3.0": version "3.3.0" resolved "https://registry.yarnpkg.com/@lerna/command/-/command-3.3.0.tgz#e81c4716a676b02dbe9d3f548d5f45b4ba32c25a" + integrity sha512-NTOkLEKlWcBLHSvUr9tzVpV7RJ4GROLeOuZ6RfztGOW/31JPSwVVBD2kPifEXNZunldOx5GVWukR+7+NpAWhsg== dependencies: "@lerna/child-process" "^3.3.0" "@lerna/package-graph" "^3.1.2" @@ -127,6 +137,7 @@ "@lerna/conventional-commits@^3.4.1": version "3.4.1" resolved "https://registry.yarnpkg.com/@lerna/conventional-commits/-/conventional-commits-3.4.1.tgz#0b47f9fc0c4a10951883e949d939188da1b527bc" + integrity sha512-3NETrA58aUkaEW3RdwdJ766Bg9NVpLzb26mtdlsJQcvB5sQBWH5dJSHIVQH1QsGloBeH2pE/mDUEVY8ZJXuR4w== dependencies: "@lerna/validation-error" "^3.0.0" conventional-changelog-angular "^5.0.1" @@ -141,6 +152,7 @@ "@lerna/create-symlink@^3.3.0": version "3.3.0" resolved "https://registry.yarnpkg.com/@lerna/create-symlink/-/create-symlink-3.3.0.tgz#91de00fd576018ba4251f0c6a5b4b7f768f22a82" + integrity sha512-0lb88Nnq1c/GG+fwybuReOnw3+ah4dB81PuWwWwuqUNPE0n50qUf/M/7FfSb5JEh/93fcdbZI0La8t3iysNW1w== dependencies: cmd-shim "^2.0.2" fs-extra "^7.0.0" @@ -149,6 +161,7 @@ "@lerna/create@^3.4.1": version "3.4.1" resolved "https://registry.yarnpkg.com/@lerna/create/-/create-3.4.1.tgz#7cad78a5701d7666a0f5d0fe0e325acd8d8f5b63" + integrity sha512-l+4t2SRO5nvW0MNYY+EWxbaMHsAN8bkWH3nyt7EzhBjs4+TlRAJRIEqd8o9NWznheE3pzwczFz1Qfl3BWbyM5A== dependencies: "@lerna/child-process" "^3.3.0" "@lerna/command" "^3.3.0" @@ -170,6 +183,7 @@ "@lerna/describe-ref@^3.3.0": version "3.3.0" resolved "https://registry.yarnpkg.com/@lerna/describe-ref/-/describe-ref-3.3.0.tgz#d373adb530d5428ab91e303ccbfcf51a98374a3a" + integrity sha512-4t7M4OupnYMSPNLrLUau8qkS+dgLEi4w+DkRkV0+A+KNYga1W0jVgNLPIIsxta7OHfodPkCNAqZCzNCw/dmAwA== dependencies: "@lerna/child-process" "^3.3.0" npmlog "^4.1.2" @@ -177,6 +191,7 @@ "@lerna/diff@^3.3.0": version "3.3.0" resolved "https://registry.yarnpkg.com/@lerna/diff/-/diff-3.3.0.tgz#c8130a5f508b47fad5fec81404498bc3acdf9cb5" + integrity sha512-sIoMjsm3NVxvmt6ofx8Uu/2fxgldQqLl0zmC9X1xW00j831o5hBffx1EoKj9CnmaEvoSP6j/KFjxy2RWjebCIg== dependencies: "@lerna/child-process" "^3.3.0" "@lerna/command" "^3.3.0" @@ -186,6 +201,7 @@ "@lerna/exec@^3.3.2": version "3.3.2" resolved "https://registry.yarnpkg.com/@lerna/exec/-/exec-3.3.2.tgz#95ecaca617fd85abdb91e9a378ed06ec1763d665" + integrity sha512-mN6vGxNir7JOGvWLwKr3DW3LNy1ecCo2ziZj5rO9Mw5Rew3carUu1XLmhF/4judtsvXViUY+rvGIcqHe0vvb+w== dependencies: "@lerna/batch-packages" "^3.1.2" "@lerna/child-process" "^3.3.0" @@ -197,6 +213,7 @@ "@lerna/filter-options@^3.3.2": version "3.3.2" resolved "https://registry.yarnpkg.com/@lerna/filter-options/-/filter-options-3.3.2.tgz#ac90702b7876ff4980dcdeaeac049c433dd01773" + integrity sha512-0WHqdDgAnt5WKoByi1q+lFw8HWt5tEKP2DnLlGqWv3YFwVF5DsPRlO7xbzjY9sJgvyJtZcnkMtccdBPFhGGyIQ== dependencies: "@lerna/collect-updates" "^3.3.2" "@lerna/filter-packages" "^3.0.0" @@ -205,6 +222,7 @@ "@lerna/filter-packages@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@lerna/filter-packages/-/filter-packages-3.0.0.tgz#5eb25ad1610f3e2ab845133d1f8d7d40314e838f" + integrity sha512-zwbY1J4uRjWRZ/FgYbtVkq7I3Nduwsg2V2HwLKSzwV2vPglfGqgovYOVkND6/xqe2BHwDX4IyA2+e7OJmLaLSA== dependencies: "@lerna/validation-error" "^3.0.0" multimatch "^2.1.0" @@ -213,16 +231,19 @@ "@lerna/get-npm-exec-opts@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@lerna/get-npm-exec-opts/-/get-npm-exec-opts-3.0.0.tgz#8fc7866e8d8e9a2f2dc385287ba32eb44de8bdeb" + integrity sha512-arcYUm+4xS8J3Palhl+5rRJXnZnFHsLFKHBxznkPIxjwGQeAEw7df38uHdVjEQ+HNeFmHnBgSqfbxl1VIw5DHg== dependencies: npmlog "^4.1.2" "@lerna/global-options@^3.1.3": version "3.1.3" resolved "https://registry.yarnpkg.com/@lerna/global-options/-/global-options-3.1.3.tgz#cf85e24655a91d04d4efc9a80c1f83fc768d08ae" + integrity sha512-LVeZU/Zgc0XkHdGMRYn+EmHfDmmYNwYRv3ta59iCVFXLVp7FRFWF7oB1ss/WRa9x/pYU0o6L8as/5DomLUGASA== "@lerna/has-npm-version@^3.3.0": version "3.3.0" resolved "https://registry.yarnpkg.com/@lerna/has-npm-version/-/has-npm-version-3.3.0.tgz#8a73c2c437a0e1e68a19ccbd0dd3c014d4d39135" + integrity sha512-GX7omRep1eBRZHgjZLRw3MpBJSdA5gPZFz95P7rxhpvsiG384Tdrr/cKFMhm0A09yq27Tk/nuYTaZIj7HsVE6g== dependencies: "@lerna/child-process" "^3.3.0" semver "^5.5.0" @@ -230,6 +251,7 @@ "@lerna/import@^3.3.1": version "3.3.1" resolved "https://registry.yarnpkg.com/@lerna/import/-/import-3.3.1.tgz#deca8c93c9cc03c5844b975c6da9937dd7530440" + integrity sha512-2OzTQDkYKbBPpyP2iOI1sWfcvMjNLjjHjmREq/uOWJaSIk5J3Ukt71OPpcOHh4V2CBOlXidCcO+Hyb4FVIy8fw== dependencies: "@lerna/child-process" "^3.3.0" "@lerna/command" "^3.3.0" @@ -242,6 +264,7 @@ "@lerna/init@^3.3.0": version "3.3.0" resolved "https://registry.yarnpkg.com/@lerna/init/-/init-3.3.0.tgz#998f3497da3d891867c593b808b6db4b8fc4ccb9" + integrity sha512-HvgRLkIG6nDIeAO6ix5sUVIVV+W9UMk2rSSmFT66CDOefRi7S028amiyYnFUK1QkIAaUbVUyOnYaErtbJwICuw== dependencies: "@lerna/child-process" "^3.3.0" "@lerna/command" "^3.3.0" @@ -252,6 +275,7 @@ "@lerna/link@^3.3.0": version "3.3.0" resolved "https://registry.yarnpkg.com/@lerna/link/-/link-3.3.0.tgz#c0c05ff52d0f0c659fcf221627edfcd58e477a5c" + integrity sha512-8CeXzGL7okrsVXsy2sHXI2KuBaczw3cblAnA2+FJPUqSKMPNbUTRzeU3bOlCjYtK0LbxC4ngENJTL3jJ8RaYQQ== dependencies: "@lerna/command" "^3.3.0" "@lerna/package-graph" "^3.1.2" @@ -262,6 +286,7 @@ "@lerna/list@^3.3.2": version "3.3.2" resolved "https://registry.yarnpkg.com/@lerna/list/-/list-3.3.2.tgz#1412b3cce2a83b1baa4ff6fb962d50b46c28ec98" + integrity sha512-XXEVy7w+i/xx8NeJmGirw4upEoEF9OfD6XPLjISNQc24VgQV+frXdVJ02QcP7Y/PkY1rdIVrOjvo3ipKVLUxaQ== dependencies: "@lerna/command" "^3.3.0" "@lerna/filter-options" "^3.3.2" @@ -271,6 +296,7 @@ "@lerna/listable@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@lerna/listable/-/listable-3.0.0.tgz#27209b1382c87abdbc964220e75c247d803d4199" + integrity sha512-HX/9hyx1HLg2kpiKXIUc1EimlkK1T58aKQ7ovO7rQdTx9ForpefoMzyLnHE1n4XrUtEszcSWJIICJ/F898M6Ag== dependencies: chalk "^2.3.1" columnify "^1.5.4" @@ -278,6 +304,7 @@ "@lerna/log-packed@^3.0.4": version "3.0.4" resolved "https://registry.yarnpkg.com/@lerna/log-packed/-/log-packed-3.0.4.tgz#6d1f6ce5ca68b9971f2a27f0ecf3c50684be174a" + integrity sha512-vVQHgMagE2wnbxhNY9nFkdu+Cx2TsyWalkJfkxbNzmo6gOCrDsxCBDj9vTEV8Q+4aWx0C0Bsc0sB2Eb8y/+ofA== dependencies: byte-size "^4.0.3" columnify "^1.5.4" @@ -287,6 +314,7 @@ "@lerna/npm-conf@^3.4.1": version "3.4.1" resolved "https://registry.yarnpkg.com/@lerna/npm-conf/-/npm-conf-3.4.1.tgz#859e931b0bc9a5eed86309cc09508810c1e7d121" + integrity sha512-i9G6DnbCqiAqxKx2rSXej/n14qxlV/XOebL6QZonxJKzNTB+Q2wglnhTXmfZXTPJfoqimLaY4NfAEtbOXRWOXQ== dependencies: config-chain "^1.1.11" pify "^3.0.0" @@ -294,6 +322,7 @@ "@lerna/npm-dist-tag@^3.3.0": version "3.3.0" resolved "https://registry.yarnpkg.com/@lerna/npm-dist-tag/-/npm-dist-tag-3.3.0.tgz#e1c5ab67674216d901266a16846b21cc81ff6afd" + integrity sha512-EtZJXzh3w5tqXEev+EBBPrWKWWn0WgJfxm4FihfS9VgyaAW8udIVZHGkIQ3f+tBtupcAzA9Q8cQNUkGF2efwmA== dependencies: "@lerna/child-process" "^3.3.0" "@lerna/get-npm-exec-opts" "^3.0.0" @@ -302,6 +331,7 @@ "@lerna/npm-install@^3.3.0": version "3.3.0" resolved "https://registry.yarnpkg.com/@lerna/npm-install/-/npm-install-3.3.0.tgz#16d00ffd668d11b2386b3ac68bdac2cf8320e533" + integrity sha512-WoVvKdS8ltROTGSNQwo6NDq0YKnjwhvTG4li1okcN/eHKOS3tL9bxbgPx7No0wOq5DKBpdeS9KhAfee6LFAZ5g== dependencies: "@lerna/child-process" "^3.3.0" "@lerna/get-npm-exec-opts" "^3.0.0" @@ -314,6 +344,7 @@ "@lerna/npm-publish@^3.3.1": version "3.3.1" resolved "https://registry.yarnpkg.com/@lerna/npm-publish/-/npm-publish-3.3.1.tgz#30384665d7ee387343332ece62ca231207bbabea" + integrity sha512-bVTlWIcBL6Zpyzqvr9C7rxXYcoPw+l7IPz5eqQDNREj1R39Wj18OWB2KTJq8l7LIX7Wf4C2A1uT5hJaEf9BuvA== dependencies: "@lerna/child-process" "^3.3.0" "@lerna/get-npm-exec-opts" "^3.0.0" @@ -326,6 +357,7 @@ "@lerna/npm-run-script@^3.3.0": version "3.3.0" resolved "https://registry.yarnpkg.com/@lerna/npm-run-script/-/npm-run-script-3.3.0.tgz#3c79601c27c67121155b20e039be53130217db72" + integrity sha512-YqDguWZzp4jIomaE4aWMUP7MIAJAFvRAf6ziQLpqwoQskfWLqK5mW0CcszT1oLjhfb3cY3MMfSTFaqwbdKmICg== dependencies: "@lerna/child-process" "^3.3.0" "@lerna/get-npm-exec-opts" "^3.0.0" @@ -334,12 +366,14 @@ "@lerna/output@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@lerna/output/-/output-3.0.0.tgz#4ed4a30ed2f311046b714b3840a090990ba3ce35" + integrity sha512-EFxnSbO0zDEVKkTKpoCUAFcZjc3gn3DwPlyTDxbeqPU7neCfxP4rA4+0a6pcOfTlRS5kLBRMx79F2TRCaMM3DA== dependencies: npmlog "^4.1.2" "@lerna/package-graph@^3.1.2": version "3.1.2" resolved "https://registry.yarnpkg.com/@lerna/package-graph/-/package-graph-3.1.2.tgz#b70298a3a8c82e12090da33233bf242223a38f20" + integrity sha512-9wIWb49I1IJmyjPdEVZQ13IAi9biGfH/OZHOC04U2zXGA0GLiY+B3CAx6FQvqkZ8xEGfqzmXnv3LvZ0bQfc1aQ== dependencies: "@lerna/validation-error" "^3.0.0" npm-package-arg "^6.0.0" @@ -348,6 +382,7 @@ "@lerna/package@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@lerna/package/-/package-3.0.0.tgz#14afc9a6cb1f7f7b23c1d7c7aa81bdac7d44c0e5" + integrity sha512-djzEJxzn212wS8d9znBnlXkeRlPL7GqeAYBykAmsuq51YGvaQK67Umh5ejdO0uxexF/4r7yRwgrlRHpQs8Rfqg== dependencies: npm-package-arg "^6.0.0" write-pkg "^3.1.0" @@ -355,6 +390,7 @@ "@lerna/project@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@lerna/project/-/project-3.0.0.tgz#4320d2a2b4080cabcf95161d9c48475217d8a545" + integrity sha512-XhDFVfqj79jG2Speggd15RpYaE8uiR25UKcQBDmumbmqvTS7xf2cvl2pq2UTvDafaJ0YwFF3xkxQZeZnFMwdkw== dependencies: "@lerna/package" "^3.0.0" "@lerna/validation-error" "^3.0.0" @@ -372,13 +408,15 @@ "@lerna/prompt@^3.3.1": version "3.3.1" resolved "https://registry.yarnpkg.com/@lerna/prompt/-/prompt-3.3.1.tgz#ec53f9034a7a02a671627241682947f65078ab88" + integrity sha512-eJhofrUCUaItMIH6et8kI7YqHfhjWqGZoTsE+40NRCfAraOMWx+pDzfRfeoAl3qeRAH2HhNj1bkYn70FbUOxuQ== dependencies: inquirer "^6.2.0" npmlog "^4.1.2" -"@lerna/publish@^3.4.1": - version "3.4.1" - resolved "https://registry.yarnpkg.com/@lerna/publish/-/publish-3.4.1.tgz#abbbc656b3bfafc2289399a46da060b90f6baf32" +"@lerna/publish@^3.4.3": + version "3.4.3" + resolved "https://registry.yarnpkg.com/@lerna/publish/-/publish-3.4.3.tgz#fb956ca2a871729982022889f90d0e8eb8528340" + integrity sha512-baeRL8xmOR25p86cAaS9mL0jdRzdv4dUo04PlK2Wes+YlL705F55cSXeC9npNie+9rGwFyLzCTQe18WdbZyLuw== dependencies: "@lerna/batch-packages" "^3.1.2" "@lerna/check-working-tree" "^3.3.0" @@ -410,6 +448,7 @@ "@lerna/resolve-symlink@^3.3.0": version "3.3.0" resolved "https://registry.yarnpkg.com/@lerna/resolve-symlink/-/resolve-symlink-3.3.0.tgz#c5d99a60cb17e2ea90b3521a0ba445478d194a44" + integrity sha512-KmoPDcFJ2aOK2inYHbrsiO9SodedUj0L1JDvDgirVNIjMUaQe2Q6Vi4Gh+VCJcyB27JtfHioV9R2NxU72Pk2hg== dependencies: fs-extra "^7.0.0" npmlog "^4.1.2" @@ -418,6 +457,7 @@ "@lerna/rimraf-dir@^3.3.0": version "3.3.0" resolved "https://registry.yarnpkg.com/@lerna/rimraf-dir/-/rimraf-dir-3.3.0.tgz#687e9bb3668a9e540e281302a52d9a573860f5db" + integrity sha512-vSqOcZ4kZduiSprbt+y40qziyN3VKYh+ygiCdnbBbsaxpdKB6CfrSMUtrLhVFrqUfBHIZRzHIzgjTdtQex1KLw== dependencies: "@lerna/child-process" "^3.3.0" npmlog "^4.1.2" @@ -427,6 +467,7 @@ "@lerna/run-lifecycle@^3.4.1": version "3.4.1" resolved "https://registry.yarnpkg.com/@lerna/run-lifecycle/-/run-lifecycle-3.4.1.tgz#6d7e44eada31cb4ec78b18ef050da0d86f6c892b" + integrity sha512-N/hi2srM9A4BWEkXccP7vCEbf4MmIuALF00DTBMvc0A/ccItwUpl3XNuM7+ADDRK0mkwE3hDw89lJ3A7f8oUQw== dependencies: "@lerna/npm-conf" "^3.4.1" npm-lifecycle "^2.0.0" @@ -435,6 +476,7 @@ "@lerna/run-parallel-batches@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@lerna/run-parallel-batches/-/run-parallel-batches-3.0.0.tgz#468704934084c74991d3124d80607857d4dfa840" + integrity sha512-Mj1ravlXF7AkkewKd9YFq9BtVrsStNrvVLedD/b2wIVbNqcxp8lS68vehXVOzoL/VWNEDotvqCQtyDBilCodGw== dependencies: p-map "^1.2.0" p-map-series "^1.0.0" @@ -442,6 +484,7 @@ "@lerna/run@^3.3.2": version "3.3.2" resolved "https://registry.yarnpkg.com/@lerna/run/-/run-3.3.2.tgz#f521f4a22585c90758f34a584cb1871f8bb2a83e" + integrity sha512-cruwRGZZWnQ5I0M+AqcoT3Xpq2wj3135iVw4n59/Op6dZu50sMFXZNLiTTTZ15k8rTKjydcccJMdPSpTHbH7/A== dependencies: "@lerna/batch-packages" "^3.1.2" "@lerna/command" "^3.3.0" @@ -455,6 +498,7 @@ "@lerna/symlink-binary@^3.3.0": version "3.3.0" resolved "https://registry.yarnpkg.com/@lerna/symlink-binary/-/symlink-binary-3.3.0.tgz#99ea570b21baabd61ecab27582eeb1d7b2c5f9cf" + integrity sha512-zRo6CimhvH/VJqCFl9T4IC6syjpWyQIxEfO2sBhrapEcfwjtwbhoGgKwucsvt4rIpFazCw63jQ/AXMT27KUIHg== dependencies: "@lerna/create-symlink" "^3.3.0" "@lerna/package" "^3.0.0" @@ -465,6 +509,7 @@ "@lerna/symlink-dependencies@^3.3.0": version "3.3.0" resolved "https://registry.yarnpkg.com/@lerna/symlink-dependencies/-/symlink-dependencies-3.3.0.tgz#13bcaed3e37986ab01b13498a459c7f609397dc3" + integrity sha512-IRngSNCmuD5uBKVv23tHMvr7Mplti0lKHilFKcvhbvhAfu6m/Vclxhkfs/uLyHzG+DeRpl/9o86SQET3h4XDhg== dependencies: "@lerna/create-symlink" "^3.3.0" "@lerna/resolve-symlink" "^3.3.0" @@ -477,12 +522,14 @@ "@lerna/validation-error@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@lerna/validation-error/-/validation-error-3.0.0.tgz#a27e90051c3ba71995e2a800a43d94ad04b3e3f4" + integrity sha512-5wjkd2PszV0kWvH+EOKZJWlHEqCTTKrWsvfHnHhcUaKBe/NagPZFWs+0xlsDPZ3DJt5FNfbAPAnEBQ05zLirFA== dependencies: npmlog "^4.1.2" "@lerna/version@^3.4.1": version "3.4.1" resolved "https://registry.yarnpkg.com/@lerna/version/-/version-3.4.1.tgz#029448cccd3ccefb4d5f666933bd13cfb37edab0" + integrity sha512-oefNaQLBJSI2WLZXw5XxDXk4NyF5/ct0V9ys/J308NpgZthPgwRPjk9ZR0o1IOxW1ABi6z3E317W/dxHDjvAkg== dependencies: "@lerna/batch-packages" "^3.1.2" "@lerna/check-working-tree" "^3.3.0" @@ -509,6 +556,7 @@ "@lerna/write-log-file@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@lerna/write-log-file/-/write-log-file-3.0.0.tgz#2f95fee80c6821fe1ee6ccf8173d2b4079debbd2" + integrity sha512-SfbPp29lMeEVOb/M16lJwn4nnx5y+TwCdd7Uom9umd7KcZP0NOvpnX0PHehdonl7TyHZ1Xx2maklYuCLbQrd/A== dependencies: npmlog "^4.1.2" write-file-atomic "^2.3.0" @@ -516,17 +564,20 @@ "@mrmlnc/readdir-enhanced@^2.2.1": version "2.2.1" resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" + integrity sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g== dependencies: call-me-maybe "^1.0.1" glob-to-regexp "^0.3.0" "@nodelib/fs.stat@^1.0.1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.2.tgz#54c5a964462be3d4d78af631363c18d6fa91ac26" + version "1.1.3" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" + integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== JSONStream@^1.0.4, JSONStream@^1.3.4: - version "1.3.4" - resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.4.tgz#615bb2adb0cd34c8f4c447b5f6512fa1d8f16a2e" + version "1.3.5" + resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" + integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== dependencies: jsonparse "^1.2.0" through ">=2.2.7 <3" @@ -534,22 +585,26 @@ JSONStream@^1.0.4, JSONStream@^1.3.4: abbrev@1: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== agent-base@4, agent-base@^4.1.0, agent-base@~4.2.0: version "4.2.1" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9" + integrity sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg== dependencies: es6-promisify "^5.0.0" agentkeepalive@^3.4.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-3.5.1.tgz#4eba75cf2ad258fc09efd506cdb8d8c2971d35a4" + version "3.5.2" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-3.5.2.tgz#a113924dd3fa24a0bc3b78108c450c2abee00f67" + integrity sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ== dependencies: humanize-ms "^1.2.1" ajv@^5.3.0: version "5.5.2" resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" + integrity sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU= dependencies: co "^4.6.0" fast-deep-equal "^1.0.0" @@ -559,32 +614,39 @@ ajv@^5.3.0: ansi-escapes@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.1.0.tgz#f73207bb81207d75fd6c83f125af26eea378ca30" + integrity sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw== ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= ansi-regex@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== dependencies: color-convert "^1.9.0" aproba@^1.0.3, aproba@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== aproba@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" + integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== are-we-there-yet@~1.1.2: version "1.1.5" resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" + integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== dependencies: delegates "^1.0.0" readable-stream "^2.0.6" @@ -592,98 +654,120 @@ are-we-there-yet@~1.1.2: argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== dependencies: sprintf-js "~1.0.2" arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= arr-flatten@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== arr-union@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= array-differ@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-1.0.0.tgz#eff52e3758249d33be402b8bb8e564bb2b5d4031" + integrity sha1-7/UuN1gknTO+QCuLuOVkuytdQDE= array-find-index@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" + integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E= array-ify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" + integrity sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4= array-union@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" + integrity sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk= dependencies: array-uniq "^1.0.1" array-uniq@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= array-unique@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= arrify@^1.0.0, arrify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= asap@^2.0.0: version "2.0.6" resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= asn1@~0.2.3: version "0.2.4" resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== dependencies: safer-buffer "~2.1.0" assert-plus@1.0.0, assert-plus@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= assign-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= async@^2.5.0: version "2.6.1" resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610" + integrity sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ== dependencies: lodash "^4.17.10" asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= atob@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= aws4@^1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" + integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ== balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= base@^0.11.1: version "0.11.2" resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== dependencies: cache-base "^1.0.1" class-utils "^0.3.5" @@ -696,22 +780,26 @@ base@^0.11.1: bcrypt-pbkdf@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= dependencies: tweetnacl "^0.14.3" block-stream@*: version "0.0.9" resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" + integrity sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo= dependencies: inherits "~2.0.0" -bluebird@^3.5.1: - version "3.5.2" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.2.tgz#1be0908e054a751754549c270489c1505d4ab15a" +bluebird@^3.5.1, bluebird@^3.5.2: + version "3.5.3" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.3.tgz#7d01c6f9616c9a51ab0f8c549a79dfe6ec33efa7" + integrity sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw== brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== dependencies: balanced-match "^1.0.0" concat-map "0.0.1" @@ -719,6 +807,7 @@ brace-expansion@^1.1.7: braces@^2.3.1: version "2.3.2" resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== dependencies: arr-flatten "^1.1.0" array-unique "^0.3.2" @@ -734,26 +823,32 @@ braces@^2.3.1: buffer-from@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== builtin-modules@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" + integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= builtins@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" + integrity sha1-y5T662HIaWRR2zZTThQi+U8K7og= byline@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" + integrity sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE= byte-size@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/byte-size/-/byte-size-4.0.3.tgz#b7c095efc68eadf82985fccd9a2df43a74fa2ccd" + version "4.0.4" + resolved "https://registry.yarnpkg.com/byte-size/-/byte-size-4.0.4.tgz#29d381709f41aae0d89c631f1c81aec88cd40b23" + integrity sha512-82RPeneC6nqCdSwCX2hZUz3JPOvN5at/nTEw/CMf05Smu3Hrpo9Psb7LjN+k+XndNArG1EY8L4+BM3aTM4BCvw== -cacache@^11.0.1, cacache@^11.0.2: - version "11.2.0" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-11.2.0.tgz#617bdc0b02844af56310e411c0878941d5739965" +cacache@^11.0.1, cacache@^11.2.0: + version "11.3.1" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-11.3.1.tgz#d09d25f6c4aca7a6d305d141ae332613aa1d515f" + integrity sha512-2PEw4cRRDu+iQvBTTuttQifacYjLPhET+SYO/gEFMy8uhi+jlJREDAjSF5FWSdV/Aw5h18caHA7vMTw2c+wDzA== dependencies: bluebird "^3.5.1" chownr "^1.0.1" @@ -773,6 +868,7 @@ cacache@^11.0.1, cacache@^11.0.2: cache-base@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== dependencies: collection-visit "^1.0.0" component-emitter "^1.2.1" @@ -787,10 +883,31 @@ cache-base@^1.0.1: call-me-maybe@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b" + integrity sha1-JtII6onje1y95gJQoV8DHBak1ms= + +caller-callsite@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" + integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ= + dependencies: + callsites "^2.0.0" + +caller-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" + integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ= + dependencies: + caller-callsite "^2.0.0" + +callsites@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" + integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA= camelcase-keys@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" + integrity sha1-MIvur/3ygRkFHvodkyITyRuPkuc= dependencies: camelcase "^2.0.0" map-obj "^1.0.0" @@ -798,6 +915,7 @@ camelcase-keys@^2.0.0: camelcase-keys@^4.0.0: version "4.2.0" resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-4.2.0.tgz#a2aa5fb1af688758259c32c141426d78923b9b77" + integrity sha1-oqpfsa9oh1glnDLBQUJteJI7m3c= dependencies: camelcase "^4.1.0" map-obj "^2.0.0" @@ -806,18 +924,22 @@ camelcase-keys@^4.0.0: camelcase@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" + integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= camelcase@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" + integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= chalk@^2.0.0, chalk@^2.3.1: version "2.4.1" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" + integrity sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ== dependencies: ansi-styles "^3.2.1" escape-string-regexp "^1.0.5" @@ -826,18 +948,22 @@ chalk@^2.0.0, chalk@^2.3.1: chardet@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== -chownr@^1.0.1: +chownr@^1.0.1, chownr@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.1.tgz#54726b8b8fff4df053c42187e801fb4412df1494" + integrity sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g== ci-info@^1.5.0: version "1.6.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497" + integrity sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A== class-utils@^0.3.5: version "0.3.6" resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== dependencies: arr-union "^3.1.0" define-property "^0.2.5" @@ -847,16 +973,19 @@ class-utils@^0.3.5: cli-cursor@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= dependencies: restore-cursor "^2.0.0" cli-width@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" + integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= cliui@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49" + integrity sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ== dependencies: string-width "^2.1.1" strip-ansi "^4.0.0" @@ -865,10 +994,12 @@ cliui@^4.0.0: clone@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= cmd-shim@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-2.0.2.tgz#6fcbda99483a8fd15d7d30a196ca69d688a2efdb" + integrity sha1-b8vamUg6j9FdfTChlspp1oii79s= dependencies: graceful-fs "^4.1.2" mkdirp "~0.5.0" @@ -876,14 +1007,17 @@ cmd-shim@^2.0.2: co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= code-point-at@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= collection-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= dependencies: map-visit "^1.0.0" object-visit "^1.0.0" @@ -891,39 +1025,39 @@ collection-visit@^1.0.0: color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== dependencies: color-name "1.1.3" color-name@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= columnify@^1.5.4: version "1.5.4" resolved "https://registry.yarnpkg.com/columnify/-/columnify-1.5.4.tgz#4737ddf1c7b69a8a7c340570782e947eec8e78bb" + integrity sha1-Rzfd8ce2mop8NAVweC6UfuyOeLs= dependencies: strip-ansi "^3.0.0" wcwidth "^1.0.0" -combined-stream@1.0.6: - version "1.0.6" - resolved "http://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818" - dependencies: - delayed-stream "~1.0.0" - -combined-stream@~1.0.6: +combined-stream@^1.0.6, combined-stream@~1.0.6: version "1.0.7" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.7.tgz#2d1d24317afb8abe95d6d2c0b07b57813539d828" + integrity sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w== dependencies: delayed-stream "~1.0.0" commander@~2.17.1: version "2.17.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" + integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg== compare-func@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/compare-func/-/compare-func-1.3.2.tgz#99dd0ba457e1f9bc722b12c08ec33eeab31fa648" + integrity sha1-md0LpFfh+bxyKxLAjsM+6rMfpkg= dependencies: array-ify "^1.0.0" dot-prop "^3.0.0" @@ -931,14 +1065,17 @@ compare-func@^1.3.1: component-emitter@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" + integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY= concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= concat-stream@^1.5.0, concat-stream@^1.6.0: version "1.6.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== dependencies: buffer-from "^1.0.0" inherits "^2.0.3" @@ -948,6 +1085,7 @@ concat-stream@^1.5.0, concat-stream@^1.6.0: config-chain@^1.1.11: version "1.1.12" resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa" + integrity sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA== dependencies: ini "^1.3.4" proto-list "~1.2.1" @@ -955,42 +1093,47 @@ config-chain@^1.1.11: console-control-strings@^1.0.0, console-control-strings@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= conventional-changelog-angular@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.1.tgz#f96431b76de453333a909decd02b15cb5bd2d364" + version "5.0.2" + resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.2.tgz#39d945635e03b6d0c9d4078b1df74e06163dc66a" + integrity sha512-yx7m7lVrXmt4nKWQgWZqxSALEiAKZhOAcbxdUaU9575mB0CzXVbgrgpfSnSP7OqWDUTYGD0YVJ0MSRdyOPgAwA== dependencies: compare-func "^1.3.1" q "^1.5.1" conventional-changelog-core@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/conventional-changelog-core/-/conventional-changelog-core-3.1.0.tgz#96a81bb3301b4b2a3dc2851cc54c5fb674ac1942" + version "3.1.5" + resolved "https://registry.yarnpkg.com/conventional-changelog-core/-/conventional-changelog-core-3.1.5.tgz#c2edf928539308b54fe1b90a2fc731abc021852c" + integrity sha512-iwqAotS4zk0wA4S84YY1JCUG7X3LxaRjJxuUo6GI4dZuIy243j5nOg/Ora35ExT4DOiw5dQbMMQvw2SUjh6moQ== dependencies: - conventional-changelog-writer "^4.0.0" - conventional-commits-parser "^3.0.0" + conventional-changelog-writer "^4.0.2" + conventional-commits-parser "^3.0.1" dateformat "^3.0.0" get-pkg-repo "^1.0.0" - git-raw-commits "^2.0.0" + git-raw-commits "2.0.0" git-remote-origin-url "^2.0.0" - git-semver-tags "^2.0.0" + git-semver-tags "^2.0.2" lodash "^4.2.1" normalize-package-data "^2.3.5" q "^1.5.1" - read-pkg "^1.1.0" - read-pkg-up "^1.0.1" + read-pkg "^3.0.0" + read-pkg-up "^3.0.0" through2 "^2.0.0" -conventional-changelog-preset-loader@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.0.1.tgz#d134734e0cc1b91b88b30586c5991f31442029f1" +conventional-changelog-preset-loader@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.0.2.tgz#81d1a07523913f3d17da3a49f0091f967ad345b0" + integrity sha512-pBY+qnUoJPXAXXqVGwQaVmcye05xi6z231QM98wHWamGAmu/ghkBprQAwmF5bdmyobdVxiLhPY3PrCfSeUNzRQ== -conventional-changelog-writer@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/conventional-changelog-writer/-/conventional-changelog-writer-4.0.0.tgz#3ed983c8ef6a3aa51fe44e82c9c75e86f1b5aa42" +conventional-changelog-writer@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/conventional-changelog-writer/-/conventional-changelog-writer-4.0.2.tgz#eb493ed84269e7a663da36e49af51c54639c9a67" + integrity sha512-d8/FQY/fix2xXEBUhOo8u3DCbyEw3UOQgYHxLsPDw+wHUDma/GQGAGsGtoH876WyNs32fViHmTOUrgRKVLvBug== dependencies: compare-func "^1.3.1" - conventional-commits-filter "^2.0.0" + conventional-commits-filter "^2.0.1" dateformat "^3.0.0" handlebars "^4.0.2" json-stringify-safe "^5.0.1" @@ -1000,16 +1143,18 @@ conventional-changelog-writer@^4.0.0: split "^1.0.0" through2 "^2.0.0" -conventional-commits-filter@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/conventional-commits-filter/-/conventional-commits-filter-2.0.0.tgz#a0ce1d1ff7a1dd7fab36bee8e8256d348d135651" +conventional-commits-filter@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/conventional-commits-filter/-/conventional-commits-filter-2.0.1.tgz#55a135de1802f6510b6758e0a6aa9e0b28618db3" + integrity sha512-92OU8pz/977udhBjgPEbg3sbYzIxMDFTlQT97w7KdhR9igNqdJvy8smmedAAgn4tPiqseFloKkrVfbXCVd+E7A== dependencies: is-subset "^0.1.1" modify-values "^1.0.0" -conventional-commits-parser@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/conventional-commits-parser/-/conventional-commits-parser-3.0.0.tgz#7f604549a50bd8f60443fbe515484b1c2f06a5c4" +conventional-commits-parser@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/conventional-commits-parser/-/conventional-commits-parser-3.0.1.tgz#fe1c49753df3f98edb2285a5e485e11ffa7f2e4c" + integrity sha512-P6U5UOvDeidUJ8ebHVDIoXzI7gMlQ1OF/id6oUvp8cnZvOXMt1n8nYl74Ey9YMn0uVQtxmCtjPQawpsssBWtGg== dependencies: JSONStream "^1.0.4" is-text-path "^1.0.0" @@ -1020,21 +1165,23 @@ conventional-commits-parser@^3.0.0: trim-off-newlines "^1.0.0" conventional-recommended-bump@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/conventional-recommended-bump/-/conventional-recommended-bump-4.0.1.tgz#304a45a412cfec050a10ea2e7e4a89320eaf3991" + version "4.0.4" + resolved "https://registry.yarnpkg.com/conventional-recommended-bump/-/conventional-recommended-bump-4.0.4.tgz#05540584641d3da758c8863c09788fcaeb586872" + integrity sha512-9mY5Yoblq+ZMqJpBzgS+RpSq+SUfP2miOR3H/NR9drGf08WCrY9B6HAGJZEm6+ThsVP917VHAahSOjM6k1vhPg== dependencies: concat-stream "^1.6.0" - conventional-changelog-preset-loader "^2.0.1" - conventional-commits-filter "^2.0.0" - conventional-commits-parser "^3.0.0" - git-raw-commits "^2.0.0" - git-semver-tags "^2.0.0" + conventional-changelog-preset-loader "^2.0.2" + conventional-commits-filter "^2.0.1" + conventional-commits-parser "^3.0.1" + git-raw-commits "2.0.0" + git-semver-tags "^2.0.2" meow "^4.0.0" q "^1.5.1" copy-concurrently@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" + integrity sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A== dependencies: aproba "^1.1.1" fs-write-stream-atomic "^1.0.8" @@ -1046,15 +1193,19 @@ copy-concurrently@^1.0.0: copy-descriptor@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= cosmiconfig@^5.0.2: - version "5.0.6" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.0.6.tgz#dca6cf680a0bd03589aff684700858c81abeeb39" + version "5.0.7" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.0.7.tgz#39826b292ee0d78eda137dfa3173bd1c21a43b04" + integrity sha512-PcLqxTKiDmNT6pSpy4N6KtuPwb53W+2tzNvwOZw0WH9N6O0vLIBq0x8aj8Oj75ere4YcGi48bDFCL+3fRJdlNA== dependencies: + import-fresh "^2.0.0" is-directory "^0.3.1" js-yaml "^3.9.0" parse-json "^4.0.0" @@ -1062,6 +1213,7 @@ cosmiconfig@^5.0.2: cross-spawn@^6.0.0: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== dependencies: nice-try "^1.0.4" path-key "^2.0.1" @@ -1072,54 +1224,64 @@ cross-spawn@^6.0.0: currently-unhandled@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" + integrity sha1-mI3zP+qxke95mmE2nddsF635V+o= dependencies: array-find-index "^1.0.1" cyclist@~0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640" + integrity sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA= dargs@^4.0.1: version "4.1.0" resolved "https://registry.yarnpkg.com/dargs/-/dargs-4.1.0.tgz#03a9dbb4b5c2f139bf14ae53f0b8a2a6a86f4e17" + integrity sha1-A6nbtLXC8Tm/FK5T8LiipqhvThc= dependencies: number-is-nan "^1.0.0" dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= dependencies: assert-plus "^1.0.0" dateformat@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" + integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== debug@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== dependencies: ms "2.0.0" debug@^2.2.0, debug@^2.3.3: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" debug@^3.1.0: - version "3.2.5" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.5.tgz#c2418fbfd7a29f4d4f70ff4cea604d4b64c46407" + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== dependencies: ms "^2.1.1" debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" + integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI= decamelize-keys@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9" + integrity sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk= dependencies: decamelize "^1.1.0" map-obj "^1.0.0" @@ -1127,42 +1289,50 @@ decamelize-keys@^1.0.0: decamelize@^1.1.0, decamelize@^1.1.2: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= decamelize@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-2.0.0.tgz#656d7bbc8094c4c788ea53c5840908c9c7d063c7" + integrity sha512-Ikpp5scV3MSYxY39ymh45ZLEecsTdv/Xj2CaQfI8RLMuwi7XvjX9H/fhraiSuU+C5w5NTDu4ZU72xNiZnurBPg== dependencies: xregexp "4.0.0" decode-uri-component@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= dedent@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" + integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= defaults@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" + integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730= dependencies: clone "^1.0.2" define-property@^0.2.5: version "0.2.5" resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= dependencies: is-descriptor "^0.1.0" define-property@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= dependencies: is-descriptor "^1.0.0" define-property@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== dependencies: is-descriptor "^1.0.2" isobject "^3.0.1" @@ -1170,18 +1340,22 @@ define-property@^2.0.2: delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= delegates@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= detect-indent@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" + integrity sha1-OHHMCmoALow+Wzz38zYmRnXwa50= dezalgo@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456" + integrity sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY= dependencies: asap "^2.0.0" wrappy "1" @@ -1189,6 +1363,7 @@ dezalgo@^1.0.0: dir-glob@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.0.0.tgz#0b205d2b6aef98238ca286598a8204d29d0a0034" + integrity sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag== dependencies: arrify "^1.0.1" path-type "^3.0.0" @@ -1196,22 +1371,26 @@ dir-glob@^2.0.0: dot-prop@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-3.0.0.tgz#1b708af094a49c9a0e7dbcad790aba539dac1177" + integrity sha1-G3CK8JSknJoOfbyteQq6U52sEXc= dependencies: is-obj "^1.0.0" dot-prop@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57" + integrity sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ== dependencies: is-obj "^1.0.0" duplexer@^0.1.1: version "0.1.1" - resolved "http://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" + integrity sha1-rOb/gIwc5mtX0ev5eXessCM0z8E= duplexify@^3.4.2, duplexify@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.6.0.tgz#592903f5d80b38d037220541264d69a198fb3410" + version "3.6.1" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.6.1.tgz#b1a7a29c4abfd639585efaecce80d666b1e34125" + integrity sha512-vM58DwdnKmty+FSPzT14K9JXb90H+j5emaR4KYbr2KTIz00WHGbWOe5ghQTx233ZCLZtrGDALzKwcjEtSt35mA== dependencies: end-of-stream "^1.0.0" inherits "^2.0.1" @@ -1221,6 +1400,7 @@ duplexify@^3.4.2, duplexify@^3.6.0: ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= dependencies: jsbn "~0.1.0" safer-buffer "^2.1.0" @@ -1228,46 +1408,55 @@ ecc-jsbn@~0.1.1: encoding@^0.1.11: version "0.1.12" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" + integrity sha1-U4tm8+5izRq1HsMjgp0flIDHS+s= dependencies: iconv-lite "~0.4.13" end-of-stream@^1.0.0, end-of-stream@^1.1.0: version "1.4.1" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" + integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q== dependencies: once "^1.4.0" err-code@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/err-code/-/err-code-1.1.2.tgz#06e0116d3028f6aef4806849eb0ea6a748ae6960" + integrity sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA= error-ex@^1.2.0, error-ex@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== dependencies: is-arrayish "^0.2.1" es6-promise@^4.0.3: version "4.2.5" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.5.tgz#da6d0d5692efb461e082c14817fe2427d8f5d054" + integrity sha512-n6wvpdE43VFtJq+lUDYDBFUwV8TZbuGXLV4D6wKafg13ldznKsyEvatubnmUe31zcvelSzOHF+XbaT+Bl9ObDg== es6-promisify@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" + integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM= dependencies: es6-promise "^4.0.3" escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= esprima@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== execa@^0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/execa/-/execa-0.10.0.tgz#ff456a8f53f90f8eccc71a96d11bdfc7f082cb50" + integrity sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw== dependencies: cross-spawn "^6.0.0" get-stream "^3.0.0" @@ -1280,6 +1469,7 @@ execa@^0.10.0: execa@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" + integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== dependencies: cross-spawn "^6.0.0" get-stream "^4.0.0" @@ -1292,6 +1482,7 @@ execa@^1.0.0: expand-brackets@^2.1.4: version "2.1.4" resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= dependencies: debug "^2.3.3" define-property "^0.2.5" @@ -1304,12 +1495,14 @@ expand-brackets@^2.1.4: extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= dependencies: is-extendable "^0.1.0" extend-shallow@^3.0.0, extend-shallow@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= dependencies: assign-symbols "^1.0.0" is-extendable "^1.0.1" @@ -1317,10 +1510,12 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: extend@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== external-editor@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.0.3.tgz#5866db29a97826dbe4bf3afd24070ead9ea43a27" + integrity sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA== dependencies: chardet "^0.7.0" iconv-lite "^0.4.24" @@ -1329,6 +1524,7 @@ external-editor@^3.0.0: extglob@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== dependencies: array-unique "^0.3.2" define-property "^1.0.0" @@ -1342,18 +1538,22 @@ extglob@^2.0.4: extsprintf@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= extsprintf@^1.2.0: version "1.4.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= fast-deep-equal@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614" + integrity sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ= fast-glob@^2.0.2: version "2.2.3" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.3.tgz#d09d378e9ef6b0076a0fa1ba7519d9d4d9699c28" + integrity sha512-NiX+JXjnx43RzvVFwRWfPKo4U+1BrK5pJPsHQdKMlLoFHrrGktXglQhHliSihWAq+m1z6fHk3uwGHrtRbS9vLA== dependencies: "@mrmlnc/readdir-enhanced" "^2.2.1" "@nodelib/fs.stat" "^1.0.1" @@ -1365,20 +1565,24 @@ fast-glob@^2.0.2: fast-json-stable-stringify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" + integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= -figgy-pudding@^3.1.0, figgy-pudding@^3.2.1, figgy-pudding@^3.4.1, figgy-pudding@^3.5.1: +figgy-pudding@^3.1.0, figgy-pudding@^3.4.1, figgy-pudding@^3.5.1: version "3.5.1" resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790" + integrity sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w== figures@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" + integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI= dependencies: escape-string-regexp "^1.0.5" fill-range@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= dependencies: extend-shallow "^2.0.1" is-number "^3.0.0" @@ -1388,6 +1592,7 @@ fill-range@^4.0.0: find-up@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= dependencies: path-exists "^2.0.0" pinkie-promise "^2.0.0" @@ -1395,18 +1600,21 @@ find-up@^1.0.0: find-up@^2.0.0, find-up@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= dependencies: locate-path "^2.0.0" find-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== dependencies: locate-path "^3.0.0" flush-write-stream@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.0.3.tgz#c5d586ef38af6097650b49bc41b55fabb19f35bd" + integrity sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw== dependencies: inherits "^2.0.1" readable-stream "^2.0.4" @@ -1414,35 +1622,41 @@ flush-write-stream@^1.0.0: for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= form-data@~2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.2.tgz#4970498be604c20c005d4f5c23aecd21d6b49099" + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== dependencies: asynckit "^0.4.0" - combined-stream "1.0.6" + combined-stream "^1.0.6" mime-types "^2.1.12" fragment-cache@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= dependencies: map-cache "^0.2.2" from2@^2.1.0: version "2.3.0" resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" + integrity sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8= dependencies: inherits "^2.0.1" readable-stream "^2.0.0" fs-extra@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.0.tgz#8cc3f47ce07ef7b3593a11b9fb245f7e34c041d6" + version "7.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" + integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== dependencies: graceful-fs "^4.1.2" jsonfile "^4.0.0" @@ -1451,12 +1665,14 @@ fs-extra@^7.0.0: fs-minipass@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d" + integrity sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ== dependencies: minipass "^2.2.1" fs-write-stream-atomic@^1.0.8: version "1.0.10" resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" + integrity sha1-tH31NJPvkR33VzHnCp3tAYnbQMk= dependencies: graceful-fs "^4.1.2" iferr "^0.1.5" @@ -1466,10 +1682,12 @@ fs-write-stream-atomic@^1.0.8: fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= fstream@^1.0.0, fstream@^1.0.2: version "1.0.11" resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171" + integrity sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE= dependencies: graceful-fs "^4.1.2" inherits "~2.0.0" @@ -1479,6 +1697,7 @@ fstream@^1.0.0, fstream@^1.0.2: gauge@~2.7.3: version "2.7.4" resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= dependencies: aproba "^1.0.3" console-control-strings "^1.0.0" @@ -1489,17 +1708,20 @@ gauge@~2.7.3: strip-ansi "^3.0.1" wide-align "^1.1.0" -genfun@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/genfun/-/genfun-4.0.1.tgz#ed10041f2e4a7f1b0a38466d17a5c3e27df1dfc1" +genfun@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/genfun/-/genfun-5.0.0.tgz#9dd9710a06900a5c4a5bf57aca5da4e52fe76537" + integrity sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA== get-caller-file@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" + integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== get-pkg-repo@^1.0.0: version "1.4.0" resolved "https://registry.yarnpkg.com/get-pkg-repo/-/get-pkg-repo-1.4.0.tgz#c73b489c06d80cc5536c2c853f9e05232056972d" + integrity sha1-xztInAbYDMVTbCyFP54FIyBWly0= dependencies: hosted-git-info "^2.1.4" meow "^3.3.0" @@ -1510,34 +1732,41 @@ get-pkg-repo@^1.0.0: get-port@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/get-port/-/get-port-3.2.0.tgz#dd7ce7de187c06c8bf353796ac71e099f0980ebc" + integrity sha1-3Xzn3hh8Bsi/NTeWrHHgmfCYDrw= get-stdin@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" + integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= get-stream@^3.0.0: version "3.0.0" - resolved "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" + integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= -get-stream@^4.0.0: +get-stream@^4.0.0, get-stream@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== dependencies: pump "^3.0.0" get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= dependencies: assert-plus "^1.0.0" -git-raw-commits@^2.0.0: +git-raw-commits@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/git-raw-commits/-/git-raw-commits-2.0.0.tgz#d92addf74440c14bcc5c83ecce3fb7f8a79118b5" + integrity sha512-w4jFEJFgKXMQJ0H0ikBk2S+4KP2VEjhCvLCNqbNRQC8BgGWgLKNCO7a9K9LI+TVT7Gfoloje502sEnctibffgg== dependencies: dargs "^4.0.1" lodash.template "^4.0.2" @@ -1548,13 +1777,15 @@ git-raw-commits@^2.0.0: git-remote-origin-url@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz#5282659dae2107145a11126112ad3216ec5fa65f" + integrity sha1-UoJlna4hBxRaERJhEq0yFuxfpl8= dependencies: gitconfiglocal "^1.0.0" pify "^2.3.0" -git-semver-tags@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/git-semver-tags/-/git-semver-tags-2.0.0.tgz#c218fd895bdf8e8e02f6bde555b2c3893ac73cd7" +git-semver-tags@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/git-semver-tags/-/git-semver-tags-2.0.2.tgz#f506ec07caade191ac0c8d5a21bdb8131b4934e3" + integrity sha512-34lMF7Yo1xEmsK2EkbArdoU79umpvm0MfzaDkSNYSJqtM5QLAVTPWgpiXSVI5o/O9EvZPSrP4Zvnec/CqhSd5w== dependencies: meow "^4.0.0" semver "^5.5.0" @@ -1562,12 +1793,14 @@ git-semver-tags@^2.0.0: gitconfiglocal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz#41d045f3851a5ea88f03f24ca1c6178114464b9b" + integrity sha1-QdBF84UaXqiPA/JMocYXgRRGS5s= dependencies: ini "^1.3.2" glob-parent@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" + integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= dependencies: is-glob "^3.1.0" path-dirname "^1.0.0" @@ -1575,10 +1808,12 @@ glob-parent@^3.1.0: glob-to-regexp@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" + integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= -glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2: +glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3: version "7.1.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" + integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -1590,6 +1825,7 @@ glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2: globby@^8.0.1: version "8.0.1" resolved "https://registry.yarnpkg.com/globby/-/globby-8.0.1.tgz#b5ad48b8aa80b35b814fc1281ecc851f1d2b5b50" + integrity sha512-oMrYrJERnKBLXNLVTqhm3vPEdJ/b2ZE28xN4YARiix1NOIOBPEpOUnm844K1iu/BkphCaf2WNFwMszv8Soi1pw== dependencies: array-union "^1.0.1" dir-glob "^2.0.0" @@ -1600,12 +1836,14 @@ globby@^8.0.1: slash "^1.0.0" graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6: - version "4.1.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" + version "4.1.15" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00" + integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA== handlebars@^4.0.2: version "4.0.12" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.12.tgz#2c15c8a96d46da5e266700518ba8cb8d919d5bc5" + integrity sha512-RhmTekP+FZL+XNhwS1Wf+bTTZpdLougwt5pcgA1tuz6Jcx0fpH/7z0qd71RKnZHBCxIRBHfBOnio4gViPemNzA== dependencies: async "^2.5.0" optimist "^0.6.1" @@ -1616,10 +1854,12 @@ handlebars@^4.0.2: har-schema@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= har-validator@~5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.0.tgz#44657f5688a22cfd4b72486e81b3a3fb11742c29" + integrity sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA== dependencies: ajv "^5.3.0" har-schema "^2.0.0" @@ -1627,14 +1867,17 @@ har-validator@~5.1.0: has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= has-unicode@^2.0.0, has-unicode@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= has-value@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= dependencies: get-value "^2.0.3" has-values "^0.1.4" @@ -1643,6 +1886,7 @@ has-value@^0.3.1: has-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= dependencies: get-value "^2.0.6" has-values "^1.0.0" @@ -1651,10 +1895,12 @@ has-value@^1.0.0: has-values@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= has-values@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= dependencies: is-number "^3.0.0" kind-of "^4.0.0" @@ -1662,14 +1908,17 @@ has-values@^1.0.0: hosted-git-info@^2.1.4, hosted-git-info@^2.6.0: version "2.7.1" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047" + integrity sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w== http-cache-semantics@^3.8.1: version "3.8.1" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2" + integrity sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w== http-proxy-agent@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" + integrity sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg== dependencies: agent-base "4" debug "3.1.0" @@ -1677,6 +1926,7 @@ http-proxy-agent@^2.1.0: http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= dependencies: assert-plus "^1.0.0" jsprim "^1.2.2" @@ -1685,6 +1935,7 @@ http-signature@~1.2.0: https-proxy-agent@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0" + integrity sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ== dependencies: agent-base "^4.1.0" debug "^3.1.0" @@ -1692,32 +1943,46 @@ https-proxy-agent@^2.2.1: humanize-ms@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" + integrity sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0= dependencies: ms "^2.0.0" iconv-lite@^0.4.24, iconv-lite@~0.4.13: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== dependencies: safer-buffer ">= 2.1.2 < 3" iferr@^0.1.5: version "0.1.5" resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" + integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE= ignore-walk@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8" + integrity sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ== dependencies: minimatch "^3.0.4" ignore@^3.3.5: version "3.3.10" resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" + integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug== + +import-fresh@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" + integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY= + dependencies: + caller-path "^2.0.0" + resolve-from "^3.0.0" import-local@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/import-local/-/import-local-1.0.0.tgz#5e4ffdc03f4fe6c009c6729beb29631c2f8227bc" + integrity sha512-vAaZHieK9qjGo58agRBg+bhHX3hoTZU/Oa3GESWLz7t1U62fk63aHuDJJEteXoDeTCcPmUT+z38gkHPZkkmpmQ== dependencies: pkg-dir "^2.0.0" resolve-cwd "^2.0.0" @@ -1725,20 +1990,24 @@ import-local@^1.0.0: imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= indent-string@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" + integrity sha1-ji1INIdCEhtKghi3oTfppSBJ3IA= dependencies: repeating "^2.0.0" indent-string@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" + integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok= inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= dependencies: once "^1.3.0" wrappy "1" @@ -1746,14 +2015,17 @@ inflight@^1.0.4: inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= ini@^1.3.2, ini@^1.3.4: version "1.3.5" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" + integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== init-package-json@^1.10.3: version "1.10.3" resolved "https://registry.yarnpkg.com/init-package-json/-/init-package-json-1.10.3.tgz#45ffe2f610a8ca134f2bd1db5637b235070f6cbe" + integrity sha512-zKSiXKhQveNteyhcj1CoOP8tqp1QuxPIPBl8Bid99DGLFqA1p87M6lNgfjJHSBoWJJlidGOv5rWjyYKEB3g2Jw== dependencies: glob "^7.1.1" npm-package-arg "^4.0.0 || ^5.0.0 || ^6.0.0" @@ -1767,6 +2039,7 @@ init-package-json@^1.10.3: inquirer@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.2.0.tgz#51adcd776f661369dc1e894859c2560a224abdd8" + integrity sha512-QIEQG4YyQ2UYZGDC4srMZ7BjHOmNk1lR2JQj5UknBapklm6WHA+VVH7N+sUdX3A7NeCfGF8o4X1S3Ao7nAcIeg== dependencies: ansi-escapes "^3.0.0" chalk "^2.0.0" @@ -1785,58 +2058,69 @@ inquirer@^6.2.0: invert-kv@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" + integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== ip@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" + integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= is-accessor-descriptor@^0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= dependencies: kind-of "^3.0.2" is-accessor-descriptor@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== dependencies: kind-of "^6.0.0" is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== is-builtin-module@^1.0.0: version "1.0.0" - resolved "http://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" + resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" + integrity sha1-VAVy0096wxGfj3bDDLwbHgN6/74= dependencies: builtin-modules "^1.0.0" is-ci@^1.0.10: version "1.2.1" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.2.1.tgz#e3779c8ee17fccf428488f6e281187f2e632841c" + integrity sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg== dependencies: ci-info "^1.5.0" is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= dependencies: kind-of "^3.0.2" is-data-descriptor@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== dependencies: kind-of "^6.0.0" is-descriptor@^0.1.0: version "0.1.6" resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== dependencies: is-accessor-descriptor "^0.1.6" is-data-descriptor "^0.1.4" @@ -1845,6 +2129,7 @@ is-descriptor@^0.1.0: is-descriptor@^1.0.0, is-descriptor@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== dependencies: is-accessor-descriptor "^1.0.0" is-data-descriptor "^1.0.0" @@ -1853,124 +2138,150 @@ is-descriptor@^1.0.0, is-descriptor@^1.0.2: is-directory@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" + integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE= is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= is-extendable@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== dependencies: is-plain-object "^2.0.4" is-extglob@^2.1.0, is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= is-finite@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" + integrity sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko= dependencies: number-is-nan "^1.0.0" is-fullwidth-code-point@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= dependencies: number-is-nan "^1.0.0" is-fullwidth-code-point@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= is-glob@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= dependencies: is-extglob "^2.1.0" is-glob@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.0.tgz#9521c76845cc2610a85203ddf080a958c2ffabc0" + integrity sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A= dependencies: is-extglob "^2.1.1" is-number@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= dependencies: kind-of "^3.0.2" is-obj@^1.0.0: version "1.0.1" - resolved "http://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" + integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8= is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== dependencies: isobject "^3.0.1" is-promise@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" + integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o= is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= is-subset@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-subset/-/is-subset-0.1.1.tgz#8a59117d932de1de00f245fcdd39ce43f1e939a6" + integrity sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY= is-text-path@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-text-path/-/is-text-path-1.0.1.tgz#4e1aa0fb51bfbcb3e92688001397202c1775b66e" + integrity sha1-Thqg+1G/vLPpJogAE5cgLBd1tm4= dependencies: text-extensions "^1.0.0" is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= is-utf8@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== isarray@1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= isobject@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= dependencies: isarray "1.0.0" isobject@^3.0.0, isobject@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= js-yaml@^3.9.0: version "3.12.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1" + integrity sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A== dependencies: argparse "^1.0.7" esprima "^4.0.0" @@ -1978,36 +2289,44 @@ js-yaml@^3.9.0: jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= json-parse-better-errors@^1.0.0, json-parse-better-errors@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== json-schema-traverse@^0.3.0: version "0.3.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" + integrity sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A= json-schema@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= optionalDependencies: graceful-fs "^4.1.6" jsonparse@^1.2.0: version "1.3.1" resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" + integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= dependencies: assert-plus "1.0.0" extsprintf "1.3.0" @@ -2017,32 +2336,38 @@ jsprim@^1.2.2: kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= dependencies: is-buffer "^1.1.5" kind-of@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= dependencies: is-buffer "^1.1.5" kind-of@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== kind-of@^6.0.0, kind-of@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" + integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA== lcid@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf" + integrity sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA== dependencies: invert-kv "^2.0.0" -lerna@^3.2.1: - version "3.4.1" - resolved "https://registry.yarnpkg.com/lerna/-/lerna-3.4.1.tgz#4acc5a6b9d843993db7a7bb1350274bcaf20ca80" +lerna@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/lerna/-/lerna-3.4.3.tgz#501454efb453c65c305802d370ee337f7298787e" + integrity sha512-tWq1LvpHqkyB+FaJCmkEweivr88yShDMmauofPVdh0M5gU1cVucszYnIgWafulKYu2LMQ3IfUMUU5Pp3+MvADQ== dependencies: "@lerna/add" "^3.4.1" "@lerna/bootstrap" "^3.4.1" @@ -2056,7 +2381,7 @@ lerna@^3.2.1: "@lerna/init" "^3.3.0" "@lerna/link" "^3.3.0" "@lerna/list" "^3.3.2" - "@lerna/publish" "^3.4.1" + "@lerna/publish" "^3.4.3" "@lerna/run" "^3.3.2" "@lerna/version" "^3.4.1" import-local "^1.0.0" @@ -2065,6 +2390,7 @@ lerna@^3.2.1: libnpmaccess@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/libnpmaccess/-/libnpmaccess-3.0.0.tgz#33cc9c8a5cb53e87d06bf2e547c2eba974f619af" + integrity sha512-SiE4AZAzMpD7pmmXHfgD7rof8QIQGoKaeyAS8exgx2CKA6tzRTbRljq1xM4Tgj8/tIg+KBJPJWkR0ifqKT3irQ== dependencies: aproba "^2.0.0" get-stream "^4.0.0" @@ -2073,7 +2399,8 @@ libnpmaccess@^3.0.0: load-json-file@^1.0.0: version "1.1.0" - resolved "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= dependencies: graceful-fs "^4.1.2" parse-json "^2.2.0" @@ -2084,6 +2411,7 @@ load-json-file@^1.0.0: load-json-file@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" + integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs= dependencies: graceful-fs "^4.1.2" parse-json "^4.0.0" @@ -2093,6 +2421,7 @@ load-json-file@^4.0.0: locate-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= dependencies: p-locate "^2.0.0" path-exists "^3.0.0" @@ -2100,6 +2429,7 @@ locate-path@^2.0.0: locate-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== dependencies: p-locate "^3.0.0" path-exists "^3.0.0" @@ -2107,14 +2437,17 @@ locate-path@^3.0.0: lodash._reinterpolate@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" + integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= lodash.sortby@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" + integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= lodash.template@^4.0.2: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.4.0.tgz#e73a0385c8355591746e020b99679c690e68fba0" + integrity sha1-5zoDhcg1VZF0bgILmWecaQ5o+6A= dependencies: lodash._reinterpolate "~3.0.0" lodash.templatesettings "^4.0.0" @@ -2122,16 +2455,19 @@ lodash.template@^4.0.2: lodash.templatesettings@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.1.0.tgz#2b4d4e95ba440d915ff08bc899e4553666713316" + integrity sha1-K01OlbpEDZFf8IvImeRVNmZxMxY= dependencies: lodash._reinterpolate "~3.0.0" lodash@^4.17.10, lodash@^4.17.5, lodash@^4.2.1: version "4.17.11" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" + integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== loud-rejection@^1.0.0: version "1.6.0" resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" + integrity sha1-W0b4AUft7leIcPCG0Eghz5mOVR8= dependencies: currently-unhandled "^0.4.1" signal-exit "^3.0.0" @@ -2139,6 +2475,7 @@ loud-rejection@^1.0.0: lru-cache@^4.1.2, lru-cache@^4.1.3: version "4.1.3" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.3.tgz#a1175cf3496dfc8436c156c334b4955992bce69c" + integrity sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA== dependencies: pseudomap "^1.0.2" yallist "^2.1.2" @@ -2146,12 +2483,14 @@ lru-cache@^4.1.2, lru-cache@^4.1.3: make-dir@^1.0.0: version "1.3.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" + integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ== dependencies: pify "^3.0.0" make-fetch-happen@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-4.0.1.tgz#141497cb878f243ba93136c83d8aba12c216c083" + integrity sha512-7R5ivfy9ilRJ1EMKIOziwrns9fGeAD4bAha8EB7BIiBBLHm2KeTUGCrICFt2rbHfzheTLynv50GnNTK1zDTrcQ== dependencies: agentkeepalive "^3.4.1" cacache "^11.0.1" @@ -2168,30 +2507,36 @@ make-fetch-happen@^4.0.1: map-age-cleaner@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.2.tgz#098fb15538fd3dbe461f12745b0ca8568d4e3f74" + integrity sha512-UN1dNocxQq44IhJyMI4TU8phc2m9BddacHRPRjKGLYaF0jqd3xLz0jS0skpAU9WgYyoR4gHtUpzytNBS385FWQ== dependencies: p-defer "^1.0.0" map-cache@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= map-obj@^1.0.0, map-obj@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= map-obj@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-2.0.0.tgz#a65cd29087a92598b8791257a523e021222ac1f9" + integrity sha1-plzSkIepJZi4eRJXpSPgISIqwfk= map-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= dependencies: object-visit "^1.0.0" mem@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/mem/-/mem-4.0.0.tgz#6437690d9471678f6cc83659c00cbafcd6b0cdaf" + integrity sha512-WQxG/5xYc3tMbYLXoXPm81ET2WDULiU5FxbuIoNbJqLOOI8zehXFdZuiUEgfdrU2mVB1pxBZUGlYORSrpuJreA== dependencies: map-age-cleaner "^0.1.1" mimic-fn "^1.0.0" @@ -2200,6 +2545,7 @@ mem@^4.0.0: meow@^3.3.0: version "3.7.0" resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" + integrity sha1-cstmi0JSKCkKu/qFaJJYcwioAfs= dependencies: camelcase-keys "^2.0.0" decamelize "^1.1.2" @@ -2215,6 +2561,7 @@ meow@^3.3.0: meow@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/meow/-/meow-4.0.1.tgz#d48598f6f4b1472f35bf6317a95945ace347f975" + integrity sha512-xcSBHD5Z86zaOc+781KrupuHAzeGXSLtiAOmBsiLDiPSaYSB6hdew2ng9EBAnZ62jagG9MHAOdxpDi/lWBFJ/A== dependencies: camelcase-keys "^4.0.0" decamelize-keys "^1.0.0" @@ -2227,12 +2574,14 @@ meow@^4.0.0: trim-newlines "^2.0.0" merge2@^1.2.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.2.2.tgz#03212e3da8d86c4d8523cebd6318193414f94e34" + version "1.2.3" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.2.3.tgz#7ee99dbd69bb6481689253f018488a1b902b0ed5" + integrity sha512-gdUU1Fwj5ep4kplwcmftruWofEFt6lfpkkr3h860CXbAB9c3hGb55EOL2ali0Td5oebvW0E1+3Sr+Ur7XfKpRA== micromatch@^3.1.10: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== dependencies: arr-diff "^4.0.0" array-unique "^0.3.2" @@ -2248,61 +2597,72 @@ micromatch@^3.1.10: snapdragon "^0.8.1" to-regex "^3.0.2" -mime-db@~1.36.0: - version "1.36.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.36.0.tgz#5020478db3c7fe93aad7bbcc4dcf869c43363397" +mime-db@~1.37.0: + version "1.37.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.37.0.tgz#0b6a0ce6fdbe9576e25f1f2d2fde8830dc0ad0d8" + integrity sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg== mime-types@^2.1.12, mime-types@~2.1.19: - version "2.1.20" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.20.tgz#930cb719d571e903738520f8470911548ca2cc19" + version "2.1.21" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.21.tgz#28995aa1ecb770742fe6ae7e58f9181c744b3f96" + integrity sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg== dependencies: - mime-db "~1.36.0" + mime-db "~1.37.0" mimic-fn@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" + integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== minimatch@^3.0.0, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== dependencies: brace-expansion "^1.1.7" minimist-options@^3.0.1: version "3.0.2" resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-3.0.2.tgz#fba4c8191339e13ecf4d61beb03f070103f3d954" + integrity sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ== dependencies: arrify "^1.0.1" is-plain-obj "^1.1.0" minimist@0.0.8: version "0.0.8" - resolved "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= minimist@^1.1.3, minimist@^1.2.0: version "1.2.0" - resolved "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= minimist@~0.0.1: version "0.0.10" - resolved "http://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" + integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8= -minipass@^2.2.1, minipass@^2.3.3: - version "2.3.4" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.4.tgz#4768d7605ed6194d6d576169b9e12ef71e9d9957" +minipass@^2.2.1, minipass@^2.3.4, minipass@^2.3.5: + version "2.3.5" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.5.tgz#cacebe492022497f656b0f0f51e2682a9ed2d848" + integrity sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA== dependencies: safe-buffer "^5.1.2" yallist "^3.0.0" -minizlib@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.1.0.tgz#11e13658ce46bc3a70a267aac58359d1e0c29ceb" +minizlib@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.1.1.tgz#6734acc045a46e61d596a43bb9d9cd326e19cc42" + integrity sha512-TrfjCjk4jLhcJyGMYymBH6oTXcWjYbUAXTHDbtnWHjZC25h0cdajHuPE1zxb4DVmu8crfh+HwH/WMuyLG0nHBg== dependencies: minipass "^2.2.1" mississippi@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022" + integrity sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA== dependencies: concat-stream "^1.5.0" duplexify "^3.4.2" @@ -2318,23 +2678,27 @@ mississippi@^3.0.0: mixin-deep@^1.2.0: version "1.3.1" resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe" + integrity sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ== dependencies: for-in "^1.0.2" is-extendable "^1.0.1" "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0: version "0.5.1" - resolved "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= dependencies: minimist "0.0.8" modify-values@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" + integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== move-concurrently@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" + integrity sha1-viwAX9oy4LKa8fBdfEszIUxwH5I= dependencies: aproba "^1.1.1" copy-concurrently "^1.0.0" @@ -2346,14 +2710,17 @@ move-concurrently@^1.0.1: ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= ms@^2.0.0, ms@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== multimatch@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-2.1.0.tgz#9c7906a22fb4c02919e2f5f75161b4cdbd4b2a2b" + integrity sha1-nHkGoi+0wCkZ4vX3UWG0zb1LKis= dependencies: array-differ "^1.0.0" array-union "^1.0.1" @@ -2363,10 +2730,12 @@ multimatch@^2.1.0: mute-stream@0.0.7, mute-stream@~0.0.4: version "0.0.7" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" + integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== dependencies: arr-diff "^4.0.0" array-unique "^0.3.2" @@ -2383,10 +2752,12 @@ nanomatch@^1.2.9: nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== node-fetch-npm@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/node-fetch-npm/-/node-fetch-npm-2.0.2.tgz#7258c9046182dca345b4208eda918daf33697ff7" + integrity sha512-nJIxm1QmAj4v3nfCvEeCrYSoVwXyxLnaPBK5W1W5DGEJwjlKuC2VEUycGw5oxk+4zZahRrB84PUJJgEmhFTDFw== dependencies: encoding "^0.1.11" json-parse-better-errors "^1.0.0" @@ -2395,6 +2766,7 @@ node-fetch-npm@^2.0.2: node-gyp@^3.8.0: version "3.8.0" resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.8.0.tgz#540304261c330e80d0d5edce253a68cb3964218c" + integrity sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA== dependencies: fstream "^1.0.0" glob "^7.0.3" @@ -2412,12 +2784,14 @@ node-gyp@^3.8.0: "nopt@2 || 3": version "3.0.6" resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" + integrity sha1-xkZdvwirzU2zWTF/eaxopkayj/k= dependencies: abbrev "1" normalize-package-data@^2.0.0, normalize-package-data@^2.3.0, normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package-data@^2.3.5, normalize-package-data@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" + integrity sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw== dependencies: hosted-git-info "^2.1.4" is-builtin-module "^1.0.0" @@ -2427,10 +2801,12 @@ normalize-package-data@^2.0.0, normalize-package-data@^2.3.0, normalize-package- npm-bundled@^1.0.1: version "1.0.5" resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.5.tgz#3c1732b7ba936b3a10325aef616467c0ccbcc979" + integrity sha512-m/e6jgWu8/v5niCUKQi9qQl8QdeEduFA96xHDDzFGqly0OOjI7c+60KM/2sppfnUU9JJagf+zs+yGhqSOFj71g== npm-lifecycle@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/npm-lifecycle/-/npm-lifecycle-2.1.0.tgz#1eda2eedb82db929e3a0c50341ab0aad140ed569" + integrity sha512-QbBfLlGBKsktwBZLj6AviHC6Q9Y3R/AY4a2PYSIRhSKSS0/CxRyD/PfxEX6tPeOCXQgMSNdwGeECacstgptc+g== dependencies: byline "^5.0.0" graceful-fs "^4.1.11" @@ -2444,29 +2820,34 @@ npm-lifecycle@^2.0.0: "npm-package-arg@^4.0.0 || ^5.0.0 || ^6.0.0", npm-package-arg@^6.0.0, npm-package-arg@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-6.1.0.tgz#15ae1e2758a5027efb4c250554b85a737db7fcc1" + integrity sha512-zYbhP2k9DbJhA0Z3HKUePUgdB1x7MfIfKssC+WLPFMKTBZKpZh5m13PgexJjCq6KW7j17r0jHWcCpxEqnnncSA== dependencies: hosted-git-info "^2.6.0" osenv "^0.1.5" semver "^5.5.0" validate-npm-package-name "^3.0.0" -npm-packlist@^1.1.10: - version "1.1.11" - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.1.11.tgz#84e8c683cbe7867d34b1d357d893ce29e28a02de" +npm-packlist@^1.1.12: + version "1.1.12" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.1.12.tgz#22bde2ebc12e72ca482abd67afc51eb49377243a" + integrity sha512-WJKFOVMeAlsU/pjXuqVdzU0WfgtIBCupkEVwn+1Y0ERAbUfWw8R4GjgVbaKnUjRoD2FoQbHOCbOyT5Mbs9Lw4g== dependencies: ignore-walk "^3.0.1" npm-bundled "^1.0.1" -npm-pick-manifest@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-2.1.0.tgz#dc381bdd670c35d81655e1d5a94aa3dd4d87fce5" +npm-pick-manifest@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-2.2.3.tgz#32111d2a9562638bb2c8f2bf27f7f3092c8fae40" + integrity sha512-+IluBC5K201+gRU85vFlUwX3PFShZAbAgDNp2ewJdWMVSppdo/Zih0ul2Ecky/X7b51J7LrrUAP+XOmOCvYZqA== dependencies: + figgy-pudding "^3.5.1" npm-package-arg "^6.0.0" semver "^5.4.1" -npm-registry-fetch@^3.0.0, npm-registry-fetch@^3.8.0: +npm-registry-fetch@^3.8.0: version "3.8.0" resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-3.8.0.tgz#aa7d9a7c92aff94f48dba0984bdef4bd131c88cc" + integrity sha512-hrw8UMD+Nob3Kl3h8Z/YjmKamb1gf7D1ZZch2otrIXM3uFLB5vjEY6DhMlq80z/zZet6eETLbOXcuQudCB3Zpw== dependencies: JSONStream "^1.3.4" bluebird "^3.5.1" @@ -2478,12 +2859,14 @@ npm-registry-fetch@^3.0.0, npm-registry-fetch@^3.8.0: npm-run-path@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= dependencies: path-key "^2.0.0" "npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== dependencies: are-we-there-yet "~1.1.2" console-control-strings "~1.1.0" @@ -2493,18 +2876,22 @@ npm-run-path@^2.0.0: number-is-nan@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= oauth-sign@~0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== object-assign@^4.0.1, object-assign@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= object-copy@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= dependencies: copy-descriptor "^0.1.0" define-property "^0.2.5" @@ -2513,30 +2900,35 @@ object-copy@^0.1.0: object-visit@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= dependencies: isobject "^3.0.0" object.pick@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= dependencies: isobject "^3.0.1" once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= dependencies: wrappy "1" onetime@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= dependencies: mimic-fn "^1.0.0" optimist@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" + integrity sha1-2j6nRob6IaGaERwybpDrFaAZZoY= dependencies: minimist "~0.0.1" wordwrap "~0.0.2" @@ -2544,10 +2936,12 @@ optimist@^0.6.1: os-homedir@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= os-locale@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.0.1.tgz#3b014fbf01d87f60a1e5348d80fe870dc82c4620" + integrity sha512-7g5e7dmXPtzcP4bgsZ8ixDVqA7oWYuEz4lOSujeWyliPai4gfVDiFIcwBg3aGCPnmSGfzOKTK3ccPn0CKv3DBw== dependencies: execa "^0.10.0" lcid "^2.0.0" @@ -2556,10 +2950,12 @@ os-locale@^3.0.0: os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= osenv@0, osenv@^0.1.5: version "0.1.5" resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" + integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== dependencies: os-homedir "^1.0.0" os-tmpdir "^1.0.0" @@ -2567,106 +2963,122 @@ osenv@0, osenv@^0.1.5: p-defer@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" + integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww= p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= p-is-promise@^1.1.0: version "1.1.0" - resolved "http://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz#9c9456989e9f6588017b0434d56097675c3da05e" + resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-1.1.0.tgz#9c9456989e9f6588017b0434d56097675c3da05e" + integrity sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4= p-limit@^1.1.0: version "1.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== dependencies: p-try "^1.0.0" p-limit@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.0.0.tgz#e624ed54ee8c460a778b3c9f3670496ff8a57aec" + integrity sha512-fl5s52lI5ahKCernzzIyAP0QAZbGIovtVHGwpcu1Jr/EpzLVDI2myISHwGqK7m8uQFugVWSrbxH7XnhGtvEc+A== dependencies: p-try "^2.0.0" p-locate@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= dependencies: p-limit "^1.1.0" p-locate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== dependencies: p-limit "^2.0.0" p-map-series@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-map-series/-/p-map-series-1.0.0.tgz#bf98fe575705658a9e1351befb85ae4c1f07bdca" + integrity sha1-v5j+V1cFZYqeE1G++4WuTB8Hvco= dependencies: p-reduce "^1.0.0" p-map@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b" + integrity sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA== p-pipe@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/p-pipe/-/p-pipe-1.2.0.tgz#4b1a11399a11520a67790ee5a0c1d5881d6befe9" + integrity sha1-SxoROZoRUgpneQ7loMHViB1r7+k= p-reduce@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-1.0.0.tgz#18c2b0dd936a4690a529f8231f58a0fdb6a47dfa" + integrity sha1-GMKw3ZNqRpClKfgjH1ig/bakffo= p-try@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= p-try@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.0.0.tgz#85080bb87c64688fa47996fe8f7dfbe8211760b1" + integrity sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ== p-waterfall@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-waterfall/-/p-waterfall-1.0.0.tgz#7ed94b3ceb3332782353af6aae11aa9fc235bb00" + integrity sha1-ftlLPOszMngjU69qrhGqn8I1uwA= dependencies: p-reduce "^1.0.0" pacote@^9.1.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/pacote/-/pacote-9.1.0.tgz#59810859bbd72984dcb267269259375d32f391e5" + version "9.2.3" + resolved "https://registry.yarnpkg.com/pacote/-/pacote-9.2.3.tgz#48cfe87beb9177acd6594355a584a538835424b3" + integrity sha512-Y3+yY3nBRAxMlZWvr62XLJxOwCmG9UmkGZkFurWHoCjqF0cZL72cTOCRJTvWw8T4OhJS2RTg13x4oYYriauvEw== dependencies: - bluebird "^3.5.1" - cacache "^11.0.2" - figgy-pudding "^3.2.1" - get-stream "^3.0.0" - glob "^7.1.2" + bluebird "^3.5.2" + cacache "^11.2.0" + figgy-pudding "^3.5.1" + get-stream "^4.1.0" + glob "^7.1.3" lru-cache "^4.1.3" make-fetch-happen "^4.0.1" minimatch "^3.0.4" - minipass "^2.3.3" + minipass "^2.3.5" mississippi "^3.0.0" mkdirp "^0.5.1" normalize-package-data "^2.4.0" npm-package-arg "^6.1.0" - npm-packlist "^1.1.10" - npm-pick-manifest "^2.1.0" - npm-registry-fetch "^3.0.0" + npm-packlist "^1.1.12" + npm-pick-manifest "^2.2.3" + npm-registry-fetch "^3.8.0" osenv "^0.1.5" promise-inflight "^1.0.1" promise-retry "^1.1.1" - protoduck "^5.0.0" + protoduck "^5.0.1" rimraf "^2.6.2" safe-buffer "^5.1.2" - semver "^5.5.0" - ssri "^6.0.0" - tar "^4.4.3" - unique-filename "^1.1.0" - which "^1.3.0" + semver "^5.6.0" + ssri "^6.0.1" + tar "^4.4.6" + unique-filename "^1.1.1" + which "^1.3.1" parallel-transform@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.1.0.tgz#d410f065b05da23081fcd10f28854c29bda33b06" + integrity sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY= dependencies: cyclist "~0.2.2" inherits "^2.0.3" @@ -2675,16 +3087,19 @@ parallel-transform@^1.1.0: parse-github-repo-url@^1.3.0: version "1.4.1" resolved "https://registry.yarnpkg.com/parse-github-repo-url/-/parse-github-repo-url-1.4.1.tgz#9e7d8bb252a6cb6ba42595060b7bf6df3dbc1f50" + integrity sha1-nn2LslKmy2ukJZUGC3v23z28H1A= parse-json@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= dependencies: error-ex "^1.2.0" parse-json@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= dependencies: error-ex "^1.3.1" json-parse-better-errors "^1.0.1" @@ -2692,32 +3107,39 @@ parse-json@^4.0.0: pascalcase@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= path-dirname@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" + integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= path-exists@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= dependencies: pinkie-promise "^2.0.0" path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= path-key@^2.0.0, path-key@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= path-type@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= dependencies: graceful-fs "^4.1.2" pify "^2.0.0" @@ -2726,52 +3148,63 @@ path-type@^1.0.0: path-type@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" + integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== dependencies: pify "^3.0.0" performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= pify@^2.0.0, pify@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= pify@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= pinkie-promise@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= dependencies: pinkie "^2.0.0" pinkie@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= pkg-dir@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" + integrity sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s= dependencies: find-up "^2.1.0" posix-character-classes@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= process-nextick-args@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" + integrity sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw== promise-inflight@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" + integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= promise-retry@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-1.1.1.tgz#6739e968e3051da20ce6497fb2b50f6911df3d6d" + integrity sha1-ZznpaOMFHaIM5kl/srUPaRHfPW0= dependencies: err-code "^1.0.0" retry "^0.10.0" @@ -2779,30 +3212,36 @@ promise-retry@^1.1.1: promzard@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/promzard/-/promzard-0.3.0.tgz#26a5d6ee8c7dee4cb12208305acfb93ba382a9ee" + integrity sha1-JqXW7ox97kyxIggwWs+5O6OCqe4= dependencies: read "1" proto-list@~1.2.1: version "1.2.4" resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" + integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= -protoduck@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/protoduck/-/protoduck-5.0.0.tgz#752145e6be0ad834cb25716f670a713c860dce70" +protoduck@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/protoduck/-/protoduck-5.0.1.tgz#03c3659ca18007b69a50fd82a7ebcc516261151f" + integrity sha512-WxoCeDCoCBY55BMvj4cAEjdVUFGRWed9ZxPlqTKYyw1nDDTQ4pqmnIMAGfJlg7Dx35uB/M+PHJPTmGOvaCaPTg== dependencies: - genfun "^4.0.1" + genfun "^5.0.0" pseudomap@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= psl@^1.1.24: version "1.1.29" resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.29.tgz#60f580d360170bb722a797cc704411e6da850c67" + integrity sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ== pump@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" + integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA== dependencies: end-of-stream "^1.1.0" once "^1.3.1" @@ -2810,6 +3249,7 @@ pump@^2.0.0: pump@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== dependencies: end-of-stream "^1.1.0" once "^1.3.1" @@ -2817,6 +3257,7 @@ pump@^3.0.0: pumpify@^1.3.3: version "1.5.1" resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" + integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ== dependencies: duplexify "^3.6.0" inherits "^2.0.3" @@ -2825,32 +3266,39 @@ pumpify@^1.3.3: punycode@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= punycode@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== q@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" + integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= qs@~6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== quick-lru@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8" + integrity sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g= read-cmd-shim@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-1.0.1.tgz#2d5d157786a37c055d22077c32c53f8329e91c7b" + integrity sha1-LV0Vd4ajfAVdIgd8MsU/gynpHHs= dependencies: graceful-fs "^4.1.2" "read-package-json@1 || 2", read-package-json@^2.0.0: version "2.0.13" resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-2.0.13.tgz#2e82ebd9f613baa6d2ebe3aa72cefe3f68e41f4a" + integrity sha512-/1dZ7TRZvGrYqE0UAfN6qQb5GYBsNcqS1C0tNK601CFOJmtHI7NIGXwetEPU/OtoFHZL3hDxm4rolFFVE9Bnmg== dependencies: glob "^7.1.1" json-parse-better-errors "^1.0.1" @@ -2862,6 +3310,7 @@ read-cmd-shim@^1.0.1: read-package-tree@^5.1.6: version "5.2.1" resolved "https://registry.yarnpkg.com/read-package-tree/-/read-package-tree-5.2.1.tgz#6218b187d6fac82289ce4387bbbaf8eef536ad63" + integrity sha512-2CNoRoh95LxY47LvqrehIAfUVda2JbuFE/HaGYs42bNrGG+ojbw1h3zOcPcQ+1GQ3+rkzNndZn85u1XyZ3UsIA== dependencies: debuglog "^1.0.1" dezalgo "^1.0.0" @@ -2872,6 +3321,7 @@ read-package-tree@^5.1.6: read-pkg-up@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= dependencies: find-up "^1.0.0" read-pkg "^1.0.0" @@ -2879,13 +3329,15 @@ read-pkg-up@^1.0.1: read-pkg-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" + integrity sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc= dependencies: find-up "^2.0.0" read-pkg "^3.0.0" -read-pkg@^1.0.0, read-pkg@^1.1.0: +read-pkg@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= dependencies: load-json-file "^1.0.0" normalize-package-data "^2.3.2" @@ -2894,6 +3346,7 @@ read-pkg@^1.0.0, read-pkg@^1.1.0: read-pkg@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" + integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k= dependencies: load-json-file "^4.0.0" normalize-package-data "^2.3.2" @@ -2902,12 +3355,14 @@ read-pkg@^3.0.0: read@1, read@~1.0.1: version "1.0.7" resolved "https://registry.yarnpkg.com/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4" + integrity sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ= dependencies: mute-stream "~0.0.4" -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.4, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2: +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.4, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@~2.3.6: version "2.3.6" - resolved "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" + integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== dependencies: core-util-is "~1.0.0" inherits "~2.0.3" @@ -2920,6 +3375,7 @@ read@1, read@~1.0.1: readdir-scoped-modules@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.0.2.tgz#9fafa37d286be5d92cbaebdee030dc9b5f406747" + integrity sha1-n6+jfShr5dksuuve4DDcm19AZ0c= dependencies: debuglog "^1.0.1" dezalgo "^1.0.0" @@ -2929,6 +3385,7 @@ readdir-scoped-modules@^1.0.0: redent@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" + integrity sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94= dependencies: indent-string "^2.1.0" strip-indent "^1.0.1" @@ -2936,6 +3393,7 @@ redent@^1.0.0: redent@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-2.0.0.tgz#c1b2007b42d57eb1389079b3c8333639d5e1ccaa" + integrity sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo= dependencies: indent-string "^3.0.0" strip-indent "^2.0.0" @@ -2943,6 +3401,7 @@ redent@^2.0.0: regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== dependencies: extend-shallow "^3.0.2" safe-regex "^1.1.0" @@ -2950,20 +3409,24 @@ regex-not@^1.0.0, regex-not@^1.0.2: repeat-element@^1.1.2: version "1.1.3" resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" + integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= repeating@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= dependencies: is-finite "^1.0.0" request@^2.87.0: version "2.88.0" resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" + integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== dependencies: aws-sign2 "~0.7.0" aws4 "^1.8.0" @@ -2989,32 +3452,39 @@ request@^2.87.0: require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= require-main-filename@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" + integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= resolve-cwd@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" + integrity sha1-AKn3OHVW4nA46uIyyqNypqWbZlo= dependencies: resolve-from "^3.0.0" resolve-from@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + integrity sha1-six699nWiBvItuZTM17rywoYh0g= resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= restore-cursor@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= dependencies: onetime "^2.0.0" signal-exit "^3.0.2" @@ -3022,64 +3492,82 @@ restore-cursor@^2.0.0: ret@~0.1.10: version "0.1.15" resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== retry@^0.10.0: version "0.10.1" resolved "https://registry.yarnpkg.com/retry/-/retry-0.10.1.tgz#e76388d217992c252750241d3d3956fed98d8ff4" + integrity sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q= rimraf@2, rimraf@^2.5.4, rimraf@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" + integrity sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w== dependencies: glob "^7.0.5" run-async@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" + integrity sha1-A3GrSuC91yDUFm19/aZP96RFpsA= dependencies: is-promise "^2.1.0" run-queue@^1.0.0, run-queue@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" + integrity sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec= dependencies: aproba "^1.1.1" rxjs@^6.1.0: version "6.3.3" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.3.3.tgz#3c6a7fa420e844a81390fb1158a9ec614f4bad55" + integrity sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw== dependencies: tslib "^1.9.0" safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== safe-regex@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= dependencies: ret "~0.1.10" "safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -"semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", semver@^5.4.1, semver@^5.5.0: - version "5.5.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.1.tgz#7dfdd8814bdb7cabc7be0fb1d734cfb66c940477" +sax@>=0.6.0: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + +"semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.6.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" + integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg== semver@~5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" + integrity sha1-myzl094C0XxgEq0yaqa00M9U+U8= set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= set-value@^0.4.3: version "0.4.3" resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1" + integrity sha1-fbCPnT0i3H945Trzw79GZuzfzPE= dependencies: extend-shallow "^2.0.1" is-extendable "^0.1.1" @@ -3089,6 +3577,7 @@ set-value@^0.4.3: set-value@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.0.tgz#71ae4a88f0feefbbf52d1ea604f3fb315ebb6274" + integrity sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg== dependencies: extend-shallow "^2.0.1" is-extendable "^0.1.1" @@ -3098,32 +3587,39 @@ set-value@^2.0.0: shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= dependencies: shebang-regex "^1.0.0" shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= slash@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" + integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU= slide@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" + integrity sha1-VusCfWW00tzmyy4tMsTUr8nh1wc= smart-buffer@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.0.1.tgz#07ea1ca8d4db24eb4cac86537d7d18995221ace3" + integrity sha512-RFqinRVJVcCAL9Uh1oVqE6FZkqsyLiVOYEZ20TqIOjuX7iFVJ+zsbs4RIghnw/pTs7mZvt8ZHhvm1ZUrR4fykg== snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== dependencies: define-property "^1.0.0" isobject "^3.0.0" @@ -3132,12 +3628,14 @@ snapdragon-node@^2.0.1: snapdragon-util@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== dependencies: kind-of "^3.2.0" snapdragon@^0.8.1: version "0.8.2" resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== dependencies: base "^0.11.1" debug "^2.2.0" @@ -3151,13 +3649,15 @@ snapdragon@^0.8.1: socks-proxy-agent@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-4.0.1.tgz#5936bf8b707a993079c6f37db2091821bffa6473" + integrity sha512-Kezx6/VBguXOsEe5oU3lXYyKMi4+gva72TwJ7pQY5JfqUx2nMk7NXA6z/mpNqIlfQjWYVfeuNvQjexiTaTn6Nw== dependencies: agent-base "~4.2.0" socks "~2.2.0" socks@~2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/socks/-/socks-2.2.1.tgz#68ad678b3642fbc5d99c64c165bc561eab0215f9" + version "2.2.2" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.2.2.tgz#f061219fc2d4d332afb4af93e865c84d3fa26e2b" + integrity sha512-g6wjBnnMOZpE0ym6e0uHSddz9p3a+WsBaaYQaBaSCJYvrC4IXykQR9MNGjLQf38e9iIIhp3b1/Zk8YZI3KGJ0Q== dependencies: ip "^1.1.5" smart-buffer "^4.0.1" @@ -3165,12 +3665,14 @@ socks@~2.2.0: sort-keys@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128" + integrity sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg= dependencies: is-plain-obj "^1.0.0" source-map-resolve@^0.5.0: version "0.5.2" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" + integrity sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA== dependencies: atob "^2.1.1" decode-uri-component "^0.2.0" @@ -3181,18 +3683,22 @@ source-map-resolve@^0.5.0: source-map-url@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" + integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= source-map@^0.5.6: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== spdx-correct@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.0.2.tgz#19bb409e91b47b1ad54159243f7312a858db3c2e" + integrity sha512-q9hedtzyXHr5S0A1vEPoK/7l8NpfkFYTq6iCY+Pno2ZbdZR6WexZFtqeVGkGxW3TEJMN914Z55EnAGMmenlIQQ== dependencies: spdx-expression-parse "^3.0.0" spdx-license-ids "^3.0.0" @@ -3200,64 +3706,73 @@ spdx-correct@^3.0.0: spdx-exceptions@^2.1.0: version "2.2.0" resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz#2ea450aee74f2a89bfb94519c07fcd6f41322977" + integrity sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA== spdx-expression-parse@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0" + integrity sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg== dependencies: spdx-exceptions "^2.1.0" spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.1.tgz#e2a303236cac54b04031fa7a5a79c7e701df852f" + version "3.0.2" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.2.tgz#a59efc09784c2a5bada13cfeaf5c75dd214044d2" + integrity sha512-qky9CVt0lVIECkEsYbNILVnPvycuEBkXoMFLRWsREkomQLevYhtRKC+R91a5TOAQ3bCMjikRwhyaRqj1VYatYg== split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== dependencies: extend-shallow "^3.0.0" split2@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/split2/-/split2-2.2.0.tgz#186b2575bcf83e85b7d18465756238ee4ee42493" + integrity sha512-RAb22TG39LhI31MbreBgIuKiIKhVsawfTgEGqKHTK87aG+ul/PB8Sqoi3I7kVdRWiCfrKxK3uo4/YUkpNvhPbw== dependencies: through2 "^2.0.2" split@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9" + integrity sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg== dependencies: through "2" sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= sshpk@^1.7.0: - version "1.14.2" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.14.2.tgz#c6fc61648a3d9c4e764fd3fcdf4ea105e492ba98" + version "1.15.2" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.15.2.tgz#c946d6bd9b1a39d0e8635763f5242d6ed6dcb629" + integrity sha512-Ra/OXQtuh0/enyl4ETZAfTaeksa6BXks5ZcjpSUNrjBr0DvrJKX+1fsKDPpT9TBXgHAFsa4510aNVgI8g/+SzA== dependencies: asn1 "~0.2.3" assert-plus "^1.0.0" - dashdash "^1.12.0" - getpass "^0.1.1" - safer-buffer "^2.0.2" - optionalDependencies: bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" ecc-jsbn "~0.1.1" + getpass "^0.1.1" jsbn "~0.1.0" + safer-buffer "^2.0.2" tweetnacl "~0.14.0" -ssri@^6.0.0: +ssri@^6.0.0, ssri@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.1.tgz#2a3c41b28dd45b62b63676ecb74001265ae9edd8" + integrity sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA== dependencies: figgy-pudding "^3.5.1" static-extend@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= dependencies: define-property "^0.2.5" object-copy "^0.1.0" @@ -3265,6 +3780,7 @@ static-extend@^0.1.1: stream-each@^1.1.0: version "1.2.3" resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.3.tgz#ebe27a0c389b04fbcc233642952e10731afa9bae" + integrity sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw== dependencies: end-of-stream "^1.1.0" stream-shift "^1.0.0" @@ -3272,10 +3788,12 @@ stream-each@^1.1.0: stream-shift@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952" + integrity sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI= string-width@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= dependencies: code-point-at "^1.0.0" is-fullwidth-code-point "^1.0.0" @@ -3284,6 +3802,7 @@ string-width@^1.0.1: "string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== dependencies: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" @@ -3291,48 +3810,57 @@ string-width@^1.0.1: string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== dependencies: safe-buffer "~5.1.0" strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" - resolved "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= dependencies: ansi-regex "^2.0.0" strip-ansi@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= dependencies: ansi-regex "^3.0.0" strip-bom@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= dependencies: is-utf8 "^0.2.0" strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= strip-eof@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= strip-indent@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" + integrity sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI= dependencies: get-stdin "^4.0.1" strip-indent@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" + integrity sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g= strong-log-transformer@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strong-log-transformer/-/strong-log-transformer-2.0.0.tgz#fa6d8e0a9e62b3c168c3cad5ae5d00dc97ba26cc" + integrity sha512-FQmNqAXJgOX8ygOcvPLlGWBNT41mvNJ9ALoYf0GTwVt9t30mGTqpmp/oJx5gLcu52DXK10kS7dVWhx8aPXDTlg== dependencies: byline "^5.0.0" duplexer "^0.1.1" @@ -3342,25 +3870,28 @@ strong-log-transformer@^2.0.0: supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== dependencies: has-flag "^3.0.0" tar@^2.0.0: version "2.2.1" resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" + integrity sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE= dependencies: block-stream "*" fstream "^1.0.2" inherits "2" -tar@^4.4.3: - version "4.4.6" - resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.6.tgz#63110f09c00b4e60ac8bcfe1bf3c8660235fbc9b" +tar@^4.4.6: + version "4.4.8" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.8.tgz#b19eec3fde2a96e64666df9fdb40c5ca1bc3747d" + integrity sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ== dependencies: - chownr "^1.0.1" + chownr "^1.1.1" fs-minipass "^1.2.5" - minipass "^2.3.3" - minizlib "^1.1.0" + minipass "^2.3.4" + minizlib "^1.1.1" mkdirp "^0.5.0" safe-buffer "^5.1.2" yallist "^3.0.2" @@ -3368,10 +3899,12 @@ tar@^4.4.3: temp-dir@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d" + integrity sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0= temp-write@^3.4.0: version "3.4.0" resolved "https://registry.yarnpkg.com/temp-write/-/temp-write-3.4.0.tgz#8cff630fb7e9da05f047c74ce4ce4d685457d492" + integrity sha1-jP9jD7fp2gXwR8dM5M5NaFRX1JI= dependencies: graceful-fs "^4.1.2" is-stream "^1.1.0" @@ -3381,35 +3914,41 @@ temp-write@^3.4.0: uuid "^3.0.1" text-extensions@^1.0.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.8.0.tgz#6f343c62268843019b21a616a003557bdb952d2b" + version "1.9.0" + resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26" + integrity sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ== through2@^2.0.0, through2@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be" + version "2.0.5" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" + integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== dependencies: - readable-stream "^2.1.5" + readable-stream "~2.3.6" xtend "~4.0.1" through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6: version "2.3.8" - resolved "http://registry.npmjs.org/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== dependencies: os-tmpdir "~1.0.2" to-object-path@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= dependencies: kind-of "^3.0.2" to-regex-range@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= dependencies: is-number "^3.0.0" repeat-string "^1.6.1" @@ -3417,6 +3956,7 @@ to-regex-range@^2.1.0: to-regex@^3.0.1, to-regex@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== dependencies: define-property "^2.0.2" extend-shallow "^3.0.2" @@ -3426,6 +3966,7 @@ to-regex@^3.0.1, to-regex@^3.0.2: tough-cookie@~2.4.3: version "2.4.3" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" + integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ== dependencies: psl "^1.1.24" punycode "^1.4.1" @@ -3433,42 +3974,51 @@ tough-cookie@~2.4.3: tr46@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" + integrity sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk= dependencies: punycode "^2.1.0" trim-newlines@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" + integrity sha1-WIeWa7WCpFA6QetST301ARgVphM= trim-newlines@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-2.0.0.tgz#b403d0b91be50c331dfc4b82eeceb22c3de16d20" + integrity sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA= trim-off-newlines@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz#9f9ba9d9efa8764c387698bcbfeb2c848f11adb3" + integrity sha1-n5up2e+odkw4dpi8v+sshI8RrbM= tslib@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" + integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= dependencies: safe-buffer "^5.0.1" tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= uglify-js@^3.1.4: version "3.4.9" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.9.tgz#af02f180c1207d76432e473ed24a28f4a782bae3" + integrity sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q== dependencies: commander "~2.17.1" source-map "~0.6.1" @@ -3476,39 +4026,46 @@ uglify-js@^3.1.4: uid-number@0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" + integrity sha1-DqEOgDXo61uOREnwbaHHMGY7qoE= umask@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/umask/-/umask-1.1.0.tgz#f29cebf01df517912bb58ff9c4e50fde8e33320d" + integrity sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0= union-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" + integrity sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ= dependencies: arr-union "^3.1.0" get-value "^2.0.6" is-extendable "^0.1.1" set-value "^0.4.3" -unique-filename@^1.1.0: +unique-filename@^1.1.0, unique-filename@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" + integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ== dependencies: unique-slug "^2.0.0" unique-slug@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.1.tgz#5e9edc6d1ce8fb264db18a507ef9bd8544451ca6" + integrity sha512-n9cU6+gITaVu7VGj1Z8feKMmfAjEAQGhwD9fE3zvpRRa0wEIx8ODYkVGfSc94M2OX00tUFV8wH3zYbm1I8mxFg== dependencies: imurmurhash "^0.1.4" universalify@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== unset-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= dependencies: has-value "^0.3.1" isobject "^3.0.0" @@ -3516,22 +4073,27 @@ unset-value@^1.0.0: urix@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= use@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= uuid@^3.0.1, uuid@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" + integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.3: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== dependencies: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" @@ -3539,12 +4101,14 @@ validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.3: validate-npm-package-name@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz#5fa912d81eb7d0c74afc140de7317f0ca7df437e" + integrity sha1-X6kS2B630MdK/BQN5zF/DKffQ34= dependencies: builtins "^1.0.3" verror@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= dependencies: assert-plus "^1.0.0" core-util-is "1.0.2" @@ -3553,16 +4117,19 @@ verror@1.10.0: wcwidth@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g= dependencies: defaults "^1.0.3" webidl-conversions@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" + integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== whatwg-url@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.0.0.tgz#fde926fa54a599f3adf82dff25a9f7be02dc6edd" + integrity sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ== dependencies: lodash.sortby "^4.7.0" tr46 "^1.0.1" @@ -3571,26 +4138,31 @@ whatwg-url@^7.0.0: which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= -which@1, which@^1.2.9, which@^1.3.0, which@^1.3.1: +which@1, which@^1.2.9, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== dependencies: isexe "^2.0.0" wide-align@^1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== dependencies: string-width "^1.0.2 || 2" wordwrap@~0.0.2: version "0.0.3" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" + integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc= wrap-ansi@^2.0.0: version "2.1.0" - resolved "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" + integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= dependencies: string-width "^1.0.1" strip-ansi "^3.0.1" @@ -3598,10 +4170,12 @@ wrap-ansi@^2.0.0: wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= write-file-atomic@^2.0.0, write-file-atomic@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.3.0.tgz#1ff61575c2e2a4e8e510d6fa4e243cce183999ab" + integrity sha512-xuPeK4OdjWqtfi59ylvVL0Yn35SF3zgcAcv7rBPFHVaEapaDr4GdGgm3j7ckTwH9wHL7fGmgfAnb0+THrHb8tA== dependencies: graceful-fs "^4.1.11" imurmurhash "^0.1.4" @@ -3610,6 +4184,7 @@ write-file-atomic@^2.0.0, write-file-atomic@^2.3.0: write-json-file@^2.2.0, write-json-file@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/write-json-file/-/write-json-file-2.3.0.tgz#2b64c8a33004d54b8698c76d585a77ceb61da32f" + integrity sha1-K2TIozAE1UuGmMdtWFp3zrYdoy8= dependencies: detect-indent "^5.0.0" graceful-fs "^4.1.2" @@ -3621,39 +4196,60 @@ write-json-file@^2.2.0, write-json-file@^2.3.0: write-pkg@^3.1.0: version "3.2.0" resolved "https://registry.yarnpkg.com/write-pkg/-/write-pkg-3.2.0.tgz#0e178fe97820d389a8928bc79535dbe68c2cff21" + integrity sha512-tX2ifZ0YqEFOF1wjRW2Pk93NLsj02+n1UP5RvO6rCs0K6R2g1padvf006cY74PQJKMGS2r42NK7FD0dG6Y6paw== dependencies: sort-keys "^2.0.0" write-json-file "^2.2.0" +xml2js@^0.4.19: + version "0.4.19" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7" + integrity sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q== + dependencies: + sax ">=0.6.0" + xmlbuilder "~9.0.1" + +xmlbuilder@~9.0.1: + version "9.0.7" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" + integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0= + xregexp@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.0.0.tgz#e698189de49dd2a18cc5687b05e17c8e43943020" + integrity sha512-PHyM+sQouu7xspQQwELlGwwd05mXUFqwFYfqPO0cC7x4fxyHnnuetmQr6CjJiafIDoH4MogHb9dOoJzR/Y4rFg== xtend@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" + integrity sha1-pcbVMr5lbiPbgg77lDofBJmNY68= "y18n@^3.2.1 || ^4.0.0", y18n@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" + integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== yallist@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= yallist@^3.0.0, yallist@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.2.tgz#8452b4bb7e83c7c188d8041c1a837c773d6d8bb9" + integrity sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k= yargs-parser@^10.1.0: version "10.1.0" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-10.1.0.tgz#7202265b89f7e9e9f2e5765e0fe735a905edbaa8" + integrity sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ== dependencies: camelcase "^4.1.0" yargs@^12.0.1: version "12.0.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.2.tgz#fe58234369392af33ecbef53819171eff0f5aadc" + integrity sha512-e7SkEx6N6SIZ5c5H22RTZae61qtn3PYUE8JYbBFlK9sYmh3DMQ6E5ygtaG/2BW0JZi4WGgTR2IV5ChqlqrDGVQ== dependencies: cliui "^4.0.0" decamelize "^2.0.0" diff --git a/scm-ui/src/containers/Index.js b/scm-ui/src/containers/Index.js index 0fe6364f6e..71bd6b35c2 100644 --- a/scm-ui/src/containers/Index.js +++ b/scm-ui/src/containers/Index.js @@ -27,13 +27,32 @@ type Props = { t: string => string }; -class Index extends Component { +type State = { + pluginsLoaded: boolean +}; + +class Index extends Component { + + constructor(props: Props) { + super(props); + this.state = { + pluginsLoaded: false + }; + } + componentDidMount() { this.props.fetchIndexResources(); } + pluginLoaderCallback = () => { + this.setState({ + pluginsLoaded: true + }); + }; + render() { const { indexResources, loading, error, t } = this.props; + const { pluginsLoaded } = this.state; if (error) { return ( @@ -47,7 +66,7 @@ class Index extends Component { return ; } else { return ( - + ); diff --git a/scm-ui/src/containers/PluginLoader.js b/scm-ui/src/containers/PluginLoader.js index 8e44a1d427..308f532270 100644 --- a/scm-ui/src/containers/PluginLoader.js +++ b/scm-ui/src/containers/PluginLoader.js @@ -5,12 +5,13 @@ import { getUiPluginsLink } from "../modules/indexResource"; import { connect } from "react-redux"; type Props = { + loaded: boolean, children: React.Node, - link: string + link: string, + callback: () => void }; type State = { - finished: boolean, message: string }; @@ -23,17 +24,19 @@ class PluginLoader extends React.Component { constructor(props: Props) { super(props); this.state = { - finished: false, message: "booting" }; } componentDidMount() { - this.setState({ - message: "loading plugin information" - }); - - this.getPlugins(this.props.link); + const { loaded } = this.props; + if (!loaded) { + this.setState({ + message: "loading plugin information" + }); + + this.getPlugins(this.props.link); + } } getPlugins = (link: string): Promise => { @@ -43,11 +46,7 @@ class PluginLoader extends React.Component { .then(JSON.parse) .then(pluginCollection => pluginCollection._embedded.plugins) .then(this.loadPlugins) - .then(() => { - this.setState({ - finished: true - }); - }); + .then(this.props.callback); }; loadPlugins = (plugins: Plugin[]) => { @@ -87,8 +86,9 @@ class PluginLoader extends React.Component { }; render() { - const { message, finished } = this.state; - if (finished) { + const { loaded } = this.props; + const { message } = this.state; + if (loaded) { return
{this.props.children}
; } return ; diff --git a/scm-ui/src/repos/components/form/RepositoryForm.js b/scm-ui/src/repos/components/form/RepositoryForm.js index 54abf8e08d..8f5d932778 100644 --- a/scm-ui/src/repos/components/form/RepositoryForm.js +++ b/scm-ui/src/repos/components/form/RepositoryForm.js @@ -124,7 +124,7 @@ class RepositoryForm extends React.Component { const { repositoryTypes, t } = this.props; const repository = this.state.repository; return ( -
+ <> { options={this.createSelectOptions(repositoryTypes)} helpText={t("help.typeHelpText")} /> -
+ ); } diff --git a/scm-ui/src/repos/containers/RepositoryRoot.js b/scm-ui/src/repos/containers/RepositoryRoot.js index d0c4947af9..9b8991a91a 100644 --- a/scm-ui/src/repos/containers/RepositoryRoot.js +++ b/scm-ui/src/repos/containers/RepositoryRoot.js @@ -35,6 +35,7 @@ import PermissionsNavLink from "../components/PermissionsNavLink"; import Sources from "../sources/containers/Sources"; import RepositoryNavLink from "../components/RepositoryNavLink"; import { getRepositoriesLink } from "../../modules/indexResource"; +import {ExtensionPoint} from '@scm-manager/ui-extensions'; type Props = { namespace: string, @@ -104,6 +105,12 @@ class RepositoryRoot extends React.Component { } const url = this.matchedUrl(); + + const extensionProps = { + repository, + url + }; + return (
@@ -165,6 +172,10 @@ class RepositoryRoot extends React.Component { /> )} /> +
@@ -186,11 +197,15 @@ class RepositoryRoot extends React.Component { label={t("repository-root.sources")} activeOnlyWhenExact={false} /> - + +
diff --git a/scm-webapp/src/main/java/sonia/scm/ManagerDaoAdapter.java b/scm-webapp/src/main/java/sonia/scm/ManagerDaoAdapter.java index 7979eca0e7..fb5cd9f5cc 100644 --- a/scm-webapp/src/main/java/sonia/scm/ManagerDaoAdapter.java +++ b/scm-webapp/src/main/java/sonia/scm/ManagerDaoAdapter.java @@ -30,19 +30,19 @@ public class ManagerDaoAdapter { afterUpdate.handle(notModified); } else { - throw new NotFoundException(); + throw new NotFoundException(object.getClass(), object.getId()); } } - public T create(T newObject, Supplier permissionCheck, AroundHandler beforeCreate, AroundHandler afterCreate) throws AlreadyExistsException { + public T create(T newObject, Supplier permissionCheck, AroundHandler beforeCreate, AroundHandler afterCreate) { return create(newObject, permissionCheck, beforeCreate, afterCreate, dao::contains); } - public T create(T newObject, Supplier permissionCheck, AroundHandler beforeCreate, AroundHandler afterCreate, Predicate existsCheck) throws AlreadyExistsException { + public T create(T newObject, Supplier permissionCheck, AroundHandler beforeCreate, AroundHandler afterCreate, Predicate existsCheck) { permissionCheck.get().check(); AssertUtil.assertIsValid(newObject); if (existsCheck.test(newObject)) { - throw new AlreadyExistsException(); + throw new AlreadyExistsException(newObject); } newObject.setCreationDate(System.currentTimeMillis()); beforeCreate.handle(newObject); @@ -58,7 +58,7 @@ public class ManagerDaoAdapter { dao.delete(toDelete); afterDelete.handle(toDelete); } else { - throw new NotFoundException(); + throw new NotFoundException(toDelete.getClass(), toDelete.getId()); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/FallbackExceptionMapper.java b/scm-webapp/src/main/java/sonia/scm/api/FallbackExceptionMapper.java new file mode 100644 index 0000000000..feb5341e2d --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/FallbackExceptionMapper.java @@ -0,0 +1,43 @@ +package sonia.scm.api; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; +import sonia.scm.api.v2.resources.ErrorDto; +import sonia.scm.api.v2.resources.ExceptionWithContextToErrorDtoMapper; +import sonia.scm.web.VndMediaType; + +import javax.inject.Inject; +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; +import java.util.Collections; + +@Provider +public class FallbackExceptionMapper implements ExceptionMapper { + + private static final Logger logger = LoggerFactory.getLogger(FallbackExceptionMapper.class); + + private static final String ERROR_CODE = "CmR8GCJb31"; + + private final ExceptionWithContextToErrorDtoMapper mapper; + + @Inject + public FallbackExceptionMapper(ExceptionWithContextToErrorDtoMapper mapper) { + this.mapper = mapper; + } + + @Override + public Response toResponse(Exception exception) { + logger.debug("map {} to status code 500", exception); + ErrorDto errorDto = new ErrorDto(); + errorDto.setMessage("internal server error"); + errorDto.setContext(Collections.emptyList()); + errorDto.setErrorCode(ERROR_CODE); + errorDto.setTransactionId(MDC.get("transaction_id")); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity(errorDto) + .type(VndMediaType.ERROR_TYPE) + .build(); + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/AlreadyExistsExceptionMapper.java b/scm-webapp/src/main/java/sonia/scm/api/rest/AlreadyExistsExceptionMapper.java index 13d341e1e6..65e4426fde 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/AlreadyExistsExceptionMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/AlreadyExistsExceptionMapper.java @@ -1,18 +1,16 @@ package sonia.scm.api.rest; import sonia.scm.AlreadyExistsException; +import sonia.scm.api.v2.resources.ExceptionWithContextToErrorDtoMapper; -import javax.ws.rs.core.Response; +import javax.inject.Inject; import javax.ws.rs.core.Response.Status; -import javax.ws.rs.ext.ExceptionMapper; import javax.ws.rs.ext.Provider; @Provider -public class AlreadyExistsExceptionMapper implements ExceptionMapper { - @Override - public Response toResponse(AlreadyExistsException exception) { - return Response.status(Status.CONFLICT) - .entity(exception.getMessage()) - .build(); +public class AlreadyExistsExceptionMapper extends ContextualExceptionMapper { + @Inject + public AlreadyExistsExceptionMapper(ExceptionWithContextToErrorDtoMapper mapper) { + super(AlreadyExistsException.class, Status.CONFLICT, mapper); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/AuthenticationExceptionMapper.java b/scm-webapp/src/main/java/sonia/scm/api/rest/AuthenticationExceptionMapper.java new file mode 100644 index 0000000000..6ba9984190 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/AuthenticationExceptionMapper.java @@ -0,0 +1,13 @@ +package sonia.scm.api.rest; + +import org.apache.shiro.authc.AuthenticationException; + +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.Provider; + +@Provider +public class AuthenticationExceptionMapper extends StatusExceptionMapper { + public AuthenticationExceptionMapper() { + super(AuthenticationException.class, Response.Status.UNAUTHORIZED); + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/ConcurrentModificationExceptionMapper.java b/scm-webapp/src/main/java/sonia/scm/api/rest/ConcurrentModificationExceptionMapper.java index d01b2fd7de..edcb48c91b 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/ConcurrentModificationExceptionMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/ConcurrentModificationExceptionMapper.java @@ -1,15 +1,16 @@ package sonia.scm.api.rest; import sonia.scm.ConcurrentModificationException; +import sonia.scm.api.v2.resources.ExceptionWithContextToErrorDtoMapper; +import javax.inject.Inject; import javax.ws.rs.core.Response; -import javax.ws.rs.ext.ExceptionMapper; import javax.ws.rs.ext.Provider; @Provider -public class ConcurrentModificationExceptionMapper implements ExceptionMapper { - @Override - public Response toResponse(ConcurrentModificationException exception) { - return Response.status(Response.Status.CONFLICT).build(); +public class ConcurrentModificationExceptionMapper extends ContextualExceptionMapper { + @Inject + public ConcurrentModificationExceptionMapper(ExceptionWithContextToErrorDtoMapper mapper) { + super(ConcurrentModificationException.class, Response.Status.CONFLICT, mapper); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/ContextualExceptionMapper.java b/scm-webapp/src/main/java/sonia/scm/api/rest/ContextualExceptionMapper.java new file mode 100644 index 0000000000..9584a1ea2c --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/ContextualExceptionMapper.java @@ -0,0 +1,39 @@ +package sonia.scm.api.rest; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import sonia.scm.ExceptionWithContext; +import sonia.scm.api.v2.resources.ExceptionWithContextToErrorDtoMapper; +import sonia.scm.web.VndMediaType; + +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; + +public class ContextualExceptionMapper implements ExceptionMapper { + + private static final Logger logger = LoggerFactory.getLogger(ContextualExceptionMapper.class); + + private final ExceptionWithContextToErrorDtoMapper mapper; + + private final Response.Status status; + private final Class type; + + public ContextualExceptionMapper(Class type, Response.Status status, ExceptionWithContextToErrorDtoMapper mapper) { + this.mapper = mapper; + this.type = type; + this.status = status; + } + + @Override + public Response toResponse(E exception) { + if (logger.isTraceEnabled()) { + logger.trace("map {} to status code {}", type.getSimpleName(), status.getStatusCode(), exception); + } else { + logger.debug("map {} to status code {} with message '{}'", type.getSimpleName(), status.getStatusCode(), exception.getMessage()); + } + return Response.status(status) + .entity(mapper.map(exception)) + .type(VndMediaType.ERROR_TYPE) + .build(); + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/IllegalArgumentExceptionMapper.java b/scm-webapp/src/main/java/sonia/scm/api/rest/IllegalArgumentExceptionMapper.java deleted file mode 100644 index ff29a770cf..0000000000 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/IllegalArgumentExceptionMapper.java +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright (c) 2010, Sebastian Sdorra All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. 2. Redistributions in - * binary form must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other - * materials provided with the distribution. 3. Neither the name of SCM-Manager; - * nor the names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ - - - -package sonia.scm.api.rest; - -//~--- JDK imports ------------------------------------------------------------ - -import lombok.extern.slf4j.Slf4j; - -import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.Status; -import javax.ws.rs.ext.ExceptionMapper; -import javax.ws.rs.ext.Provider; - -/** - * - * @author Sebastian Sdorra - * @since 1.36 - */ -@Provider -@Slf4j -public class IllegalArgumentExceptionMapper - implements ExceptionMapper -{ - - /** - * Method description - * - * - * @param exception - * - * @return - */ - @Override - public Response toResponse(IllegalArgumentException exception) - { - log.info("caught IllegalArgumentException -- mapping to bad request", exception); - return Response.status(Status.BAD_REQUEST).entity(exception.getMessage()).build(); - } -} diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/NotAuthorizedExceptionMapper.java b/scm-webapp/src/main/java/sonia/scm/api/rest/NotAuthorizedExceptionMapper.java new file mode 100644 index 0000000000..fee3803de7 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/NotAuthorizedExceptionMapper.java @@ -0,0 +1,13 @@ +package sonia.scm.api.rest; + +import javax.ws.rs.NotAuthorizedException; +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.Provider; + +@Provider +public class NotAuthorizedExceptionMapper extends StatusExceptionMapper { + public NotAuthorizedExceptionMapper() + { + super(NotAuthorizedException.class, Response.Status.UNAUTHORIZED); + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/BrowserStreamingOutput.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/BrowserStreamingOutput.java index 2c78df627b..d2ce744c19 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/BrowserStreamingOutput.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/BrowserStreamingOutput.java @@ -2,8 +2,6 @@ package sonia.scm.api.rest.resources; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sonia.scm.repository.PathNotFoundException; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.repository.api.CatCommandBuilder; import sonia.scm.repository.api.RepositoryService; import sonia.scm.util.IOUtil; @@ -34,18 +32,6 @@ public class BrowserStreamingOutput implements StreamingOutput { public void write(OutputStream output) throws IOException { try { builder.retriveContent(output, path); - } catch (PathNotFoundException ex) { - if (logger.isWarnEnabled()) { - logger.warn("could not find path {}", ex.getPath()); - } - - throw new WebApplicationException(Response.Status.NOT_FOUND); - } catch (RevisionNotFoundException ex) { - if (logger.isWarnEnabled()) { - logger.warn("could not find revision {}", ex.getRevision()); - } - - throw new WebApplicationException(Response.Status.NOT_FOUND); } finally { IOUtil.close(repositoryService); } diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/ChangePasswordResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/ChangePasswordResource.java index 6592bfab8b..828cbf8164 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/ChangePasswordResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/ChangePasswordResource.java @@ -44,8 +44,6 @@ import org.apache.shiro.authc.credential.PasswordService; import org.apache.shiro.subject.Subject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sonia.scm.ConcurrentModificationException; -import sonia.scm.NotFoundException; import sonia.scm.api.rest.RestActionResult; import sonia.scm.security.Role; import sonia.scm.security.ScmSecurityException; diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/ConfigurationResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/ConfigurationResource.java deleted file mode 100644 index a9beea7679..0000000000 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/ConfigurationResource.java +++ /dev/null @@ -1,148 +0,0 @@ -/** - * Copyright (c) 2010, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ - - - -package sonia.scm.api.rest.resources; - -//~--- non-JDK imports -------------------------------------------------------- - -import com.google.inject.Inject; -import com.google.inject.Singleton; - -import org.apache.shiro.SecurityUtils; -import org.apache.shiro.subject.Subject; - -import sonia.scm.config.ScmConfiguration; -import sonia.scm.security.Role; -import sonia.scm.security.ScmSecurityException; -import sonia.scm.util.ScmConfigurationUtil; - -//~--- JDK imports ------------------------------------------------------------ - -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; - -/** - * - * @author Sebastian Sdorra - */ -@Singleton -@Path("config") -public class ConfigurationResource -{ - - /** - * Constructs ... - * - * - * @param configuration - * @param securityContextProvider - */ - @Inject - public ConfigurationResource(ScmConfiguration configuration) - { - this.configuration = configuration; - } - - //~--- get methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @return - */ - @GET - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response getConfiguration() - { - Response response = null; - - if (SecurityUtils.getSubject().hasRole(Role.ADMIN)) - { - response = Response.ok(configuration).build(); - } - else - { - response = Response.status(Response.Status.FORBIDDEN).build(); - } - - return response; - } - - //~--- set methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @param uriInfo - * @param newConfig - * - * @return - */ - @POST - @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response setConfig(@Context UriInfo uriInfo, - ScmConfiguration newConfig) - { - - // TODO replace by checkRole - Subject subject = SecurityUtils.getSubject(); - - if (!subject.hasRole(Role.ADMIN)) - { - throw new ScmSecurityException("admin privileges required"); - } - - configuration.load(newConfig); - - synchronized (ScmConfiguration.class) - { - ScmConfigurationUtil.getInstance().store(configuration); - } - - return Response.created(uriInfo.getRequestUri()).build(); - } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - public ScmConfiguration configuration; -} diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/DiffStreamingOutput.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/DiffStreamingOutput.java index 1e452eeee2..d177e05a5e 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/DiffStreamingOutput.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/DiffStreamingOutput.java @@ -37,7 +37,6 @@ package sonia.scm.api.rest.resources; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.repository.api.DiffCommandBuilder; import sonia.scm.repository.api.RepositoryService; import sonia.scm.util.IOUtil; @@ -95,22 +94,6 @@ public class DiffStreamingOutput implements StreamingOutput { builder.retrieveContent(output); } - catch (RevisionNotFoundException ex) - { - if (logger.isWarnEnabled()) - { - logger.warn("could not find revision {}", ex.getRevision()); - } - - throw new WebApplicationException(Response.Status.NOT_FOUND); - } -// catch (RepositoryException ex) -// { -// logger.error("could not write content to page", ex); -// -// throw new WebApplicationException(ex, -// Response.Status.INTERNAL_SERVER_ERROR); -// } finally { IOUtil.close(repositoryService); diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/GroupResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/GroupResource.java deleted file mode 100644 index a560cc278e..0000000000 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/GroupResource.java +++ /dev/null @@ -1,248 +0,0 @@ -/** - * Copyright (c) 2010, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ - - - -package sonia.scm.api.rest.resources; - -//~--- non-JDK imports -------------------------------------------------------- - -import com.google.inject.Inject; -import com.google.inject.Singleton; -import com.webcohesion.enunciate.metadata.rs.ResponseCode; -import com.webcohesion.enunciate.metadata.rs.ResponseHeader; -import com.webcohesion.enunciate.metadata.rs.StatusCodes; -import com.webcohesion.enunciate.metadata.rs.TypeHint; -import org.apache.shiro.SecurityUtils; -import sonia.scm.group.Group; -import sonia.scm.group.GroupManager; -import sonia.scm.security.Role; - -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.DefaultValue; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.GenericEntity; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Request; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; -import java.util.Collection; - -//~--- JDK imports ------------------------------------------------------------ - -/** - * RESTful Web Service Resource to manage groups and their members. - * - * @author Sebastian Sdorra - */ -@Path("groups") -@Singleton -public class GroupResource extends AbstractManagerResource { - - /** Field description */ - public static final String PATH_PART = "groups"; - - //~--- constructors --------------------------------------------------------- - - @Inject - public GroupResource(GroupManager groupManager) - { - super(groupManager, Group.class); - } - - //~--- methods -------------------------------------------------------------- - - /** - * Creates a new group. Note: This method requires admin privileges. - * - * @param uriInfo current uri informations - * @param group the group to be created - * - * @return - */ - @POST - @StatusCodes({ - @ResponseCode(code = 201, condition = "create success", additionalHeaders = { - @ResponseHeader(name = "Location", description = "uri to the created group") - }), - @ResponseCode(code = 403, condition = "forbidden, the current user has no admin privileges"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(TypeHint.NO_CONTENT.class) - @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - @Override - public Response create(@Context UriInfo uriInfo, Group group) - { - return super.create(uriInfo, group); - } - - /** - * Deletes a group. Note: This method requires admin privileges. - * - * @param name the name of the group to delete. - * - * @return - */ - @DELETE - @Path("{id}") - @StatusCodes({ - @ResponseCode(code = 204, condition = "delete success"), - @ResponseCode(code = 403, condition = "forbidden, the current user has no admin privileges"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(TypeHint.NO_CONTENT.class) - @Override - public Response delete(@PathParam("id") String name) - { - return super.delete(name); - } - - /** - * Modifies the given group. Note: This method requires admin privileges. - * - * @param name name of the group to be modified - * @param group group object to modify - * - * @return - */ - @PUT - @Path("{id}") - @StatusCodes({ - @ResponseCode(code = 204, condition = "update success"), - @ResponseCode(code = 403, condition = "forbidden, the current user has no admin privileges"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(TypeHint.NO_CONTENT.class) - @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - @Override - public Response update(@PathParam("id") String name, Group group) - { - return super.update(name, group); - } - - //~--- get methods ---------------------------------------------------------- - - /** - * Fetches a group by its name or id. Note: This method requires admin privileges. - * - * @param request the current request - * @param id the id/name of the group - * - * @return the {@link Group} with the specified id - */ - @GET - @Path("{id}") - @TypeHint(Group.class) - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 403, condition = "forbidden, the current user has no admin privileges"), - @ResponseCode(code = 404, condition = "not found, no group with the specified id/name available"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - @Override - public Response get(@Context Request request, @PathParam("id") String id) - { - Response response = null; - - if (SecurityUtils.getSubject().hasRole(Role.ADMIN)) - { - response = super.get(request, id); - } - else - { - response = Response.status(Response.Status.FORBIDDEN).build(); - } - - return response; - } - - /** - * Returns all groups. Note: This method requires admin privileges. - * - * @param request the current request - * @param start the start value for paging - * @param limit the limit value for paging - * @param sortby sort parameter - * @param desc sort direction desc or aesc - * - * @return - */ - @GET - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - @TypeHint(Group[].class) - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 403, condition = "forbidden, the current user has no admin privileges"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @Override - public Response getAll(@Context Request request, @DefaultValue("0") - @QueryParam("start") int start, @DefaultValue("-1") - @QueryParam("limit") int limit, @QueryParam("sortby") String sortby, - @DefaultValue("false") - @QueryParam("desc") boolean desc) - { - return super.getAll(request, start, limit, sortby, desc); - } - - //~--- methods -------------------------------------------------------------- - - @Override - protected GenericEntity> createGenericEntity( - Collection items) - { - return new GenericEntity>(items) {} - ; - } - - //~--- get methods ---------------------------------------------------------- - - @Override - protected String getId(Group group) - { - return group.getName(); - } - - @Override - protected String getPathPart() - { - return PATH_PART; - } -} diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/PluginResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/PluginResource.java deleted file mode 100644 index 4a770206eb..0000000000 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/PluginResource.java +++ /dev/null @@ -1,362 +0,0 @@ -/** - * Copyright (c) 2010, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ - - - -package sonia.scm.api.rest.resources; - -//~--- non-JDK imports -------------------------------------------------------- - -import com.google.common.collect.Lists; -import com.google.inject.Inject; -import com.google.inject.Singleton; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import sonia.scm.api.rest.RestActionResult; -import sonia.scm.api.rest.RestActionUploadResult; -import sonia.scm.plugin.OverviewPluginPredicate; -import sonia.scm.plugin.PluginConditionFailedException; -import sonia.scm.plugin.PluginInformation; -import sonia.scm.plugin.PluginInformationComparator; -import sonia.scm.plugin.PluginManager; - -//~--- JDK imports ------------------------------------------------------------ - -import com.webcohesion.enunciate.metadata.rs.ResponseCode; -import com.webcohesion.enunciate.metadata.rs.StatusCodes; -import com.webcohesion.enunciate.metadata.rs.TypeHint; - -import java.io.IOException; -import java.io.InputStream; - -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; - -import javax.ws.rs.Consumes; -import javax.ws.rs.FormParam; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.Status; - -/** - * RESTful Web Service Endpoint to manage plugins. - * - * @author Sebastian Sdorra - */ -@Singleton -@Path("plugins") -public class PluginResource -{ - - /** - * the logger for PluginResource - */ - private static final Logger logger = - LoggerFactory.getLogger(PluginResource.class); - - //~--- constructors --------------------------------------------------------- - - /** - * Constructs ... - * - * - * @param pluginManager - */ - @Inject - public PluginResource(PluginManager pluginManager) - { - this.pluginManager = pluginManager; - } - - //~--- methods -------------------------------------------------------------- - - /** - * Installs a plugin from a package. - * - * @param uploadedInputStream - * - * @return - * - * @throws IOException - */ - @POST - @Path("install-package") - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 412, condition = "precondition failed"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @Consumes(MediaType.MULTIPART_FORM_DATA) - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response install( - /*@FormParam("package")*/ InputStream uploadedInputStream) - throws IOException - { - Response response = null; - - try - { - pluginManager.installPackage(uploadedInputStream); - response = Response.ok(new RestActionUploadResult(true)).build(); - } - catch (PluginConditionFailedException ex) - { - logger.warn( - "could not install plugin package, because the condition failed", ex); - response = Response.status(Status.PRECONDITION_FAILED).entity( - new RestActionResult(false)).build(); - } - catch (Exception ex) - { - logger.warn("plugin installation failed", ex); - response = - Response.serverError().entity(new RestActionResult(false)).build(); - } - - return response; - } - - /** - * Installs a plugin. - * - * @param id id of the plugin to be installed - * - * @return - */ - @POST - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(TypeHint.NO_CONTENT.class) - @Path("install/{id}") - public Response install(@PathParam("id") String id) - { - pluginManager.install(id); - - // TODO should return 204 no content - return Response.ok().build(); - } - - /** - * Installs a plugin from a package. This method is a workaround for ExtJS - * file upload, which requires text/html as content-type. - * - * @param uploadedInputStream - * @return - * - * @throws IOException - */ - @POST - @Path("install-package.html") - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 412, condition = "precondition failed"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @Consumes(MediaType.MULTIPART_FORM_DATA) - @Produces(MediaType.TEXT_HTML) - public Response installFromUI( - /*@FormParam("package")*/ InputStream uploadedInputStream) - throws IOException - { - return install(uploadedInputStream); - } - - /** - * Uninstalls a plugin. - * - * @param id id of the plugin to be uninstalled - * - * @return - */ - @POST - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @Path("uninstall/{id}") - public Response uninstall(@PathParam("id") String id) - { - pluginManager.uninstall(id); - - // TODO should return 204 content - // consider to do a uninstall with a delete - return Response.ok().build(); - } - - /** - * Updates a plugin. - * - * @param id id of the plugin to be updated - * - * @return - */ - @POST - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @Path("update/{id}") - public Response update(@PathParam("id") String id) - { - pluginManager.update(id); - - // TODO should return 204 content - // consider to do an update with a put - - return Response.ok().build(); - } - - //~--- get methods ---------------------------------------------------------- - - /** - * Returns all plugins. - * - * @return all plugins - */ - @GET - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Collection getAll() - { - return pluginManager.getAll(); - } - - /** - * Returns all available plugins. - * - * @return all available plugins - */ - @GET - @Path("available") - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Collection getAvailable() - { - return pluginManager.getAvailable(); - } - - /** - * Returns all plugins which are available for update. - * - * @return all plugins which are available for update - */ - @GET - @Path("updates") - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Collection getAvailableUpdates() - { - return pluginManager.getAvailableUpdates(); - } - - /** - * Returns all installed plugins. - * - * @return all installed plugins - */ - @GET - @Path("installed") - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) - public Collection getInstalled() - { - return pluginManager.getInstalled(); - } - - /** - * Returns all plugins for the overview. - * - * @return all plugins for the overview - */ - @GET - @Path("overview") - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Collection getOverview() - { - //J- - List plugins = Lists.newArrayList( - pluginManager.get(OverviewPluginPredicate.INSTANCE) - ); - //J+ - - Collections.sort(plugins, PluginInformationComparator.INSTANCE); - - Iterator it = plugins.iterator(); - String last = null; - - while (it.hasNext()) - { - PluginInformation pi = it.next(); - String id = pi.getId(false); - - if ((last != null) && id.equals(last)) - { - it.remove(); - } - - last = id; - } - - return plugins; - } - - //~--- fields --------------------------------------------------------------- - - /** plugin manager */ - private final PluginManager pluginManager; -} diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryImportResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryImportResource.java index 8a5ba8b1e3..6bf6c8e803 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryImportResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryImportResource.java @@ -45,11 +45,11 @@ import com.webcohesion.enunciate.metadata.rs.TypeHint; import org.apache.shiro.SecurityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sonia.scm.AlreadyExistsException; import sonia.scm.NotFoundException; -import sonia.scm.NotSupportedFeatuerException; +import sonia.scm.NotSupportedFeatureException; import sonia.scm.Type; import sonia.scm.api.rest.RestActionUploadResult; +import sonia.scm.api.v2.resources.RepositoryResource; import sonia.scm.repository.*; import sonia.scm.repository.api.Command; import sonia.scm.repository.api.RepositoryService; @@ -394,7 +394,7 @@ public class RepositoryImportResource response = Response.ok(result).build(); } - catch (NotSupportedFeatuerException ex) + catch (NotSupportedFeatureException ex) { logger .warn( @@ -515,13 +515,6 @@ public class RepositoryImportResource // repository = new Repository(null, type, name); manager.create(repository); } - catch (AlreadyExistsException ex) - { - logger.warn("a {} repository with the name {} already exists", type, - name); - - throw new WebApplicationException(Response.Status.CONFLICT); - } catch (InternalRepositoryException ex) { handleGenericCreationFailure(ex, type, name); @@ -616,7 +609,7 @@ public class RepositoryImportResource types.add(t); } } - catch (NotSupportedFeatuerException ex) + catch (NotSupportedFeatureException ex) { if (logger.isTraceEnabled()) { @@ -718,7 +711,7 @@ public class RepositoryImportResource } } } - catch (NotSupportedFeatuerException ex) + catch (NotSupportedFeatureException ex) { throw new WebApplicationException(ex, Response.Status.BAD_REQUEST); } diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryResource.java deleted file mode 100644 index 4a9fd9e38f..0000000000 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryResource.java +++ /dev/null @@ -1,1067 +0,0 @@ -/** - * Copyright (c) 2010, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ - - - -package sonia.scm.api.rest.resources; - -//~--- non-JDK imports -------------------------------------------------------- - -import com.google.common.base.Strings; -import com.google.inject.Inject; -import com.google.inject.Singleton; -import com.webcohesion.enunciate.metadata.rs.ResponseCode; -import com.webcohesion.enunciate.metadata.rs.ResponseHeader; -import com.webcohesion.enunciate.metadata.rs.StatusCodes; -import com.webcohesion.enunciate.metadata.rs.TypeHint; -import org.apache.shiro.SecurityUtils; -import org.apache.shiro.authz.AuthorizationException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import sonia.scm.NotFoundException; -import sonia.scm.repository.BlameResult; -import sonia.scm.repository.Branches; -import sonia.scm.repository.BrowserResult; -import sonia.scm.repository.Changeset; -import sonia.scm.repository.ChangesetPagingResult; -import sonia.scm.repository.HealthChecker; -import sonia.scm.repository.Permission; -import sonia.scm.repository.Repository; -import sonia.scm.repository.RepositoryIsNotArchivedException; -import sonia.scm.repository.RepositoryManager; -import sonia.scm.repository.RepositoryNotFoundException; -import sonia.scm.repository.Tags; -import sonia.scm.repository.api.BlameCommandBuilder; -import sonia.scm.repository.api.BrowseCommandBuilder; -import sonia.scm.repository.api.CatCommandBuilder; -import sonia.scm.repository.api.CommandNotSupportedException; -import sonia.scm.repository.api.DiffCommandBuilder; -import sonia.scm.repository.api.DiffFormat; -import sonia.scm.repository.api.LogCommandBuilder; -import sonia.scm.repository.api.RepositoryService; -import sonia.scm.repository.api.RepositoryServiceFactory; -import sonia.scm.util.AssertUtil; -import sonia.scm.util.HttpUtil; -import sonia.scm.util.IOUtil; -import sonia.scm.util.Util; - -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.DefaultValue; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.GenericEntity; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Request; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.Status; -import javax.ws.rs.core.StreamingOutput; -import javax.ws.rs.core.UriInfo; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; - -//~--- JDK imports ------------------------------------------------------------ - -/** - * Repository related RESTful Web Service Endpoint. - * - * @author Sebastian Sdorra - */ -@Singleton -@Path("repositories") -public class RepositoryResource extends AbstractManagerResource -{ - - /** Field description */ - public static final String PATH_PART = "repositories"; - - /** the logger for RepositoryResource */ - private static final Logger logger = - LoggerFactory.getLogger(RepositoryResource.class); - - //~--- constructors --------------------------------------------------------- - - /** - * Constructs ... - * - * @param repositoryManager - * @param servicefactory - * @param healthChecker - */ - @Inject - public RepositoryResource(RepositoryManager repositoryManager, - RepositoryServiceFactory servicefactory, HealthChecker healthChecker) - { - super(repositoryManager, Repository.class); - this.repositoryManager = repositoryManager; - this.servicefactory = servicefactory; - this.healthChecker = healthChecker; - setDisableCache(false); - } - - //~--- methods -------------------------------------------------------------- - - /** - * Creates a new repository.Note: This method requires admin privileges. - * - * @param uriInfo current uri informations - * @param repository the repository to be created - * - * @return empty response with location header to the new repository - */ - @POST - @StatusCodes({ - @ResponseCode(code = 201, condition = "success", additionalHeaders = { - @ResponseHeader(name = "Location", description = "uri to the new created repository") - }), - @ResponseCode(code = 403, condition = "forbidden, the current user has no admin privileges"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(TypeHint.NO_CONTENT.class) - @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - @Override - public Response create(@Context UriInfo uriInfo, Repository repository) - { - return super.create(uriInfo, repository); - } - - /** - * Deletes a repository. Note: This method requires owner privileges. - * - * @param id the id of the repository to delete. - * - * @return - */ - @DELETE - @Path("{id}") - @StatusCodes({ - @ResponseCode(code = 204, condition = "delete success"), - @ResponseCode(code = 403, condition = "forbidden, the current user has no owner privileges"), - @ResponseCode(code = 404, condition = "could not find repository"), - @ResponseCode( - code = 412, - condition = "precondition failed, the repository is not archived, this error occurs only with enabled repository archive" - ), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(TypeHint.NO_CONTENT.class) - @Override - public Response delete(@PathParam("id") String id) - { - Response response; - Repository repository = manager.get(id); - - if (repository != null) - { - preDelete(repository); - - try - { - manager.delete(repository); - response = Response.noContent().build(); - } - catch (RepositoryIsNotArchivedException ex) - { - logger.warn("non archived repository could not be deleted", ex); - response = Response.status(Response.Status.PRECONDITION_FAILED).build(); - } - catch (AuthorizationException ex) - { - logger.warn("delete not allowed", ex); - response = Response.status(Response.Status.FORBIDDEN).build(); - } catch (NotFoundException e) { - // there is nothing to do because delete should be idempotent - response = Response.ok().build(); - } -// catch (IOException ex) -// { -// logger.error("error during delete", ex); -// response = Response.serverError().build(); -// } - } - else - { - logger.warn("could not find repository {}", id); - response = Response.status(Status.NOT_FOUND).build(); - } - - return response; - } - - /** - * Re run repository health checks. - * - * @param id id of the repository - * - * @return - */ - @POST - @StatusCodes({ - @ResponseCode(code = 200, condition = "re run success"), - @ResponseCode(code = 403, condition = "forbidden, the current user has no owner privileges"), - @ResponseCode(code = 404, condition = "could not find repository"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(TypeHint.NO_CONTENT.class) - @Path("{id}/healthcheck") - public Response runHealthChecks(@PathParam("id") String id) - { - Response response; - - try - { - healthChecker.check(id); - // TODO should return 204 instead of 200 - response = Response.ok().build(); - } - catch (RepositoryNotFoundException ex) - { - logger.warn("could not find repository ".concat(id), ex); - response = Response.status(Status.NOT_FOUND).build(); - } catch (NotFoundException e) { - logger.error("error occured during health check", e); - response = Response.serverError().build(); - } - - return response; - } - - /** - * Modifies the given repository. Note: This method requires owner privileges. - * - * @param id id of the repository to be modified - * @param repository repository object to modify - * - * @return - */ - @PUT - @Path("{id}") - @StatusCodes({ - @ResponseCode(code = 204, condition = "update successful"), - @ResponseCode(code = 403, condition = "forbidden, the current user has no owner privileges"), - @ResponseCode(code = 404, condition = "could not find repository"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(TypeHint.NO_CONTENT.class) - @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - @Override - public Response update(@PathParam("id") String id, Repository repository) - { - return super.update(id, repository); - } - - //~--- get methods ---------------------------------------------------------- - - /** - * Returns the {@link Repository} with the specified id. - * - * @param request the current request - * @param id the id/name of the user - * - * @return the {@link Repository} with the specified id - */ - @GET - @Path("{id}") - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 404, condition = "not found, no repository with the specified id available"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(Repository.class) - @Override - public Response get(@Context Request request, @PathParam("id") String id) - { - return super.get(request, id); - } - - /** - * Returns all repositories. - * - * @param request the current request - * @param start the start value for paging - * @param limit the limit value for paging - * @param sortby sort parameter - * @param desc sort direction desc or aesc - * - * @return all repositories - */ - @GET - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(Repository[].class) - @Override - public Response getAll(@Context Request request, @DefaultValue("0") - @QueryParam("start") int start, @DefaultValue("-1") - @QueryParam("limit") int limit, @QueryParam("sortby") String sortby, - @DefaultValue("false") - @QueryParam("desc") boolean desc) - { - return super.getAll(request, start, limit, sortby, desc); - } - - /** - * Returns a annotate/blame view for the given path. - * - * @param id the id of the repository - * @param revision the revision of the file - * @param path the path of the file - * - * @return a annotate/blame view for the given path - * - * @throws IOException - */ - @GET - @Path("{id}/blame") - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 400, condition = "bad request, the blame feature is not supported by this type of repositories."), - @ResponseCode(code = 404, condition = "not found, the repository or the path could not be found"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(BlameResult.class) - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response getBlame(@PathParam("id") String id, - @QueryParam("revision") String revision, @QueryParam("path") String path) - throws IOException - { - Response response = null; - RepositoryService service = null; - - try - { - AssertUtil.assertIsNotNull(path); - service = servicefactory.create(id); - - BlameCommandBuilder builder = service.getBlameCommand(); - - if (!Strings.isNullOrEmpty(revision)) - { - builder.setRevision(revision); - } - - BlameResult blamePagingResult = builder.getBlameResult(path); - - if (blamePagingResult != null) - { - response = Response.ok(blamePagingResult).build(); - } - else - { - response = Response.ok().build(); - } - } - catch (IllegalStateException ex) - { - response = Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); - } - catch (RepositoryNotFoundException ex) - { - response = Response.status(Response.Status.NOT_FOUND).build(); - } - catch (CommandNotSupportedException ex) - { - response = Response.status(Response.Status.BAD_REQUEST).build(); - } - finally - { - IOUtil.close(service); - } - - return response; - } - - /** - * Returns all {@link Branches} of a repository. - * - * @param id the id of the repository - * - * @return all {@link Branches} of a repository - * - * @throws IOException - * - * @since 1.18 - */ - @GET - @Path("{id}/branches") - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 400, condition = "bad request, the branch feature is not supported by this type of repositories."), - @ResponseCode(code = 404, condition = "not found, the repository could not be found"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(Branches.class) - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response getBranches(@PathParam("id") String id) - throws IOException - { - Response response = null; - RepositoryService service = null; - - try - { - service = servicefactory.create(id); - - Branches branches = service.getBranchesCommand().getBranches(); - - if (branches != null) - { - response = Response.ok(branches).build(); - } - else - { - response = Response.status(Status.NOT_FOUND).build(); - } - } - catch (RepositoryNotFoundException ex) - { - response = Response.status(Response.Status.NOT_FOUND).build(); - } - catch (CommandNotSupportedException ex) - { - response = Response.status(Response.Status.BAD_REQUEST).build(); - } - finally - { - IOUtil.close(service); - } - - return response; - } - - /** - * Returns a list of folders and files for the given folder. - * - * @param id the id of the repository - * @param revision the revision of the file - * @param path the path of the folder - * @param disableLastCommit true disables fetch of last commit message - * @param disableSubRepositoryDetection true disables sub repository detection - * @param recursive true to enable recursive browsing - * - * @return a list of folders and files for the given folder - * - * @throws IOException - */ - @GET - @Path("{id}/browse") - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 400, condition = "bad request, the browse feature is not supported by this type of repositories."), - @ResponseCode(code = 404, condition = "not found, the repository or the path could not be found"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(BrowserResult.class) - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - //J- - public Response getBrowserResult( - @PathParam("id") String id, - @QueryParam("revision") String revision, - @QueryParam("path") String path, - @QueryParam("disableLastCommit") @DefaultValue("false") boolean disableLastCommit, - @QueryParam("disableSubRepositoryDetection") @DefaultValue("false") boolean disableSubRepositoryDetection, - @QueryParam("recursive") @DefaultValue("false") boolean recursive) - throws IOException - //J+ - { - Response response = null; - RepositoryService service = null; - - try - { - service = servicefactory.create(id); - - BrowseCommandBuilder builder = service.getBrowseCommand(); - - if (!Strings.isNullOrEmpty(revision)) - { - builder.setRevision(revision); - } - - if (!Strings.isNullOrEmpty(path)) - { - builder.setPath(path); - } - - //J- - builder.setDisableLastCommit(disableLastCommit) - .setDisableSubRepositoryDetection(disableSubRepositoryDetection) - .setRecursive(recursive); - //J+ - - BrowserResult result = builder.getBrowserResult(); - - if (result != null) - { - response = Response.ok(result).build(); - } - else - { - response = Response.status(Response.Status.NOT_FOUND).build(); - } - } - catch (NotFoundException ex) - { - response = Response.status(Response.Status.NOT_FOUND).build(); - } - catch (CommandNotSupportedException ex) - { - response = Response.status(Response.Status.BAD_REQUEST).build(); - } - finally - { - IOUtil.close(service); - } - - return response; - } - - /** - * Returns the {@link Changeset} from the given repository - * with the specified revision. - * - * @param id the id of the repository - * @param revision the revision of the changeset - * - * @return a {@link Changeset} from the given repository - * - * @throws IOException - */ - @GET - @Path("{id}/changeset/{revision}") - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 400, condition = "bad request, the changeset feature is not supported by this type of repositories."), - @ResponseCode(code = 404, condition = "not found, the repository or the revision could not be found"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(Changeset.class) - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response getChangeset(@PathParam("id") String id, - @PathParam("revision") String revision) - throws IOException - { - Response response = null; - - if (Util.isNotEmpty(id) && Util.isNotEmpty(revision)) - { - RepositoryService service = null; - - try - { - service = servicefactory.create(id); - - Changeset changeset = service.getLogCommand().getChangeset(revision); - - if (changeset != null) - { - response = Response.ok(changeset).build(); - } - else - { - response = Response.status(Status.NOT_FOUND).build(); - } - } - catch (NotFoundException ex) - { - response = Response.status(Response.Status.NOT_FOUND).build(); - } - catch (CommandNotSupportedException ex) - { - response = Response.status(Response.Status.BAD_REQUEST).build(); - } - finally - { - IOUtil.close(service); - } - } - else - { - logger.warn("id or revision is empty"); - response = Response.status(Status.BAD_REQUEST).build(); - } - - return response; - } - - /** - * Returns a list of {@link Changeset} for the given repository. - * - * @param id the id of the repository - * @param path path of a file - * @param revision the revision of the file specified by the path parameter - * @param branch name of the branch - * @param start the start value for paging - * @param limit the limit value for paging - * - * @return a list of {@link Changeset} for the given repository - * - * @throws IOException - */ - @GET - @Path("{id}/changesets") - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 400, condition = "bad request, the changeset feature is not supported by this type of repositories."), - @ResponseCode(code = 404, condition = "not found, the repository or the path could not be found"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(ChangesetPagingResult.class) - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - //J- - public Response getChangesets( - @PathParam("id") String id, - @QueryParam("path") String path, - @QueryParam("revision") String revision, - @QueryParam("branch") String branch, - @DefaultValue("0") @QueryParam("start") int start, - @DefaultValue("20") @QueryParam("limit") int limit - ) throws IOException - //J+ - { - Response response = null; - RepositoryService service = null; - - try - { - ChangesetPagingResult changesets; - - service = servicefactory.create(id); - - LogCommandBuilder builder = service.getLogCommand(); - - if (!Strings.isNullOrEmpty(path)) - { - builder.setPath(path); - } - - if (!Strings.isNullOrEmpty(revision)) - { - builder.setStartChangeset(revision); - } - - if (!Strings.isNullOrEmpty(branch)) - { - builder.setBranch(branch); - } - - changesets = - builder.setPagingStart(start).setPagingLimit(limit).getChangesets(); - - if (changesets != null) - { - response = Response.ok(changesets).build(); - } - else - { - response = Response.ok().build(); - } - } - catch (NotFoundException ex) - { - response = Response.status(Response.Status.NOT_FOUND).build(); - } - catch (CommandNotSupportedException ex) - { - response = Response.status(Response.Status.BAD_REQUEST).build(); - } - finally - { - IOUtil.close(service); - } - - return response; - } - - /** - * Returns the content of a file. - * - * @param id the id of the repository - * @param revision the revision of the file - * @param path path to the file - * - * @return the content of a file - */ - @GET - @Path("{id}/content") - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 400, condition = "bad request, the content feature is not supported by this type of repositories."), - @ResponseCode(code = 404, condition = "not found, the repository or the path could not be found"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(StreamingOutput.class) - @Produces({ MediaType.APPLICATION_OCTET_STREAM }) - public Response getContent(@PathParam("id") String id, - @QueryParam("revision") String revision, @QueryParam("path") String path) - { - Response response; - StreamingOutput output; - RepositoryService service; - - try - { - service = servicefactory.create(id); - - CatCommandBuilder builder = service.getCatCommand(); - - if (!Strings.isNullOrEmpty(revision)) - { - builder.setRevision(revision); - } - - output = new BrowserStreamingOutput(service, builder, path); - - /** - * protection for crlf injection - * see https://bitbucket.org/sdorra/scm-manager/issue/320/crlf-injection-vulnerability-in-diff-api - */ - path = HttpUtil.removeCRLFInjectionChars(path); - - String contentDispositionName = getContentDispositionNameFromPath(path); - - response = Response.ok(output).header("Content-Disposition", - contentDispositionName).build(); - } - catch (RepositoryNotFoundException ex) - { - logger.warn("could not find repository browser for respository {}", id); - response = Response.status(Response.Status.NOT_FOUND).build(); - } - catch (CommandNotSupportedException ex) - { - response = Response.status(Response.Status.BAD_REQUEST).build(); - } - catch (Exception ex) - { - logger.error("could not retrive content", ex); - response = createErrorResponse(ex); - } - - return response; - } - - /** - * Returns the modifications of a {@link Changeset}. - * - * @param id the id of the repository - * @param revision the revision of the file - * @param path path to the file - * @param format - * - * @return the modifications of a {@link Changeset} - * - * @throws IOException - */ - @GET - @Path("{id}/diff") - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 400, condition = "bad request, the diff feature is not supported by this type of repositories."), - @ResponseCode(code = 404, condition = "not found, the repository or the path could not be found"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(DiffStreamingOutput.class) - @Produces(MediaType.APPLICATION_OCTET_STREAM) - public Response getDiff(@PathParam("id") String id, - @QueryParam("revision") String revision, @QueryParam("path") String path, - @QueryParam("format") DiffFormat format) - throws IOException - { - AssertUtil.assertIsNotEmpty(id); - AssertUtil.assertIsNotEmpty(revision); - - /** - * check for a crlf injection attack - * see https://bitbucket.org/sdorra/scm-manager/issue/320/crlf-injection-vulnerability-in-diff-api - */ - HttpUtil.checkForCRLFInjection(revision); - - RepositoryService service; - Response response; - - try - { - service = servicefactory.create(id); - - DiffCommandBuilder builder = service.getDiffCommand(); - - if (!Strings.isNullOrEmpty(revision)) - { - builder.setRevision(revision); - } - - if (!Strings.isNullOrEmpty(path)) - { - builder.setPath(path); - } - - if (format != null) - { - builder.setFormat(format); - } - - String name = service.getRepository().getName().concat("-").concat( - revision).concat(".diff"); - String contentDispositionName = getContentDispositionName(name); - - response = Response.ok(new DiffStreamingOutput(service, - builder)).header("Content-Disposition", contentDispositionName).build(); - } - catch (RepositoryNotFoundException ex) - { - response = Response.status(Response.Status.NOT_FOUND).build(); - } - catch (CommandNotSupportedException ex) - { - response = Response.status(Response.Status.BAD_REQUEST).build(); - } - catch (Exception ex) - { - logger.error("could not create diff", ex); - response = createErrorResponse(ex); - } - - return response; - } - - /** - * Returns all {@link Tags} of a repository. - * - * @param id the id of the repository - * - * @return all {@link Tags} of a repository - * - * @throws IOException - * - * @since 1.18 - */ - @GET - @Path("{id}/tags") - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 400, condition = "bad request, the tag feature is not supported by this type of repositories."), - @ResponseCode(code = 404, condition = "not found, the repository could not be found"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(Tags.class) - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - public Response getTags(@PathParam("id") String id) - throws IOException - { - Response response = null; - RepositoryService service = null; - - try - { - service = servicefactory.create(id); - - Tags tags = service.getTagsCommand().getTags(); - - if (tags != null) - { - response = Response.ok(tags).build(); - } - else - { - response = Response.status(Status.NOT_FOUND).build(); - } - } - catch (RepositoryNotFoundException ex) - { - response = Response.status(Response.Status.NOT_FOUND).build(); - } - catch (CommandNotSupportedException ex) - { - response = Response.status(Response.Status.BAD_REQUEST).build(); - } - finally - { - IOUtil.close(service); - } - - return response; - } - - //~--- methods -------------------------------------------------------------- - - /** - * Method description - * - * - * @param items - * - * @return - */ - @Override - protected GenericEntity> createGenericEntity( - Collection items) - { - return new GenericEntity>(items) {} - ; - } - - /** - * Method description - * - * - * - * @param repositories - * @return - */ - @Override - protected Collection prepareForReturn( - Collection repositories) - { - for (Repository repository : repositories) - { - prepareForReturn(repository); - } - - return repositories; - } - - /** - * Method description - * - * - * @param repository - * - * @return - */ - @Override - protected Repository prepareForReturn(Repository repository) - { - if (SecurityUtils.getSubject().isPermitted( - "repository:modify:".concat(repository.getId()))) - { - if (repository.getPermissions() == null) - { - repository.setPermissions(new ArrayList()); - } - } - else - { - logger.trace("remove properties and permissions from repository, " - + "because the user is not privileged"); - - repository.setProperties(null); - repository.setPermissions(null); - repository.setHealthCheckFailures(null); - } - - return repository; - } - - //~--- get methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @param item - * - * @return - */ - @Override - protected String getId(Repository item) - { - return item.getId(); - } - - /** - * Method description - * - * - * @return - */ - @Override - protected String getPathPart() - { - return PATH_PART; - } - - /** - * Method description - * - * - * - * @param name - * - * @return - */ - private String getContentDispositionName(String name) - { - return HttpUtil.createContentDispositionAttachmentHeader(name); - } - - /** - * Method description - * - * - * @param path - * - * @return - */ - private String getContentDispositionNameFromPath(String path) - { - String name = path; - int index = path.lastIndexOf('/'); - - if (index >= 0) - { - name = path.substring(index + 1); - } - - return getContentDispositionName(name); - } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private final HealthChecker healthChecker; - - /** Field description */ - private final RepositoryManager repositoryManager; - - /** Field description */ - private final RepositoryServiceFactory servicefactory; -} diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/UserResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/UserResource.java deleted file mode 100644 index e054c4c32f..0000000000 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/UserResource.java +++ /dev/null @@ -1,319 +0,0 @@ -/** - * Copyright (c) 2010, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ - - - -package sonia.scm.api.rest.resources; - -//~--- non-JDK imports -------------------------------------------------------- - -import com.google.inject.Inject; -import com.google.inject.Singleton; -import com.webcohesion.enunciate.metadata.rs.ResponseCode; -import com.webcohesion.enunciate.metadata.rs.ResponseHeader; -import com.webcohesion.enunciate.metadata.rs.StatusCodes; -import com.webcohesion.enunciate.metadata.rs.TypeHint; -import org.apache.shiro.SecurityUtils; -import org.apache.shiro.authc.credential.PasswordService; -import sonia.scm.security.Role; -import sonia.scm.user.User; -import sonia.scm.user.UserManager; -import sonia.scm.util.AssertUtil; -import sonia.scm.util.Util; - -import javax.ws.rs.DELETE; -import javax.ws.rs.DefaultValue; -import javax.ws.rs.GET; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.GenericEntity; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Request; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; -import java.util.Collection; - -//~--- JDK imports ------------------------------------------------------------ - -/** - * RESTful Web Service Resource to manage users. - * - * @author Sebastian Sdorra - */ -@Singleton -@Path("users") -public class UserResource extends AbstractManagerResource -{ - - /** Field description */ - public static final String DUMMY_PASSWORT = "__dummypassword__"; - - /** Field description */ - public static final String PATH_PART = "users"; - - //~--- constructors --------------------------------------------------------- - - /** - * Constructs ... - * - * - * @param userManager - * @param passwordService - */ - @Inject - public UserResource(UserManager userManager, PasswordService passwordService) - { - super(userManager, User.class); - this.passwordService = passwordService; - } - - //~--- methods -------------------------------------------------------------- - - /** - * Creates a new user. Note: This method requires admin privileges. - * - * @param uriInfo current uri informations - * @param user the user to be created - * - * @return - */ - @POST - @StatusCodes({ - @ResponseCode(code = 201, condition = "create success", additionalHeaders = { - @ResponseHeader(name = "Location", description = "uri to the created group") - }), - @ResponseCode(code = 403, condition = "forbidden, the current user has no admin privileges"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(TypeHint.NO_CONTENT.class) - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - @Override - public Response create(@Context UriInfo uriInfo, User user) - { - return super.create(uriInfo, user); - } - - /** - * Deletes a user. Note: This method requires admin privileges. - * - * @param name the name of the user to delete. - * - * @return - */ - @DELETE - @Path("{id}") - @StatusCodes({ - @ResponseCode(code = 204, condition = "delete success"), - @ResponseCode(code = 403, condition = "forbidden, the current user has no admin privileges"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(TypeHint.NO_CONTENT.class) - @Override - public Response delete(@PathParam("id") String name) - { - return super.delete(name); - } - - /** - * Modifies the given user. Note: This method requires admin privileges. - * - * @param name name of the user to be modified - * @param user user object to modify - * - * @return - */ - @PUT - @Path("{id}") - @StatusCodes({ - @ResponseCode(code = 204, condition = "update success"), - @ResponseCode(code = 403, condition = "forbidden, the current user has no admin privileges"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @TypeHint(TypeHint.NO_CONTENT.class) - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - @Override - public Response update(@PathParam("id") String name, User user) - { - return super.update(name, user); - } - - //~--- get methods ---------------------------------------------------------- - - /** - * Returns a user. Note: This method requires admin privileges. - * - * @param request the current request - * @param id the id/name of the user - * - * @return the {@link User} with the specified id - */ - @GET - @Path("{id}") - @TypeHint(User.class) - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 403, condition = "forbidden, the current user has no admin privileges"), - @ResponseCode(code = 404, condition = "not found, no group with the specified id/name available"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - @Override - public Response get(@Context Request request, @PathParam("id") String id) - { - Response response = null; - - if (SecurityUtils.getSubject().hasRole(Role.ADMIN)) - { - response = super.get(request, id); - } - else - { - response = Response.status(Response.Status.FORBIDDEN).build(); - } - - return response; - } - - /** - * Returns all users. Note: This method requires admin privileges. - * - * @param request the current request - * @param start the start value for paging - * @param limit the limit value for paging - * @param sortby sort parameter - * @param desc sort direction desc or aesc - * - * @return - */ - @GET - @TypeHint(User[].class) - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 403, condition = "forbidden, the current user has no admin privileges"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) - @Override - public Response getAll(@Context Request request, @DefaultValue("0") - @QueryParam("start") int start, @DefaultValue("-1") - @QueryParam("limit") int limit, @QueryParam("sortby") String sortby, - @DefaultValue("false") - @QueryParam("desc") boolean desc) - { - return super.getAll(request, start, limit, sortby, desc); - } - - //~--- methods -------------------------------------------------------------- - - @Override - protected GenericEntity> createGenericEntity( - Collection items) - { - return new GenericEntity>(items) {} - ; - } - - @Override - protected void preCreate(User user) - { - encryptPassword(user); - } - - @Override - protected void preUpdate(User user) - { - if (DUMMY_PASSWORT.equals(user.getPassword())) - { - User o = manager.get(user.getName()); - - AssertUtil.assertIsNotNull(o); - user.setPassword(o.getPassword()); - } - else - { - encryptPassword(user); - } - } - - @Override - protected Collection prepareForReturn(Collection users) - { - if (Util.isNotEmpty(users)) - { - for (User u : users) - { - u.setPassword(DUMMY_PASSWORT); - } - } - - return users; - } - - @Override - protected User prepareForReturn(User user) - { - user.setPassword(DUMMY_PASSWORT); - - return user; - } - - @Override - protected String getId(User user) - { - return user.getName(); - } - - @Override - protected String getPathPart() - { - return PATH_PART; - } - - private void encryptPassword(User user) - { - String password = user.getPassword(); - - if (Util.isNotEmpty(password)) - { - user.setPassword(passwordService.encryptPassword(password)); - } - } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private PasswordService passwordService; -} diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/NotFoundExceptionMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/NotFoundExceptionMapper.java similarity index 79% rename from scm-webapp/src/main/java/sonia/scm/api/v2/resources/NotFoundExceptionMapper.java rename to scm-webapp/src/main/java/sonia/scm/api/v2/NotFoundExceptionMapper.java index 04264a7629..944ec276ce 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/NotFoundExceptionMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/NotFoundExceptionMapper.java @@ -28,12 +28,14 @@ */ -package sonia.scm.api.v2.resources; +package sonia.scm.api.v2; import sonia.scm.NotFoundException; -import sonia.scm.api.rest.StatusExceptionMapper; +import sonia.scm.api.rest.ContextualExceptionMapper; +import sonia.scm.api.v2.resources.ExceptionWithContextToErrorDtoMapper; +import javax.inject.Inject; import javax.ws.rs.core.Response; import javax.ws.rs.ext.Provider; @@ -41,9 +43,9 @@ import javax.ws.rs.ext.Provider; * @since 2.0.0 */ @Provider -public class NotFoundExceptionMapper extends StatusExceptionMapper { - - public NotFoundExceptionMapper() { - super(NotFoundException.class, Response.Status.NOT_FOUND); +public class NotFoundExceptionMapper extends ContextualExceptionMapper { + @Inject + public NotFoundExceptionMapper(ExceptionWithContextToErrorDtoMapper mapper) { + super(NotFoundException.class, Response.Status.NOT_FOUND, mapper); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/NotSupportedFeatureExceptionMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/NotSupportedFeatureExceptionMapper.java new file mode 100644 index 0000000000..6a48663aa5 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/NotSupportedFeatureExceptionMapper.java @@ -0,0 +1,17 @@ +package sonia.scm.api.v2; + +import sonia.scm.NotSupportedFeatureException; +import sonia.scm.api.rest.ContextualExceptionMapper; +import sonia.scm.api.v2.resources.ExceptionWithContextToErrorDtoMapper; + +import javax.inject.Inject; +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.Provider; + +@Provider +public class NotSupportedFeatureExceptionMapper extends ContextualExceptionMapper { + @Inject + public NotSupportedFeatureExceptionMapper(ExceptionWithContextToErrorDtoMapper mapper) { + super(NotSupportedFeatureException.class, Response.Status.BAD_REQUEST, mapper); + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/ValidationExceptionMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/ValidationExceptionMapper.java index c6af6b8921..6fadce8500 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/ValidationExceptionMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/ValidationExceptionMapper.java @@ -1,58 +1,30 @@ package sonia.scm.api.v2; -import lombok.Getter; import org.jboss.resteasy.api.validation.ResteasyViolationException; +import sonia.scm.api.v2.resources.ViolationExceptionToErrorDtoMapper; -import javax.validation.ConstraintViolation; +import javax.inject.Inject; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.ext.ExceptionMapper; import javax.ws.rs.ext.Provider; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlElementWrapper; -import javax.xml.bind.annotation.XmlRootElement; -import java.util.List; -import java.util.stream.Collectors; @Provider public class ValidationExceptionMapper implements ExceptionMapper { + private final ViolationExceptionToErrorDtoMapper mapper; + + @Inject + public ValidationExceptionMapper(ViolationExceptionToErrorDtoMapper mapper) { + this.mapper = mapper; + } + @Override public Response toResponse(ResteasyViolationException exception) { - - List violations = - exception.getConstraintViolations() - .stream() - .map(ConstraintViolationBean::new) - .collect(Collectors.toList()); - return Response .status(Response.Status.BAD_REQUEST) .type(MediaType.APPLICATION_JSON_TYPE) - .entity(new ValidationError(violations)) + .entity(mapper.map(exception)) .build(); } - - @Getter - public static class ValidationError { - @XmlElement(name = "violation") - @XmlElementWrapper(name = "violations") - private List violations; - - public ValidationError(List violations) { - this.violations = violations; - } - } - - @XmlRootElement(name = "violation") - @Getter - public static class ConstraintViolationBean { - private String path; - private String message; - - public ConstraintViolationBean(ConstraintViolation violation) { - message = violation.getMessage(); - path = violation.getPropertyPath().toString(); - } - } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AuthenticationResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AuthenticationResource.java index ffe0ce51d0..47918080ab 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AuthenticationResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AuthenticationResource.java @@ -18,6 +18,7 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; @Path(AuthenticationResource.PATH) +@AllowAnonymousAccess public class AuthenticationResource { private static final Logger LOG = LoggerFactory.getLogger(AuthenticationResource.class); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchRootResource.java index 431e39e759..a0d73ad24e 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchRootResource.java @@ -11,7 +11,6 @@ import sonia.scm.repository.Changeset; import sonia.scm.repository.ChangesetPagingResult; import sonia.scm.repository.NamespaceAndName; import sonia.scm.repository.Repository; -import sonia.scm.repository.RepositoryNotFoundException; import sonia.scm.repository.RepositoryPermissions; import sonia.scm.repository.api.CommandNotSupportedException; import sonia.scm.repository.api.RepositoryService; @@ -29,6 +28,9 @@ import javax.ws.rs.core.Response; import java.io.IOException; import java.util.List; +import static sonia.scm.ContextEntry.ContextBuilder.entity; +import static sonia.scm.NotFoundException.notFound; + public class BranchRootResource { private final RepositoryServiceFactory serviceFactory; @@ -79,7 +81,7 @@ public class BranchRootResource { .build(); } catch (CommandNotSupportedException ex) { return Response.status(Response.Status.BAD_REQUEST).build(); - } catch (RepositoryNotFoundException e) { + } catch (NotFoundException e) { return Response.status(Response.Status.NOT_FOUND).build(); } } @@ -99,7 +101,7 @@ public class BranchRootResource { @PathParam("name") String name, @PathParam("branch") String branchName, @DefaultValue("0") @QueryParam("page") int page, - @DefaultValue("10") @QueryParam("pageSize") int pageSize) throws Exception { + @DefaultValue("10") @QueryParam("pageSize") int pageSize) throws IOException { try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) { boolean branchExists = repositoryService.getBranchesCommand() .getBranches() @@ -107,7 +109,7 @@ public class BranchRootResource { .stream() .anyMatch(branch -> branchName.equals(branch.getName())); if (!branchExists){ - throw new NotFoundException("branch", branchName); + throw notFound(entity(Branch.class, branchName).in(Repository.class, namespace + "/" + name)); } Repository repository = repositoryService.getRepository(); RepositoryPermissions.read(repository).check(); @@ -195,8 +197,6 @@ public class BranchRootResource { return Response.ok(branchCollectionToDtoMapper.map(namespace, name, branches.getBranches())).build(); } catch (CommandNotSupportedException ex) { return Response.status(Response.Status.BAD_REQUEST).build(); - } catch (RepositoryNotFoundException e) { - return Response.status(Response.Status.NOT_FOUND).build(); } } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangePasswordNotAllowedExceptionMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangePasswordNotAllowedExceptionMapper.java index e9bb5304a5..18a6e6e75c 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangePasswordNotAllowedExceptionMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangePasswordNotAllowedExceptionMapper.java @@ -1,17 +1,17 @@ package sonia.scm.api.v2.resources; +import sonia.scm.api.rest.ContextualExceptionMapper; import sonia.scm.user.ChangePasswordNotAllowedException; +import sonia.scm.user.InvalidPasswordException; +import javax.inject.Inject; import javax.ws.rs.core.Response; -import javax.ws.rs.ext.ExceptionMapper; import javax.ws.rs.ext.Provider; @Provider -public class ChangePasswordNotAllowedExceptionMapper implements ExceptionMapper { - @Override - public Response toResponse(ChangePasswordNotAllowedException exception) { - return Response.status(Response.Status.BAD_REQUEST) - .entity(exception.getMessage()) - .build(); +public class ChangePasswordNotAllowedExceptionMapper extends ContextualExceptionMapper { + @Inject + public ChangePasswordNotAllowedExceptionMapper(ExceptionWithContextToErrorDtoMapper mapper) { + super(ChangePasswordNotAllowedException.class, Response.Status.BAD_REQUEST, mapper); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetRootResource.java index 57c0bb8712..0766816d4d 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetRootResource.java @@ -9,9 +9,7 @@ import sonia.scm.repository.Changeset; import sonia.scm.repository.ChangesetPagingResult; import sonia.scm.repository.NamespaceAndName; import sonia.scm.repository.Repository; -import sonia.scm.repository.RepositoryNotFoundException; import sonia.scm.repository.RepositoryPermissions; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.repository.api.RepositoryService; import sonia.scm.repository.api.RepositoryServiceFactory; import sonia.scm.web.VndMediaType; @@ -56,7 +54,7 @@ public class ChangesetRootResource { @Produces(VndMediaType.CHANGESET_COLLECTION) @TypeHint(CollectionDto.class) public Response getAll(@PathParam("namespace") String namespace, @PathParam("name") String name, @DefaultValue("0") @QueryParam("page") int page, - @DefaultValue("10") @QueryParam("pageSize") int pageSize) throws IOException, RevisionNotFoundException, RepositoryNotFoundException { + @DefaultValue("10") @QueryParam("pageSize") int pageSize) throws IOException { try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) { Repository repository = repositoryService.getRepository(); RepositoryPermissions.read(repository).check(); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/CollectionResourceManagerAdapter.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/CollectionResourceManagerAdapter.java index 3ef19f7294..052bf771b1 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/CollectionResourceManagerAdapter.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/CollectionResourceManagerAdapter.java @@ -1,7 +1,6 @@ package sonia.scm.api.v2.resources; import de.otto.edison.hal.HalRepresentation; -import sonia.scm.AlreadyExistsException; import sonia.scm.Manager; import sonia.scm.ModelObject; import sonia.scm.PageResult; @@ -47,7 +46,7 @@ class CollectionResourceManagerAdapter modelObjectSupplier, Function uriCreator) throws AlreadyExistsException { + public Response create(DTO dto, Supplier modelObjectSupplier, Function uriCreator) { if (dto == null) { return Response.status(BAD_REQUEST).build(); } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ContentResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ContentResource.java index 99ed29e6f6..9f99efdea2 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ContentResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ContentResource.java @@ -6,10 +6,8 @@ import com.webcohesion.enunciate.metadata.rs.ResponseCode; import com.webcohesion.enunciate.metadata.rs.StatusCodes; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import sonia.scm.NotFoundException; import sonia.scm.repository.NamespaceAndName; -import sonia.scm.repository.PathNotFoundException; -import sonia.scm.repository.RepositoryNotFoundException; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.repository.api.RepositoryService; import sonia.scm.repository.api.RepositoryServiceFactory; import sonia.scm.util.IOUtil; @@ -64,8 +62,8 @@ public class ContentResource { try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) { Response.ResponseBuilder responseBuilder = Response.ok(stream); return createContentHeader(namespace, name, revision, path, repositoryService, responseBuilder); - } catch (RepositoryNotFoundException e) { - LOG.debug("path '{}' not found in repository {}/{}", path, namespace, name, e); + } catch (NotFoundException e) { + LOG.debug(e.getMessage()); return Response.status(Status.NOT_FOUND).build(); } } @@ -75,14 +73,8 @@ public class ContentResource { try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) { repositoryService.getCatCommand().setRevision(revision).retriveContent(os, path); os.close(); - } catch (RepositoryNotFoundException e) { - LOG.debug("repository {}/{} not found", path, namespace, name, e); - throw new WebApplicationException(Status.NOT_FOUND); - } catch (PathNotFoundException e) { - LOG.debug("path '{}' not found in repository {}/{}", path, namespace, name, e); - throw new WebApplicationException(Status.NOT_FOUND); - } catch (RevisionNotFoundException e) { - LOG.debug("revision '{}' not found in repository {}/{}", revision, namespace, name, e); + } catch (NotFoundException e) { + LOG.debug(e.getMessage()); throw new WebApplicationException(Status.NOT_FOUND); } }; @@ -111,8 +103,8 @@ public class ContentResource { try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) { Response.ResponseBuilder responseBuilder = Response.ok(); return createContentHeader(namespace, name, revision, path, repositoryService, responseBuilder); - } catch (RepositoryNotFoundException e) { - LOG.debug("path '{}' not found in repository {}/{}", path, namespace, name, e); + } catch (NotFoundException e) { + LOG.debug(e.getMessage()); return Response.status(Status.NOT_FOUND).build(); } } @@ -120,12 +112,6 @@ public class ContentResource { private Response createContentHeader(String namespace, String name, String revision, String path, RepositoryService repositoryService, Response.ResponseBuilder responseBuilder) { try { appendContentHeader(path, getHead(revision, path, repositoryService), responseBuilder); - } catch (PathNotFoundException e) { - LOG.debug("path '{}' not found in repository {}/{}", path, namespace, name, e); - return Response.status(Status.NOT_FOUND).build(); - } catch (RevisionNotFoundException e) { - LOG.debug("revision '{}' not found in repository {}/{}", revision, namespace, name, e); - return Response.status(Status.NOT_FOUND).build(); } catch (IOException e) { LOG.info("error reading repository resource {} from {}/{}", path, namespace, name, e); return Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build(); @@ -139,7 +125,7 @@ public class ContentResource { contentType.getLanguage().ifPresent(language -> responseBuilder.header("X-Programming-Language", language)); } - private byte[] getHead(String revision, String path, RepositoryService repositoryService) throws IOException, PathNotFoundException, RevisionNotFoundException { + private byte[] getHead(String revision, String path, RepositoryService repositoryService) throws IOException { InputStream stream = repositoryService.getCatCommand().setRevision(revision).getStream(path); try { byte[] buffer = new byte[HEAD_BUFFER_SIZE]; diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/DiffRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/DiffRootResource.java index 6db23a7fce..e236b54005 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/DiffRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/DiffRootResource.java @@ -4,7 +4,6 @@ import com.webcohesion.enunciate.metadata.rs.ResponseCode; import com.webcohesion.enunciate.metadata.rs.StatusCodes; import sonia.scm.NotFoundException; import sonia.scm.repository.NamespaceAndName; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.repository.api.DiffFormat; import sonia.scm.repository.api.RepositoryService; import sonia.scm.repository.api.RepositoryServiceFactory; @@ -19,7 +18,6 @@ import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; -import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Response; import javax.ws.rs.core.StreamingOutput; @@ -62,14 +60,10 @@ public class DiffRootResource { DiffFormat diffFormat = DiffFormat.valueOf(format); try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) { StreamingOutput responseEntry = output -> { - try { - repositoryService.getDiffCommand() - .setRevision(revision) + repositoryService.getDiffCommand() + .setRevision(revision) .setFormat(diffFormat) - .retrieveContent(output); - } catch (RevisionNotFoundException e) { - throw new WebApplicationException(Response.Status.NOT_FOUND); - } + .retrieveContent(output); }; return Response.ok(responseEntry) .header(HEADER_CONTENT_DISPOSITION, HttpUtil.createContentDispositionAttachmentHeader(String.format("%s-%s.diff", name, revision))) diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ErrorDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ErrorDto.java new file mode 100644 index 0000000000..bd889d5de5 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ErrorDto.java @@ -0,0 +1,33 @@ +package sonia.scm.api.v2.resources; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Getter; +import lombok.Setter; +import sonia.scm.ContextEntry; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; +import javax.xml.bind.annotation.XmlRootElement; +import java.util.List; + +@Getter @Setter +public class ErrorDto { + private String transactionId; + private String errorCode; + private List context; + private String message; + + @JsonInclude(JsonInclude.Include.NON_NULL) + @XmlElementWrapper(name = "violations") + private List violations; + + @JsonInclude(JsonInclude.Include.NON_NULL) + private String url; + + @XmlRootElement(name = "violation") + @Getter @Setter + public static class ConstraintViolationDto { + private String path; + private String message; + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ExceptionWithContextToErrorDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ExceptionWithContextToErrorDtoMapper.java new file mode 100644 index 0000000000..cdd545542a --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ExceptionWithContextToErrorDtoMapper.java @@ -0,0 +1,23 @@ +package sonia.scm.api.v2.resources; + +import org.mapstruct.AfterMapping; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; +import org.slf4j.MDC; +import sonia.scm.ExceptionWithContext; + +@Mapper +public abstract class ExceptionWithContextToErrorDtoMapper { + + @Mapping(target = "errorCode", source = "code") + @Mapping(target = "transactionId", ignore = true) + @Mapping(target = "violations", ignore = true) + @Mapping(target = "url", ignore = true) + public abstract ErrorDto map(ExceptionWithContext exception); + + @AfterMapping + void setTransactionId(@MappingTarget ErrorDto dto) { + dto.setTransactionId(MDC.get("transaction_id")); + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileHistoryRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileHistoryRootResource.java index e38a6f699a..017568b613 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileHistoryRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileHistoryRootResource.java @@ -7,11 +7,8 @@ import lombok.extern.slf4j.Slf4j; import sonia.scm.PageResult; import sonia.scm.repository.Changeset; import sonia.scm.repository.ChangesetPagingResult; -import sonia.scm.repository.InternalRepositoryException; import sonia.scm.repository.NamespaceAndName; import sonia.scm.repository.Repository; -import sonia.scm.repository.RepositoryNotFoundException; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.repository.api.RepositoryService; import sonia.scm.repository.api.RepositoryServiceFactory; import sonia.scm.web.VndMediaType; @@ -26,6 +23,9 @@ import javax.ws.rs.QueryParam; import javax.ws.rs.core.Response; import java.io.IOException; +import static sonia.scm.ContextEntry.ContextBuilder.entity; +import static sonia.scm.NotFoundException.notFound; + @Slf4j public class FileHistoryRootResource { @@ -51,8 +51,6 @@ public class FileHistoryRootResource { * @param pageSize pagination * @return all changesets related to the given file starting with the given revision * @throws IOException on io error - * @throws RevisionNotFoundException on missing revision - * @throws RepositoryNotFoundException on missing repository */ @GET @Path("{revision}/{path: .*}") @@ -69,8 +67,9 @@ public class FileHistoryRootResource { @PathParam("revision") String revision, @PathParam("path") String path, @DefaultValue("0") @QueryParam("page") int page, - @DefaultValue("10") @QueryParam("pageSize") int pageSize) throws IOException, RevisionNotFoundException, RepositoryNotFoundException { - try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) { + @DefaultValue("10") @QueryParam("pageSize") int pageSize) throws IOException { + NamespaceAndName namespaceAndName = new NamespaceAndName(namespace, name); + try (RepositoryService repositoryService = serviceFactory.create(namespaceAndName)) { log.info("Get changesets of the file {} and revision {}", path, revision); Repository repository = repositoryService.getRepository(); ChangesetPagingResult changesets = new PagedLogCommandBuilder(repositoryService) @@ -84,9 +83,9 @@ public class FileHistoryRootResource { PageResult pageResult = new PageResult<>(changesets.getChangesets(), changesets.getTotal()); return Response.ok(fileHistoryCollectionToDtoMapper.map(page, pageSize, pageResult, repository, revision, path)).build(); } else { - String message = String.format("for the revision %s and the file %s there is no changesets", revision, path); + String message = String.format("for the revision %s and the file %s there are no changesets", revision, path); log.error(message); - throw new InternalRepositoryException(message); + throw notFound(entity("path", path).in("revision", revision).in(namespaceAndName)); } } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupCollectionResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupCollectionResource.java index 8abab0f720..4c111e6707 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupCollectionResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupCollectionResource.java @@ -5,12 +5,12 @@ import com.webcohesion.enunciate.metadata.rs.ResponseHeader; import com.webcohesion.enunciate.metadata.rs.ResponseHeaders; import com.webcohesion.enunciate.metadata.rs.StatusCodes; import com.webcohesion.enunciate.metadata.rs.TypeHint; -import sonia.scm.AlreadyExistsException; import sonia.scm.group.Group; import sonia.scm.group.GroupManager; import sonia.scm.web.VndMediaType; import javax.inject.Inject; +import javax.inject.Named; import javax.validation.Valid; import javax.ws.rs.Consumes; import javax.ws.rs.DefaultValue; @@ -71,7 +71,7 @@ public class GroupCollectionResource { /** * Creates a new group. - * @param groupDto The group to be created. + * @param group The group to be created. * @return A response with the link to the new group (if created successfully). */ @POST @@ -86,9 +86,9 @@ public class GroupCollectionResource { }) @TypeHint(TypeHint.NO_CONTENT.class) @ResponseHeaders(@ResponseHeader(name = "Location", description = "uri to the created group")) - public Response create(@Valid GroupDto groupDto) throws AlreadyExistsException { - return adapter.create(groupDto, - () -> dtoToGroupMapper.map(groupDto), - group -> resourceLinks.group().self(group.getName())); + public Response create(@Valid GroupDto group) { + return adapter.create(group, + () -> dtoToGroupMapper.map(group), + g -> resourceLinks.group().self(g.getName())); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupResource.java index 789a9847cb..6d0b921d02 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupResource.java @@ -3,13 +3,12 @@ package sonia.scm.api.v2.resources; import com.webcohesion.enunciate.metadata.rs.ResponseCode; import com.webcohesion.enunciate.metadata.rs.StatusCodes; import com.webcohesion.enunciate.metadata.rs.TypeHint; -import sonia.scm.ConcurrentModificationException; -import sonia.scm.NotFoundException; import sonia.scm.group.Group; import sonia.scm.group.GroupManager; import sonia.scm.web.VndMediaType; import javax.inject.Inject; +import javax.inject.Named; import javax.validation.Valid; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; @@ -84,7 +83,7 @@ public class GroupResource { * Note: This method requires "group" privilege. * * @param name name of the group to be modified - * @param groupDto group object to modify + * @param group group object to modify */ @PUT @Path("") @@ -98,7 +97,7 @@ public class GroupResource { @ResponseCode(code = 500, condition = "internal server error") }) @TypeHint(TypeHint.NO_CONTENT.class) - public Response update(@PathParam("id") String name, @Valid GroupDto groupDto) throws ConcurrentModificationException { - return adapter.update(name, existing -> dtoToGroupMapper.map(groupDto)); + public Response update(@PathParam("id") String name, @Valid GroupDto group) { + return adapter.update(name, existing -> dtoToGroupMapper.map(group)); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IdResourceManagerAdapter.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IdResourceManagerAdapter.java index 9fe3d8284d..2b2a4cf0ad 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IdResourceManagerAdapter.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IdResourceManagerAdapter.java @@ -1,10 +1,9 @@ package sonia.scm.api.v2.resources; import de.otto.edison.hal.HalRepresentation; -import sonia.scm.AlreadyExistsException; -import sonia.scm.ConcurrentModificationException; import sonia.scm.Manager; import sonia.scm.ModelObject; +import sonia.scm.NotFoundException; import sonia.scm.PageResult; import javax.ws.rs.core.Response; @@ -22,6 +21,7 @@ class IdResourceManagerAdapter { private final Manager manager; + private final String type; private final SingleResourceManagerAdapter singleAdapter; private final CollectionResourceManagerAdapter collectionAdapter; @@ -30,13 +30,14 @@ class IdResourceManagerAdapter(manager, type); collectionAdapter = new CollectionResourceManagerAdapter<>(manager, type); + this.type = type.getSimpleName(); } Response get(String id, Function mapToDto) { return singleAdapter.get(loadBy(id), mapToDto); } - public Response update(String id, Function applyChanges) throws ConcurrentModificationException { + public Response update(String id, Function applyChanges) { return singleAdapter.update( loadBy(id), applyChanges, @@ -48,7 +49,7 @@ class IdResourceManagerAdapter modelObjectSupplier, Function uriCreator) throws AlreadyExistsException { + public Response create(DTO dto, Supplier modelObjectSupplier, Function uriCreator) { return collectionAdapter.create(dto, modelObjectSupplier, uriCreator); } @@ -56,8 +57,8 @@ class IdResourceManagerAdapter> loadBy(String id) { - return () -> Optional.ofNullable(manager.get(id)); + private Supplier loadBy(String id) { + return () -> Optional.ofNullable(manager.get(id)).orElseThrow(() -> new NotFoundException(type, id)); } private Predicate idStaysTheSame(String id) { diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexResource.java index 088558c7dc..1eec99ea96 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexResource.java @@ -1,6 +1,7 @@ package sonia.scm.api.v2.resources; import com.webcohesion.enunciate.metadata.rs.TypeHint; +import sonia.scm.security.AllowAnonymousAccess; import sonia.scm.web.VndMediaType; import javax.inject.Inject; @@ -9,6 +10,7 @@ import javax.ws.rs.Path; import javax.ws.rs.Produces; @Path(IndexResource.INDEX_PATH_V2) +@AllowAnonymousAccess public class IndexResource { public static final String INDEX_PATH_V2 = "v2/"; diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/InternalRepositoryExceptionMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/InternalRepositoryExceptionMapper.java index d30677c5f7..856a9310a0 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/InternalRepositoryExceptionMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/InternalRepositoryExceptionMapper.java @@ -1,15 +1,17 @@ package sonia.scm.api.v2.resources; -import sonia.scm.api.rest.StatusExceptionMapper; +import sonia.scm.api.rest.ContextualExceptionMapper; import sonia.scm.repository.InternalRepositoryException; +import javax.inject.Inject; import javax.ws.rs.core.Response; import javax.ws.rs.ext.Provider; @Provider -public class InternalRepositoryExceptionMapper extends StatusExceptionMapper { +public class InternalRepositoryExceptionMapper extends ContextualExceptionMapper { - public InternalRepositoryExceptionMapper() { - super(InternalRepositoryException.class, Response.Status.INTERNAL_SERVER_ERROR); + @Inject + public InternalRepositoryExceptionMapper(ExceptionWithContextToErrorDtoMapper mapper) { + super(InternalRepositoryException.class, Response.Status.INTERNAL_SERVER_ERROR, mapper); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/InvalidPasswordExceptionMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/InvalidPasswordExceptionMapper.java index 7c5364ba03..7a1d311a1c 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/InvalidPasswordExceptionMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/InvalidPasswordExceptionMapper.java @@ -1,17 +1,17 @@ package sonia.scm.api.v2.resources; +import sonia.scm.api.rest.ContextualExceptionMapper; import sonia.scm.user.InvalidPasswordException; +import javax.inject.Inject; import javax.ws.rs.core.Response; -import javax.ws.rs.ext.ExceptionMapper; import javax.ws.rs.ext.Provider; @Provider -public class InvalidPasswordExceptionMapper implements ExceptionMapper { - @Override - public Response toResponse(InvalidPasswordException exception) { - return Response.status(Response.Status.BAD_REQUEST) - .entity(exception.getMessage()) - .build(); +public class InvalidPasswordExceptionMapper extends ContextualExceptionMapper { + + @Inject + public InvalidPasswordExceptionMapper(ExceptionWithContextToErrorDtoMapper mapper) { + super(InvalidPasswordException.class, Response.Status.BAD_REQUEST, mapper); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MapperModule.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MapperModule.java index 6497cb9315..35f58cef90 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MapperModule.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MapperModule.java @@ -39,6 +39,9 @@ public class MapperModule extends AbstractModule { bind(ReducedObjectModelToDtoMapper.class).to(Mappers.getMapper(ReducedObjectModelToDtoMapper.class).getClass()); + bind(ViolationExceptionToErrorDtoMapper.class).to(Mappers.getMapper(ViolationExceptionToErrorDtoMapper.class).getClass()); + bind(ExceptionWithContextToErrorDtoMapper.class).to(Mappers.getMapper(ExceptionWithContextToErrorDtoMapper.class).getClass()); + // no mapstruct required bind(UIPluginDtoMapper.class); bind(UIPluginDtoCollectionMapper.class); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MeResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MeResource.java index f616aff852..20fc35923c 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MeResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MeResource.java @@ -10,6 +10,7 @@ import sonia.scm.user.UserManager; import sonia.scm.web.VndMediaType; import javax.inject.Inject; +import javax.inject.Named; import javax.validation.Valid; import javax.ws.rs.Consumes; import javax.ws.rs.GET; @@ -73,8 +74,8 @@ public class MeResource { }) @TypeHint(TypeHint.NO_CONTENT.class) @Consumes(VndMediaType.PASSWORD_CHANGE) - public Response changePassword(@Valid PasswordChangeDto passwordChangeDto) { - userManager.changePasswordForLoggedInUser(passwordService.encryptPassword(passwordChangeDto.getOldPassword()), passwordService.encryptPassword(passwordChangeDto.getNewPassword())); + public Response changePassword(@Valid PasswordChangeDto passwordChange) { + userManager.changePasswordForLoggedInUser(passwordService.encryptPassword(passwordChange.getOldPassword()), passwordService.encryptPassword(passwordChange.getNewPassword())); return Response.noContent().build(); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ModificationsRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ModificationsRootResource.java index 28f855f40c..eaf165cda1 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ModificationsRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ModificationsRootResource.java @@ -3,11 +3,8 @@ package sonia.scm.api.v2.resources; import com.webcohesion.enunciate.metadata.rs.ResponseCode; import com.webcohesion.enunciate.metadata.rs.StatusCodes; import com.webcohesion.enunciate.metadata.rs.TypeHint; -import sonia.scm.repository.InternalRepositoryException; import sonia.scm.repository.Modifications; import sonia.scm.repository.NamespaceAndName; -import sonia.scm.repository.RepositoryNotFoundException; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.repository.api.RepositoryService; import sonia.scm.repository.api.RepositoryServiceFactory; import sonia.scm.web.VndMediaType; @@ -46,7 +43,7 @@ public class ModificationsRootResource { @Produces(VndMediaType.MODIFICATIONS) @TypeHint(ModificationsDto.class) @Path("{revision}") - public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision) throws IOException, RevisionNotFoundException, RepositoryNotFoundException , InternalRepositoryException { + public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision) throws IOException { try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) { Modifications modifications = repositoryService.getModificationsCommand() .revision(revision) diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionRootResource.java index b1ea912acf..127a3f450e 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionRootResource.java @@ -11,11 +11,11 @@ import sonia.scm.repository.NamespaceAndName; import sonia.scm.repository.Permission; import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryManager; -import sonia.scm.repository.RepositoryNotFoundException; import sonia.scm.repository.RepositoryPermissions; import sonia.scm.web.VndMediaType; import javax.inject.Inject; +import javax.inject.Named; import javax.validation.Valid; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; @@ -30,6 +30,9 @@ import java.net.URI; import java.util.Optional; import java.util.function.Predicate; +import static sonia.scm.AlreadyExistsException.alreadyExists; +import static sonia.scm.ContextEntry.ContextBuilder.entity; +import static sonia.scm.NotFoundException.notFound; import static sonia.scm.api.v2.resources.PermissionDto.GROUP_PREFIX; @Slf4j @@ -71,12 +74,12 @@ public class PermissionRootResource { @TypeHint(TypeHint.NO_CONTENT.class) @Consumes(VndMediaType.PERMISSION) @Path("") - public Response create(@PathParam("namespace") String namespace, @PathParam("name") String name,@Valid PermissionDto permission) throws AlreadyExistsException, NotFoundException { + public Response create(@PathParam("namespace") String namespace, @PathParam("name") String name,@Valid PermissionDto permission) { log.info("try to add new permission: {}", permission); Repository repository = load(namespace, name); RepositoryPermissions.permissionWrite(repository).check(); checkPermissionAlreadyExists(permission, repository); - repository.getPermissions().add(dtoToModelMapper.map(permission)); + repository.addPermission(dtoToModelMapper.map(permission)); manager.modify(repository); String urlPermissionName = modelToDtoMapper.getUrlPermissionName(permission); return Response.created(URI.create(resourceLinks.permission().self(namespace, name, urlPermissionName))).build(); @@ -109,7 +112,7 @@ public class PermissionRootResource { .filter(filterPermission(permissionName)) .map(permission -> modelToDtoMapper.map(permission, repository)) .findFirst() - .orElseThrow(NotFoundException::new) + .orElseThrow(() -> notFound(entity(Permission.class, namespace).in(Repository.class, namespace + "/" + name))) ).build(); } @@ -131,7 +134,7 @@ public class PermissionRootResource { @Produces(VndMediaType.PERMISSION) @TypeHint(PermissionDto.class) @Path("") - public Response getAll(@PathParam("namespace") String namespace, @PathParam("name") String name) throws RepositoryNotFoundException { + public Response getAll(@PathParam("namespace") String namespace, @PathParam("name") String name) { Repository repository = load(namespace, name); RepositoryPermissions.permissionRead(repository).check(); return Response.ok(permissionCollectionToDtoMapper.map(repository)).build(); @@ -158,23 +161,23 @@ public class PermissionRootResource { public Response update(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("permission-name") String permissionName, - @Valid PermissionDto permission) throws AlreadyExistsException { + @Valid PermissionDto permission) { log.info("try to update the permission with name: {}. the modified permission is: {}", permissionName, permission); Repository repository = load(namespace, name); RepositoryPermissions.permissionWrite(repository).check(); String extractedPermissionName = getPermissionName(permissionName); if (!isPermissionExist(new PermissionDto(extractedPermissionName, isGroupPermission(permissionName)), repository)) { - throw new NotFoundException("permission", extractedPermissionName); + throw notFound(entity(Permission.class, namespace).in(Repository.class, namespace + "/" + name)); } permission.setGroupPermission(isGroupPermission(permissionName)); if (!extractedPermissionName.equals(permission.getName())) { - checkPermissionAlreadyExists(permission, repository, "target permission " + permission.getName() + " already exists"); + checkPermissionAlreadyExists(permission, repository); } Permission existingPermission = repository.getPermissions() .stream() .filter(filterPermission(permissionName)) .findFirst() - .orElseThrow(NotFoundException::new); + .orElseThrow(() -> notFound(entity(Permission.class, namespace).in(Repository.class, namespace + "/" + name))); dtoToModelMapper.modify(existingPermission, permission); manager.modify(repository); log.info("the permission with name: {} is updated.", permissionName); @@ -206,7 +209,7 @@ public class PermissionRootResource { .stream() .filter(filterPermission(permissionName)) .findFirst() - .ifPresent(p -> repository.getPermissions().remove(p)) + .ifPresent(repository::removePermission) ; manager.modify(repository); log.info("the permission with name: {} is updated.", permissionName); @@ -237,12 +240,12 @@ public class PermissionRootResource { * @param namespace the repository namespace * @param name the repository name * @return the repository if the user is permitted - * @throws RepositoryNotFoundException if the repository does not exists + * @throws NotFoundException if the repository does not exists */ - private Repository load(String namespace, String name) throws RepositoryNotFoundException { + private Repository load(String namespace, String name) { NamespaceAndName namespaceAndName = new NamespaceAndName(namespace, name); return Optional.ofNullable(manager.get(namespaceAndName)) - .orElseThrow(() -> new RepositoryNotFoundException(namespaceAndName)); + .orElseThrow(() -> notFound(entity(namespaceAndName))); } /** @@ -250,12 +253,11 @@ public class PermissionRootResource { * * @param permission the searched permission * @param repository the repository to be inspected - * @param errorMessage error message * @throws AlreadyExistsException if the permission already exists in the repository */ - private void checkPermissionAlreadyExists(PermissionDto permission, Repository repository, String errorMessage) throws AlreadyExistsException { + private void checkPermissionAlreadyExists(PermissionDto permission, Repository repository) { if (isPermissionExist(permission, repository)) { - throw new AlreadyExistsException(errorMessage); + throw alreadyExists(entity("permission", permission.getName()).in(repository)); } } @@ -264,10 +266,6 @@ public class PermissionRootResource { .stream() .anyMatch(p -> p.getName().equals(permission.getName()) && p.isGroupPermission() == permission.isGroupPermission()); } - - private void checkPermissionAlreadyExists(PermissionDto permission, Repository repository) throws AlreadyExistsException { - checkPermissionAlreadyExists(permission, repository, "the permission " + permission.getName() + " already exist."); - } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryCollectionResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryCollectionResource.java index 9f4858d2f6..d8d5280456 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryCollectionResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryCollectionResource.java @@ -5,9 +5,12 @@ import com.webcohesion.enunciate.metadata.rs.ResponseHeader; import com.webcohesion.enunciate.metadata.rs.ResponseHeaders; import com.webcohesion.enunciate.metadata.rs.StatusCodes; import com.webcohesion.enunciate.metadata.rs.TypeHint; -import sonia.scm.AlreadyExistsException; +import org.apache.shiro.SecurityUtils; +import sonia.scm.repository.Permission; +import sonia.scm.repository.PermissionType; import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryManager; +import sonia.scm.user.User; import sonia.scm.web.VndMediaType; import javax.inject.Inject; @@ -21,6 +24,8 @@ import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Response; +import static java.util.Collections.singletonList; + public class RepositoryCollectionResource { private static final int DEFAULT_PAGE_SIZE = 10; @@ -72,7 +77,7 @@ public class RepositoryCollectionResource { * Note: This method requires "repository" privilege. The namespace of the given repository will * be ignored and set by the configured namespace strategy. * - * @param repositoryDto The repository to be created. + * @param repository The repository to be created. * @return A response with the link to the new repository (if created successfully). */ @POST @@ -87,9 +92,19 @@ public class RepositoryCollectionResource { }) @TypeHint(TypeHint.NO_CONTENT.class) @ResponseHeaders(@ResponseHeader(name = "Location", description = "uri to the created repository")) - public Response create(@Valid RepositoryDto repositoryDto) throws AlreadyExistsException { - return adapter.create(repositoryDto, - () -> dtoToRepositoryMapper.map(repositoryDto, null), - repository -> resourceLinks.repository().self(repository.getNamespace(), repository.getName())); + public Response create(@Valid RepositoryDto repository) { + return adapter.create(repository, + () -> createModelObjectFromDto(repository), + r -> resourceLinks.repository().self(r.getNamespace(), r.getName())); + } + + private Repository createModelObjectFromDto(@Valid RepositoryDto repositoryDto) { + Repository repository = dtoToRepositoryMapper.map(repositoryDto, null); + repository.setPermissions(singletonList(new Permission(currentUser(), PermissionType.OWNER))); + return repository; + } + + private String currentUser() { + return SecurityUtils.getSubject().getPrincipals().oneByType(User.class).getName(); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryResource.java index 56bb50d4e6..f65235db0b 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryResource.java @@ -3,8 +3,6 @@ package sonia.scm.api.v2.resources; import com.webcohesion.enunciate.metadata.rs.ResponseCode; import com.webcohesion.enunciate.metadata.rs.StatusCodes; import com.webcohesion.enunciate.metadata.rs.TypeHint; -import sonia.scm.ConcurrentModificationException; -import sonia.scm.NotFoundException; import sonia.scm.repository.NamespaceAndName; import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryIsNotArchivedException; @@ -12,6 +10,7 @@ import sonia.scm.repository.RepositoryManager; import sonia.scm.web.VndMediaType; import javax.inject.Inject; +import javax.inject.Named; import javax.inject.Provider; import javax.validation.Valid; import javax.ws.rs.Consumes; @@ -26,6 +25,9 @@ import java.util.Optional; import java.util.function.Predicate; import java.util.function.Supplier; +import static sonia.scm.ContextEntry.ContextBuilder.entity; +import static sonia.scm.NotFoundException.notFound; + public class RepositoryResource { private final RepositoryToRepositoryDtoMapper repositoryToDtoMapper; @@ -124,7 +126,7 @@ public class RepositoryResource { * * @param namespace the namespace of the repository to be modified * @param name the name of the repository to be modified - * @param repositoryDto repository object to modify + * @param repository repository object to modify */ @PUT @Path("") @@ -138,10 +140,10 @@ public class RepositoryResource { @ResponseCode(code = 500, condition = "internal server error") }) @TypeHint(TypeHint.NO_CONTENT.class) - public Response update(@PathParam("namespace") String namespace, @PathParam("name") String name, @Valid RepositoryDto repositoryDto) throws ConcurrentModificationException { + public Response update(@PathParam("namespace") String namespace, @PathParam("name") String name, @Valid RepositoryDto repository) { return adapter.update( loadBy(namespace, name), - existing -> processUpdate(repositoryDto, existing), + existing -> processUpdate(repository, existing), nameAndNamespaceStaysTheSame(namespace, name) ); } @@ -203,8 +205,9 @@ public class RepositoryResource { } } - private Supplier> loadBy(String namespace, String name) { - return () -> Optional.ofNullable(manager.get(new NamespaceAndName(namespace, name))); + private Supplier loadBy(String namespace, String name) { + NamespaceAndName namespaceAndName = new NamespaceAndName(namespace, name); + return () -> Optional.ofNullable(manager.get(namespaceAndName)).orElseThrow(() -> notFound(entity(namespaceAndName))); } private Predicate nameAndNamespaceStaysTheSame(String namespace, String name) { diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SingleResourceManagerAdapter.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SingleResourceManagerAdapter.java index d484567bf8..e61a9f3455 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SingleResourceManagerAdapter.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SingleResourceManagerAdapter.java @@ -11,7 +11,6 @@ import javax.ws.rs.core.GenericEntity; import javax.ws.rs.core.Response; import java.util.Collection; import java.util.Optional; -import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; @@ -33,45 +32,41 @@ class SingleResourceManagerAdapter extends AbstractManagerResource { private final Function> errorHandler; + private final Class type; SingleResourceManagerAdapter(Manager manager, Class type) { this(manager, type, e -> Optional.empty()); } - SingleResourceManagerAdapter(Manager manager, Class type, Function> errorHandler) { + SingleResourceManagerAdapter( + Manager manager, + Class type, + Function> errorHandler) { super(manager, type); this.errorHandler = errorHandler; + this.type = type; } /** * Reads the model object for the given id, transforms it to a dto and returns a corresponding http response. * This handles all corner cases, eg. no matching object for the id or missing privileges. */ - Response get(Supplier> reader, Function mapToDto) { - return reader.get() - .map(mapToDto) - .map(Response::ok) - .map(Response.ResponseBuilder::build) - .orElseThrow(NotFoundException::new); - } - public Response update(Supplier> reader, Function applyChanges, Predicate hasSameKey, Consumer checker) throws NotFoundException, ConcurrentModificationException { - MODEL_OBJECT existingModelObject = reader.get().orElseThrow(NotFoundException::new); - checker.accept(existingModelObject); - return update(reader,applyChanges,hasSameKey); + Response get(Supplier reader, Function mapToDto) { + return Response.ok(mapToDto.apply(reader.get())).build(); } /** * Update the model object for the given id according to the given function and returns a corresponding http response. * This handles all corner cases, eg. no matching object for the id or missing privileges. */ - public Response update(Supplier> reader, Function applyChanges, Predicate hasSameKey) throws NotFoundException, ConcurrentModificationException { - MODEL_OBJECT existingModelObject = reader.get().orElseThrow(NotFoundException::new); + Response update(Supplier reader, Function applyChanges, Predicate hasSameKey) { + MODEL_OBJECT existingModelObject = reader.get(); MODEL_OBJECT changedModelObject = applyChanges.apply(existingModelObject); if (!hasSameKey.test(changedModelObject)) { return Response.status(BAD_REQUEST).entity("illegal change of id").build(); } else if (modelObjectWasModifiedConcurrently(existingModelObject, changedModelObject)) { - throw new ConcurrentModificationException(); + throw new ConcurrentModificationException(type, existingModelObject.getId()); } return update(getId(existingModelObject), changedModelObject); } @@ -81,11 +76,13 @@ class SingleResourceManagerAdapter updated.getLastModified()); } - public Response delete(Supplier> reader) { - return reader.get() - .map(MODEL_OBJECT::getId) - .map(this::delete) - .orElse(null); + public Response delete(Supplier reader) { + try { + return delete(reader.get().getId()); + } catch (NotFoundException e) { + // due to idempotency of delete this does not matter here. + return null; + } } @Override diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SourceRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SourceRootResource.java index 8ca9c07cf6..d507a59f14 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SourceRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/SourceRootResource.java @@ -1,6 +1,5 @@ package sonia.scm.api.v2.resources; -import sonia.scm.NotFoundException; import sonia.scm.repository.BrowserResult; import sonia.scm.repository.NamespaceAndName; import sonia.scm.repository.api.BrowseCommandBuilder; @@ -31,14 +30,14 @@ public class SourceRootResource { @GET @Produces(VndMediaType.SOURCE) @Path("") - public Response getAllWithoutRevision(@PathParam("namespace") String namespace, @PathParam("name") String name) throws NotFoundException, IOException { + public Response getAllWithoutRevision(@PathParam("namespace") String namespace, @PathParam("name") String name) throws IOException { return getSource(namespace, name, "/", null); } @GET @Produces(VndMediaType.SOURCE) @Path("{revision}") - public Response getAll(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision) throws NotFoundException, IOException { + public Response getAll(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision) throws IOException { return getSource(namespace, name, "/", revision); } @@ -49,7 +48,7 @@ public class SourceRootResource { return getSource(namespace, name, path, revision); } - private Response getSource(String namespace, String repoName, String path, String revision) throws IOException, NotFoundException { + private Response getSource(String namespace, String repoName, String path, String revision) throws IOException { NamespaceAndName namespaceAndName = new NamespaceAndName(namespace, repoName); try (RepositoryService repositoryService = serviceFactory.create(namespaceAndName)) { BrowseCommandBuilder browseCommand = repositoryService.getBrowseCommand(); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagNotFoundException.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagNotFoundException.java deleted file mode 100644 index b572d20c5b..0000000000 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagNotFoundException.java +++ /dev/null @@ -1,7 +0,0 @@ -package sonia.scm.api.v2.resources; - -import sonia.scm.NotFoundException; - -public class TagNotFoundException extends NotFoundException { - -} diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagRootResource.java index b3601f7709..7acd59e3e1 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/TagRootResource.java @@ -3,9 +3,9 @@ package sonia.scm.api.v2.resources; import com.webcohesion.enunciate.metadata.rs.ResponseCode; import com.webcohesion.enunciate.metadata.rs.StatusCodes; import com.webcohesion.enunciate.metadata.rs.TypeHint; +import sonia.scm.NotFoundException; import sonia.scm.repository.NamespaceAndName; import sonia.scm.repository.Repository; -import sonia.scm.repository.RepositoryNotFoundException; import sonia.scm.repository.RepositoryPermissions; import sonia.scm.repository.Tag; import sonia.scm.repository.Tags; @@ -21,6 +21,9 @@ import javax.ws.rs.Produces; import javax.ws.rs.core.Response; import java.io.IOException; +import static sonia.scm.ContextEntry.ContextBuilder.entity; +import static sonia.scm.NotFoundException.notFound; + public class TagRootResource { private final RepositoryServiceFactory serviceFactory; @@ -47,7 +50,7 @@ public class TagRootResource { }) @Produces(VndMediaType.TAG_COLLECTION) @TypeHint(CollectionDto.class) - public Response getAll(@PathParam("namespace") String namespace, @PathParam("name") String name) throws IOException, RepositoryNotFoundException { + public Response getAll(@PathParam("namespace") String namespace, @PathParam("name") String name) throws IOException { try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) { Tags tags = getTags(repositoryService); if (tags != null && tags.getTags() != null) { @@ -72,7 +75,7 @@ public class TagRootResource { @Produces(VndMediaType.TAG) @TypeHint(TagDto.class) @Path("{tagName}") - public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("tagName") String tagName) throws IOException, RepositoryNotFoundException, TagNotFoundException { + public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("tagName") String tagName) throws IOException { NamespaceAndName namespaceAndName = new NamespaceAndName(namespace, name); try (RepositoryService repositoryService = serviceFactory.create(namespaceAndName)) { Tags tags = getTags(repositoryService); @@ -80,7 +83,7 @@ public class TagRootResource { Tag tag = tags.getTags().stream() .filter(t -> tagName.equals(t.getName())) .findFirst() - .orElseThrow(TagNotFoundException::new); + .orElseThrow(() -> createNotFoundException(namespace, name, tagName)); return Response.ok(tagToTagDtoMapper.map(tag, namespaceAndName)).build(); } else { return Response.status(Response.Status.INTERNAL_SERVER_ERROR) @@ -90,6 +93,10 @@ public class TagRootResource { } } + private NotFoundException createNotFoundException(String namespace, String name, String tagName) { + return notFound(entity("Tag", tagName).in("Repository", namespace + "/" + name)); + } + private Tags getTags(RepositoryService repositoryService) throws IOException { Repository repository = repositoryService.getRepository(); RepositoryPermissions.read(repository).check(); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UIPluginResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UIPluginResource.java index a76c39dd69..b83f5310e3 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UIPluginResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UIPluginResource.java @@ -5,6 +5,7 @@ import com.webcohesion.enunciate.metadata.rs.StatusCodes; import com.webcohesion.enunciate.metadata.rs.TypeHint; import sonia.scm.plugin.PluginLoader; import sonia.scm.plugin.PluginWrapper; +import sonia.scm.security.AllowAnonymousAccess; import sonia.scm.web.VndMediaType; import javax.inject.Inject; @@ -17,6 +18,7 @@ import java.util.List; import java.util.Optional; import java.util.stream.Collectors; +@AllowAnonymousAccess public class UIPluginResource { private final PluginLoader pluginLoader; diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserCollectionResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserCollectionResource.java index 9b39888104..a4fe9adb94 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserCollectionResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserCollectionResource.java @@ -6,12 +6,12 @@ import com.webcohesion.enunciate.metadata.rs.ResponseHeaders; import com.webcohesion.enunciate.metadata.rs.StatusCodes; import com.webcohesion.enunciate.metadata.rs.TypeHint; import org.apache.shiro.authc.credential.PasswordService; -import sonia.scm.AlreadyExistsException; import sonia.scm.user.User; import sonia.scm.user.UserManager; import sonia.scm.web.VndMediaType; import javax.inject.Inject; +import javax.inject.Named; import javax.validation.Valid; import javax.ws.rs.Consumes; import javax.ws.rs.DefaultValue; @@ -76,7 +76,7 @@ public class UserCollectionResource { * * Note: This method requires "user" privilege. * - * @param userDto The user to be created. + * @param user The user to be created. * @return A response with the link to the new user (if created successfully). */ @POST @@ -91,7 +91,7 @@ public class UserCollectionResource { }) @TypeHint(TypeHint.NO_CONTENT.class) @ResponseHeaders(@ResponseHeader(name = "Location", description = "uri to the created user")) - public Response create(@Valid UserDto userDto) throws AlreadyExistsException { - return adapter.create(userDto, () -> dtoToUserMapper.map(userDto, passwordService.encryptPassword(userDto.getPassword())), user -> resourceLinks.user().self(user.getName())); + public Response create(@Valid UserDto user) { + return adapter.create(user, () -> dtoToUserMapper.map(user, passwordService.encryptPassword(user.getPassword())), u -> resourceLinks.user().self(u.getName())); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserResource.java index ef4b8a6eb4..0076d057ca 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserResource.java @@ -4,12 +4,12 @@ import com.webcohesion.enunciate.metadata.rs.ResponseCode; import com.webcohesion.enunciate.metadata.rs.StatusCodes; import com.webcohesion.enunciate.metadata.rs.TypeHint; import org.apache.shiro.authc.credential.PasswordService; -import sonia.scm.ConcurrentModificationException; import sonia.scm.user.User; import sonia.scm.user.UserManager; import sonia.scm.web.VndMediaType; import javax.inject.Inject; +import javax.inject.Named; import javax.validation.Valid; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; @@ -87,7 +87,7 @@ public class UserResource { * Note: This method requires "user" privilege. * * @param name name of the user to be modified - * @param userDto user object to modify + * @param user user object to modify */ @PUT @Path("") @@ -101,8 +101,8 @@ public class UserResource { @ResponseCode(code = 500, condition = "internal server error") }) @TypeHint(TypeHint.NO_CONTENT.class) - public Response update(@PathParam("id") String name, @Valid UserDto userDto) throws ConcurrentModificationException { - return adapter.update(name, existing -> dtoToUserMapper.map(userDto, existing.getPassword())); + public Response update(@PathParam("id") String name, @Valid UserDto user) { + return adapter.update(name, existing -> dtoToUserMapper.map(user, existing.getPassword())); } /** @@ -114,7 +114,7 @@ public class UserResource { * Note: This method requires "user:changeOwnPassword" privilege to modify the own password. * * @param name name of the user to be modified - * @param passwordOverwriteDto change password object to modify password. the old password is here not required + * @param passwordOverwrite change password object to modify password. the old password is here not required */ @PUT @Path("password") @@ -128,8 +128,8 @@ public class UserResource { @ResponseCode(code = 500, condition = "internal server error") }) @TypeHint(TypeHint.NO_CONTENT.class) - public Response overwritePassword(@PathParam("id") String name, @Valid PasswordOverwriteDto passwordOverwriteDto) { - userManager.overwritePassword(name, passwordService.encryptPassword(passwordOverwriteDto.getNewPassword())); + public Response overwritePassword(@PathParam("id") String name, @Valid PasswordOverwriteDto passwordOverwrite) { + userManager.overwritePassword(name, passwordService.encryptPassword(passwordOverwrite.getNewPassword())); return Response.noContent().build(); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ViolationExceptionToErrorDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ViolationExceptionToErrorDtoMapper.java new file mode 100644 index 0000000000..e713b031f7 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ViolationExceptionToErrorDtoMapper.java @@ -0,0 +1,54 @@ +package sonia.scm.api.v2.resources; + +import org.jboss.resteasy.api.validation.ResteasyViolationException; +import org.mapstruct.AfterMapping; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; +import org.slf4j.MDC; + +import javax.validation.ConstraintViolation; +import java.util.List; +import java.util.stream.Collectors; + +@Mapper +public abstract class ViolationExceptionToErrorDtoMapper { + + @Mapping(target = "errorCode", ignore = true) + @Mapping(target = "transactionId", ignore = true) + @Mapping(target = "context", ignore = true) + @Mapping(target = "url", ignore = true) + public abstract ErrorDto map(ResteasyViolationException exception); + + @AfterMapping + void setTransactionId(@MappingTarget ErrorDto dto) { + dto.setTransactionId(MDC.get("transaction_id")); + } + + @AfterMapping + void mapViolations(ResteasyViolationException exception, @MappingTarget ErrorDto dto) { + List violations = + exception.getConstraintViolations() + .stream() + .map(this::createViolationDto) + .collect(Collectors.toList()); + dto.setViolations(violations); + } + + private ErrorDto.ConstraintViolationDto createViolationDto(ConstraintViolation violation) { + ErrorDto.ConstraintViolationDto constraintViolationDto = new ErrorDto.ConstraintViolationDto(); + constraintViolationDto.setMessage(violation.getMessage()); + constraintViolationDto.setPath(violation.getPropertyPath().toString()); + return constraintViolationDto; + } + + @AfterMapping + void setErrorCode(@MappingTarget ErrorDto dto) { + dto.setErrorCode("1wR7ZBe7H1"); + } + + @AfterMapping + void setMessage(@MappingTarget ErrorDto dto) { + dto.setMessage("input violates conditions (see violation list)"); + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/filter/AdminSecurityFilter.java b/scm-webapp/src/main/java/sonia/scm/filter/AdminSecurityFilter.java deleted file mode 100644 index a5455c6d21..0000000000 --- a/scm-webapp/src/main/java/sonia/scm/filter/AdminSecurityFilter.java +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Copyright (c) 2010, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ - - - -package sonia.scm.filter; - -//~--- non-JDK imports -------------------------------------------------------- - -import com.google.inject.Inject; - -import org.apache.shiro.subject.Subject; - -import sonia.scm.Priority; -import sonia.scm.config.ScmConfiguration; -import sonia.scm.security.Role; - -/** - * Security filter which allow only administrators to access the underlying - * resources. - * - * @author Sebastian Sdorra - */ -// TODO before releasing v2, delete this filter (we use Permission objects now) -@WebElement( - value = Filters.PATTERN_CONFIG, - morePatterns = { - Filters.PATTERN_USERS, - Filters.PATTERN_GROUPS, - Filters.PATTERN_PLUGINS - } -) -@Priority(Filters.PRIORITY_AUTHORIZATION + 1) -public class AdminSecurityFilter extends SecurityFilter -{ - - /** - * Constructs a new instance. - * - * @param configuration scm-manager main configuration - */ - @Inject - public AdminSecurityFilter(ScmConfiguration configuration) - { - super(configuration); - } - - //~--- get methods ---------------------------------------------------------- - - /** - * Returns {@code true} if the subject has the admin role. - * - * @param subject subject - * - * @return {@code true} if the subject has the admin role - */ - @Override - protected boolean hasPermission(Subject subject) - { - return subject.hasRole(Role.ADMIN); - } -} diff --git a/scm-webapp/src/main/java/sonia/scm/filter/MDCFilter.java b/scm-webapp/src/main/java/sonia/scm/filter/MDCFilter.java index 9198c19ef7..b77a927a2d 100644 --- a/scm-webapp/src/main/java/sonia/scm/filter/MDCFilter.java +++ b/scm-webapp/src/main/java/sonia/scm/filter/MDCFilter.java @@ -34,7 +34,6 @@ package sonia.scm.filter; //~--- non-JDK imports -------------------------------------------------------- import com.google.common.annotations.VisibleForTesting; -import com.google.inject.Singleton; import org.apache.shiro.SecurityUtils; import org.apache.shiro.subject.Subject; @@ -42,6 +41,8 @@ import org.apache.shiro.subject.Subject; import org.slf4j.MDC; import sonia.scm.SCMContext; +import sonia.scm.security.DefaultKeyGenerator; +import sonia.scm.security.KeyGenerator; import sonia.scm.web.filter.HttpFilter; //~--- JDK imports ------------------------------------------------------------ @@ -62,27 +63,26 @@ import sonia.scm.Priority; @WebElement(Filters.PATTERN_ALL) public class MDCFilter extends HttpFilter { + private static final DefaultKeyGenerator TRANSACTION_KEY_GENERATOR = new DefaultKeyGenerator(); - /** Field description */ @VisibleForTesting - static final String MDC_CLIEN_HOST = "client_host"; + static final String MDC_CLIENT_HOST = "client_host"; - /** Field description */ @VisibleForTesting - static final String MDC_CLIEN_IP = "client_ip"; - - /** url of the current request */ + static final String MDC_CLIENT_IP = "client_ip"; + @VisibleForTesting static final String MDC_REQUEST_URI = "request_uri"; - - /** request method */ + @VisibleForTesting static final String MDC_REQUEST_METHOD = "request_method"; - /** Field description */ @VisibleForTesting static final String MDC_USERNAME = "username"; + @VisibleForTesting + static final String MDC_TRANSACTION_ID = "transaction_id"; + //~--- methods -------------------------------------------------------------- /** @@ -102,10 +102,11 @@ public class MDCFilter extends HttpFilter throws IOException, ServletException { MDC.put(MDC_USERNAME, getUsername()); - MDC.put(MDC_CLIEN_IP, request.getRemoteAddr()); - MDC.put(MDC_CLIEN_HOST, request.getRemoteHost()); + MDC.put(MDC_CLIENT_IP, request.getRemoteAddr()); + MDC.put(MDC_CLIENT_HOST, request.getRemoteHost()); MDC.put(MDC_REQUEST_METHOD, request.getMethod()); MDC.put(MDC_REQUEST_URI, request.getRequestURI()); + MDC.put(MDC_TRANSACTION_ID, TRANSACTION_KEY_GENERATOR.createKey()); try { @@ -114,10 +115,11 @@ public class MDCFilter extends HttpFilter finally { MDC.remove(MDC_USERNAME); - MDC.remove(MDC_CLIEN_IP); - MDC.remove(MDC_CLIEN_HOST); + MDC.remove(MDC_CLIENT_IP); + MDC.remove(MDC_CLIENT_HOST); MDC.remove(MDC_REQUEST_METHOD); MDC.remove(MDC_REQUEST_URI); + MDC.remove(MDC_TRANSACTION_ID); } } diff --git a/scm-webapp/src/main/java/sonia/scm/filter/SecurityFilter.java b/scm-webapp/src/main/java/sonia/scm/filter/PropagatePrincipleFilter.java similarity index 72% rename from scm-webapp/src/main/java/sonia/scm/filter/SecurityFilter.java rename to scm-webapp/src/main/java/sonia/scm/filter/PropagatePrincipleFilter.java index d97a5b050e..508e804d1f 100644 --- a/scm-webapp/src/main/java/sonia/scm/filter/SecurityFilter.java +++ b/scm-webapp/src/main/java/sonia/scm/filter/PropagatePrincipleFilter.java @@ -42,9 +42,8 @@ import org.apache.shiro.subject.Subject; import sonia.scm.Priority; import sonia.scm.SCMContext; import sonia.scm.config.ScmConfiguration; -import sonia.scm.security.SecurityRequests; import sonia.scm.web.filter.HttpFilter; -import sonia.scm.web.filter.SecurityHttpServletRequestWrapper; +import sonia.scm.web.filter.PropagatePrincipleServletRequestWrapper; import javax.servlet.FilterChain; import javax.servlet.ServletException; @@ -61,10 +60,7 @@ import static sonia.scm.api.v2.resources.ScmPathInfo.REST_API_PATH; * @author Sebastian Sdorra */ @Priority(Filters.PRIORITY_AUTHORIZATION) -// TODO find a better way for unprotected resources -@WebElement(value = REST_API_PATH + "" + - "/(?!v2/ui).*", regex = true) -public class SecurityFilter extends HttpFilter +public class PropagatePrincipleFilter extends HttpFilter { /** name of request attribute for the primary principal */ @@ -74,7 +70,7 @@ public class SecurityFilter extends HttpFilter private final ScmConfiguration configuration; @Inject - public SecurityFilter(ScmConfiguration configuration) + public PropagatePrincipleFilter(ScmConfiguration configuration) { this.configuration = configuration; } @@ -84,31 +80,16 @@ public class SecurityFilter extends HttpFilter HttpServletResponse response, FilterChain chain) throws IOException, ServletException { - if (!SecurityRequests.isAuthenticationRequest(request) && !SecurityRequests.isIndexRequest(request)) + Subject subject = SecurityUtils.getSubject(); + if (hasPermission(subject)) { - Subject subject = SecurityUtils.getSubject(); - if (hasPermission(subject)) - { - // add primary principal as request attribute - // see https://goo.gl/JRjNmf - String username = getUsername(subject); - request.setAttribute(ATTRIBUTE_REMOTE_USER, username); + // add primary principal as request attribute + // see https://goo.gl/JRjNmf + String username = getUsername(subject); + request.setAttribute(ATTRIBUTE_REMOTE_USER, username); - // wrap servlet request to provide authentication informations - chain.doFilter(new SecurityHttpServletRequestWrapper(request, username), response); - } - else if (subject.isAuthenticated() || subject.isRemembered()) - { - response.sendError(HttpServletResponse.SC_FORBIDDEN); - } - else if (configuration.isAnonymousAccessEnabled()) - { - response.sendError(HttpServletResponse.SC_FORBIDDEN); - } - else - { - response.sendError(HttpServletResponse.SC_UNAUTHORIZED); - } + // wrap servlet request to provide authentication information + chain.doFilter(new PropagatePrincipleServletRequestWrapper(request, username), response); } else { @@ -116,7 +97,7 @@ public class SecurityFilter extends HttpFilter } } - protected boolean hasPermission(Subject subject) + private boolean hasPermission(Subject subject) { return ((configuration != null) && configuration.isAnonymousAccessEnabled()) || subject.isAuthenticated() @@ -139,5 +120,4 @@ public class SecurityFilter extends HttpFilter return username; } - } diff --git a/scm-webapp/src/main/java/sonia/scm/group/DefaultGroupManager.java b/scm-webapp/src/main/java/sonia/scm/group/DefaultGroupManager.java index 236c6d0ebc..6bf850f99d 100644 --- a/scm-webapp/src/main/java/sonia/scm/group/DefaultGroupManager.java +++ b/scm-webapp/src/main/java/sonia/scm/group/DefaultGroupManager.java @@ -42,7 +42,6 @@ import com.google.inject.Inject; import com.google.inject.Singleton; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sonia.scm.AlreadyExistsException; import sonia.scm.HandlerEventType; import sonia.scm.ManagerDaoAdapter; import sonia.scm.NotFoundException; @@ -106,7 +105,7 @@ public class DefaultGroupManager extends AbstractGroupManager } @Override - public Group create(Group group) throws AlreadyExistsException { + public Group create(Group group) { String type = group.getType(); if (Util.isEmpty(type)) { group.setType(groupDAO.getType()); @@ -172,7 +171,7 @@ public class DefaultGroupManager extends AbstractGroupManager if (fresh == null) { - throw new NotFoundException("group", group.getId()); + throw new NotFoundException(Group.class, group.getId()); } fresh.copyProperties(group); diff --git a/scm-webapp/src/main/java/sonia/scm/repository/DefaultRepositoryManager.java b/scm-webapp/src/main/java/sonia/scm/repository/DefaultRepositoryManager.java index c2e8bc3ad8..c409a860ec 100644 --- a/scm-webapp/src/main/java/sonia/scm/repository/DefaultRepositoryManager.java +++ b/scm-webapp/src/main/java/sonia/scm/repository/DefaultRepositoryManager.java @@ -65,6 +65,9 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; +import static sonia.scm.ContextEntry.ContextBuilder.entity; +import static sonia.scm.NotFoundException.notFound; + /** * Default implementation of {@link RepositoryManager}. * @@ -122,11 +125,11 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager { } @Override - public Repository create(Repository repository) throws AlreadyExistsException { + public Repository create(Repository repository) { return create(repository, true); } - public Repository create(Repository repository, boolean initRepository) throws AlreadyExistsException { + public Repository create(Repository repository, boolean initRepository) { repository.setId(keyGenerator.createKey()); repository.setNamespace(namespaceStrategy.createNamespace(repository)); @@ -140,7 +143,7 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager { try { getHandler(newRepository).create(newRepository); } catch (AlreadyExistsException e) { - throw new InternalRepositoryException("directory for repository does already exist", e); + throw new InternalRepositoryException(repository, "directory for repository does already exist", e); } } fireEvent(HandlerEventType.BEFORE_CREATE, newRepository); @@ -170,7 +173,7 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager { } @Override - public void importRepository(Repository repository) throws AlreadyExistsException { + public void importRepository(Repository repository) { create(repository, false); } @@ -198,7 +201,7 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager { } @Override - public void refresh(Repository repository) throws RepositoryNotFoundException { + public void refresh(Repository repository) { AssertUtil.assertIsNotNull(repository); RepositoryPermissions.read(repository).check(); @@ -207,7 +210,7 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager { if (fresh != null) { fresh.copyProperties(repository); } else { - throw new RepositoryNotFoundException(repository); + throw notFound(entity(repository)); } } @@ -350,9 +353,9 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager { RepositoryHandler handler = handlerMap.get(type); if (handler == null) { - throw new InternalRepositoryException("could not find handler for " + type); + throw new InternalRepositoryException(entity(repository), "could not find handler for " + type); } else if (!handler.isConfigured()) { - throw new InternalRepositoryException("handler is not configured for type " + type); + throw new InternalRepositoryException(entity(repository), "handler is not configured for type " + type); } return handler; diff --git a/scm-webapp/src/main/java/sonia/scm/repository/HealthChecker.java b/scm-webapp/src/main/java/sonia/scm/repository/HealthChecker.java index e9c415c4fa..8057db53dc 100644 --- a/scm-webapp/src/main/java/sonia/scm/repository/HealthChecker.java +++ b/scm-webapp/src/main/java/sonia/scm/repository/HealthChecker.java @@ -33,7 +33,6 @@ import com.google.common.collect.ImmutableList; import com.google.inject.Inject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sonia.scm.ConcurrentModificationException; import sonia.scm.NotFoundException; import java.util.Set; @@ -61,7 +60,7 @@ public final class HealthChecker { Repository repository = repositoryManager.get(id); if (repository == null) { - throw new RepositoryNotFoundException(id); + throw new NotFoundException(Repository.class, id); } doCheck(repository); diff --git a/scm-webapp/src/main/java/sonia/scm/security/AuthorizationChangedEventProducer.java b/scm-webapp/src/main/java/sonia/scm/security/AuthorizationChangedEventProducer.java index 5a954bb1e7..cf4c980625 100644 --- a/scm-webapp/src/main/java/sonia/scm/security/AuthorizationChangedEventProducer.java +++ b/scm-webapp/src/main/java/sonia/scm/security/AuthorizationChangedEventProducer.java @@ -167,7 +167,7 @@ public class AuthorizationChangedEventProducer { private boolean isAuthorizationDataModified(Repository repository, Repository beforeModification) { return repository.isArchived() != beforeModification.isArchived() || repository.isPublicReadable() != beforeModification.isPublicReadable() - || ! repository.getPermissions().equals(beforeModification.getPermissions()); + || !(repository.getPermissions().containsAll(beforeModification.getPermissions()) && beforeModification.getPermissions().containsAll(repository.getPermissions())); } private void fireEventForEveryUser() { diff --git a/scm-webapp/src/main/java/sonia/scm/security/DefaultAuthorizationCollector.java b/scm-webapp/src/main/java/sonia/scm/security/DefaultAuthorizationCollector.java index faa7208c66..8a79293642 100644 --- a/scm-webapp/src/main/java/sonia/scm/security/DefaultAuthorizationCollector.java +++ b/scm-webapp/src/main/java/sonia/scm/security/DefaultAuthorizationCollector.java @@ -54,13 +54,14 @@ import sonia.scm.cache.CacheManager; import sonia.scm.group.GroupNames; import sonia.scm.group.GroupPermissions; import sonia.scm.plugin.Extension; +import sonia.scm.repository.Permission; import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryDAO; -import sonia.scm.repository.RepositoryPermissions; import sonia.scm.user.User; import sonia.scm.user.UserPermissions; import sonia.scm.util.Util; +import java.util.Collection; import java.util.List; import java.util.Set; @@ -198,7 +199,7 @@ public class DefaultAuthorizationCollector implements AuthorizationCollector private void collectRepositoryPermissions(Builder builder, Repository repository, User user, GroupNames groups) { - List repositoryPermissions + Collection repositoryPermissions = repository.getPermissions(); if (Util.isNotEmpty(repositoryPermissions)) diff --git a/scm-webapp/src/main/java/sonia/scm/security/SecurityRequestFilter.java b/scm-webapp/src/main/java/sonia/scm/security/SecurityRequestFilter.java new file mode 100644 index 0000000000..2bbdf6f468 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/security/SecurityRequestFilter.java @@ -0,0 +1,45 @@ +package sonia.scm.security; + +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.authc.AuthenticationException; +import org.apache.shiro.subject.Subject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.container.ResourceInfo; +import javax.ws.rs.core.Context; +import javax.ws.rs.ext.Provider; +import java.lang.reflect.Method; + +@Provider +public class SecurityRequestFilter implements ContainerRequestFilter { + + private static final Logger LOG = LoggerFactory.getLogger(SecurityRequestFilter.class); + + @Context + private ResourceInfo resourceInfo; + + @Override + public void filter(ContainerRequestContext requestContext) { + Method resourceMethod = resourceInfo.getResourceMethod(); + if (hasPermission() || anonymousAccessIsAllowed(resourceMethod)) { + LOG.debug("allowed unauthenticated request to method {}", resourceMethod); + // nothing further to do + } else { + LOG.debug("blocked unauthenticated request to method {}", resourceMethod); + throw new AuthenticationException(); + } + } + + private boolean anonymousAccessIsAllowed(Method method) { + return method.isAnnotationPresent(AllowAnonymousAccess.class) + || method.getDeclaringClass().isAnnotationPresent(AllowAnonymousAccess.class); + } + + private boolean hasPermission() { + Subject subject = SecurityUtils.getSubject(); + return subject.isAuthenticated() || subject.isRemembered(); + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/user/DefaultUserManager.java b/scm-webapp/src/main/java/sonia/scm/user/DefaultUserManager.java index 4efaaa1d1c..33762a5941 100644 --- a/scm-webapp/src/main/java/sonia/scm/user/DefaultUserManager.java +++ b/scm-webapp/src/main/java/sonia/scm/user/DefaultUserManager.java @@ -39,7 +39,7 @@ import com.google.inject.Singleton; import org.apache.shiro.SecurityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sonia.scm.AlreadyExistsException; +import sonia.scm.ContextEntry; import sonia.scm.EagerSingleton; import sonia.scm.HandlerEventType; import sonia.scm.ManagerDaoAdapter; @@ -137,7 +137,7 @@ public class DefaultUserManager extends AbstractUserManager * @throws IOException */ @Override - public User create(User user) throws AlreadyExistsException { + public User create(User user) { String type = user.getType(); if (Util.isEmpty(type)) { user.setType(userDAO.getType()); @@ -219,7 +219,7 @@ public class DefaultUserManager extends AbstractUserManager if (fresh == null) { - throw new NotFoundException(); + throw new NotFoundException(User.class, user.getName()); } fresh.copyProperties(user); @@ -403,7 +403,7 @@ public class DefaultUserManager extends AbstractUserManager User user = get((String) SecurityUtils.getSubject().getPrincipals().getPrimaryPrincipal()); if (!user.getPassword().equals(oldPassword)) { - throw new InvalidPasswordException(); + throw new InvalidPasswordException(ContextEntry.ContextBuilder.entity("passwordChange", "-").in(User.class, user.getName())); } user.setPassword(newPassword); @@ -419,10 +419,10 @@ public class DefaultUserManager extends AbstractUserManager public void overwritePassword(String userId, String newPassword) { User user = get(userId); if (user == null) { - throw new NotFoundException(); + throw new NotFoundException(User.class, userId); } if (!isTypeDefault(user)) { - throw new ChangePasswordNotAllowedException(user.getType()); + throw new ChangePasswordNotAllowedException(ContextEntry.ContextBuilder.entity("passwordChange", "-").in(User.class, user.getName()), user.getType()); } user.setPassword(newPassword); this.modify(user); diff --git a/scm-webapp/src/main/java/sonia/scm/web/i18n/I18nServlet.java b/scm-webapp/src/main/java/sonia/scm/web/i18n/I18nServlet.java index 9773b91cf7..b074781fec 100644 --- a/scm-webapp/src/main/java/sonia/scm/web/i18n/I18nServlet.java +++ b/scm-webapp/src/main/java/sonia/scm/web/i18n/I18nServlet.java @@ -30,6 +30,9 @@ import java.util.Optional; import java.util.function.BiConsumer; import java.util.function.Function; +import static sonia.scm.ContextEntry.ContextBuilder.entity; +import static sonia.scm.NotFoundException.notFound; + /** * Collect the plugin translations. @@ -69,7 +72,7 @@ public class I18nServlet extends HttpServlet { createdFile.ifPresent(map -> createdJsonFileConsumer.accept(path, map)); return createdFile.orElse(null); } - )).orElseThrow(NotFoundException::new); + )).orElseThrow(() -> notFound(entity("jsonprovider", path))); } @VisibleForTesting diff --git a/scm-webapp/src/main/java/sonia/scm/web/protocol/HttpProtocolServlet.java b/scm-webapp/src/main/java/sonia/scm/web/protocol/HttpProtocolServlet.java index bc085d2cc7..c6a96e4b9e 100644 --- a/scm-webapp/src/main/java/sonia/scm/web/protocol/HttpProtocolServlet.java +++ b/scm-webapp/src/main/java/sonia/scm/web/protocol/HttpProtocolServlet.java @@ -4,11 +4,11 @@ import com.google.inject.Inject; import com.google.inject.Singleton; import lombok.extern.slf4j.Slf4j; import org.apache.http.HttpStatus; +import sonia.scm.NotFoundException; import sonia.scm.PushStateDispatcher; import sonia.scm.filter.WebElement; import sonia.scm.repository.DefaultRepositoryProvider; import sonia.scm.repository.NamespaceAndName; -import sonia.scm.repository.RepositoryNotFoundException; import sonia.scm.repository.api.RepositoryService; import sonia.scm.repository.api.RepositoryServiceFactory; import sonia.scm.repository.spi.HttpScmProtocol; @@ -71,8 +71,8 @@ public class HttpProtocolServlet extends HttpServlet { requestProvider.get().setAttribute(DefaultRepositoryProvider.ATTRIBUTE_NAME, repositoryService.getRepository()); HttpScmProtocol protocol = repositoryService.getProtocol(HttpScmProtocol.class); protocol.serve(req, resp, getServletConfig()); - } catch (RepositoryNotFoundException e) { - log.debug("Repository not found for namespace and name {}", namespaceAndName, e); + } catch (NotFoundException e) { + log.debug(e.getMessage()); resp.setStatus(HttpStatus.SC_NOT_FOUND); } } diff --git a/scm-webapp/src/main/resources/META-INF/validation.xml b/scm-webapp/src/main/resources/META-INF/validation.xml new file mode 100644 index 0000000000..337bbff26e --- /dev/null +++ b/scm-webapp/src/main/resources/META-INF/validation.xml @@ -0,0 +1,9 @@ + + + org.hibernate.validator.parameternameprovider.ReflectionParameterNameProvider + + diff --git a/scm-webapp/src/main/resources/logback.default.xml b/scm-webapp/src/main/resources/logback.default.xml index b12b9e0890..a4e2eed965 100644 --- a/scm-webapp/src/main/resources/logback.default.xml +++ b/scm-webapp/src/main/resources/logback.default.xml @@ -46,7 +46,7 @@ - %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%-10X{transaction_id}] %-5level %logger - %msg%n diff --git a/scm-webapp/src/main/resources/logback.release.xml b/scm-webapp/src/main/resources/logback.release.xml index 4cf5906581..03ac987992 100644 --- a/scm-webapp/src/main/resources/logback.release.xml +++ b/scm-webapp/src/main/resources/logback.release.xml @@ -61,14 +61,14 @@ true - %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%-10X{transaction_id}] %-5level %logger - %msg%n - %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%-10X{transaction_id}] %-5level %logger - %msg%n diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ChangesetRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ChangesetRootResourceTest.java index c26998eef7..d5c0f91f81 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ChangesetRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ChangesetRootResourceTest.java @@ -9,7 +9,6 @@ import org.apache.shiro.util.ThreadContext; import org.apache.shiro.util.ThreadState; import org.assertj.core.util.Lists; import org.jboss.resteasy.core.Dispatcher; -import org.jboss.resteasy.mock.MockDispatcherFactory; import org.jboss.resteasy.mock.MockHttpRequest; import org.jboss.resteasy.mock.MockHttpResponse; import org.junit.After; @@ -19,7 +18,6 @@ import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; -import sonia.scm.api.rest.AuthorizationExceptionMapper; import sonia.scm.repository.Changeset; import sonia.scm.repository.ChangesetPagingResult; import sonia.scm.repository.NamespaceAndName; @@ -38,7 +36,6 @@ import java.util.List; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -50,7 +47,8 @@ public class ChangesetRootResourceTest extends RepositoryTestBase { public static final String CHANGESET_PATH = "space/repo/changesets/"; public static final String CHANGESET_URL = "/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + CHANGESET_PATH; - private final Dispatcher dispatcher = MockDispatcherFactory.createDispatcher(); + + private Dispatcher dispatcher; private final URI baseUri = URI.create("/"); private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(baseUri); @@ -77,16 +75,14 @@ public class ChangesetRootResourceTest extends RepositoryTestBase { @Before - public void prepareEnvironment() throws Exception { + public void prepareEnvironment() { changesetCollectionToDtoMapper = new ChangesetCollectionToDtoMapper(changesetToChangesetDtoMapper, resourceLinks); changesetRootResource = new ChangesetRootResource(serviceFactory, changesetCollectionToDtoMapper, changesetToChangesetDtoMapper); super.changesetRootResource = Providers.of(changesetRootResource); - dispatcher.getRegistry().addSingletonResource(getRepositoryRootResource()); + dispatcher = DispatcherMock.createDispatcher(getRepositoryRootResource()); when(serviceFactory.create(new NamespaceAndName("space", "repo"))).thenReturn(repositoryService); when(serviceFactory.create(any(Repository.class))).thenReturn(repositoryService); when(repositoryService.getRepository()).thenReturn(new Repository("repoId", "git", "space", "repo")); - dispatcher.getProviderFactory().registerProvider(NotFoundExceptionMapper.class); - dispatcher.getProviderFactory().registerProvider(AuthorizationExceptionMapper.class); when(repositoryService.getLogCommand()).thenReturn(logCommandBuilder); subjectThreadState.bind(); ThreadContext.bind(subject); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ContentResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ContentResourceTest.java index e6a0972b78..5deab62e47 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ContentResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ContentResourceTest.java @@ -8,9 +8,8 @@ import org.mockito.Answers; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; +import sonia.scm.NotFoundException; import sonia.scm.repository.NamespaceAndName; -import sonia.scm.repository.PathNotFoundException; -import sonia.scm.repository.RepositoryNotFoundException; import sonia.scm.repository.api.CatCommandBuilder; import sonia.scm.repository.api.RepositoryService; import sonia.scm.repository.api.RepositoryServiceFactory; @@ -58,8 +57,8 @@ public class ContentResourceTest { when(catCommand.setRevision(REV)).thenReturn(catCommand); // defaults for unknown things - doThrow(new RepositoryNotFoundException("x")).when(repositoryServiceFactory).create(not(eq(existingNamespaceAndName))); - doThrow(new PathNotFoundException("x")).when(catCommand).getStream(any()); + doThrow(new NotFoundException("Test", "r")).when(repositoryServiceFactory).create(not(eq(existingNamespaceAndName))); + doThrow(new NotFoundException("Test", "X")).when(catCommand).getStream(any()); } @Test @@ -175,7 +174,7 @@ public class ContentResourceTest { } @Override - public void close() throws IOException { + public void close() { closed = true; } diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/DiffResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/DiffResourceTest.java index d530e0dd4e..01d5e22b53 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/DiffResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/DiffResourceTest.java @@ -17,12 +17,11 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; +import sonia.scm.NotFoundException; import sonia.scm.api.rest.AuthorizationExceptionMapper; -import sonia.scm.api.rest.IllegalArgumentExceptionMapper; +import sonia.scm.api.v2.NotFoundExceptionMapper; import sonia.scm.repository.NamespaceAndName; import sonia.scm.repository.Repository; -import sonia.scm.repository.RepositoryNotFoundException; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.repository.api.DiffCommandBuilder; import sonia.scm.repository.api.DiffFormat; import sonia.scm.repository.api.RepositoryService; @@ -66,17 +65,17 @@ public class DiffResourceTest extends RepositoryTestBase { @Before - public void prepareEnvironment() throws Exception { + public void prepareEnvironment() { diffRootResource = new DiffRootResource(serviceFactory); super.diffRootResource = Providers.of(diffRootResource); dispatcher.getRegistry().addSingletonResource(getRepositoryRootResource()); when(serviceFactory.create(new NamespaceAndName("space", "repo"))).thenReturn(service); when(serviceFactory.create(any(Repository.class))).thenReturn(service); when(service.getRepository()).thenReturn(new Repository("repoId", "git", "space", "repo")); - dispatcher.getProviderFactory().registerProvider(NotFoundExceptionMapper.class); + ExceptionWithContextToErrorDtoMapperImpl mapper = new ExceptionWithContextToErrorDtoMapperImpl(); + dispatcher.getProviderFactory().register(new NotFoundExceptionMapper(mapper)); dispatcher.getProviderFactory().registerProvider(AuthorizationExceptionMapper.class); dispatcher.getProviderFactory().registerProvider(CRLFInjectionExceptionMapper.class); - dispatcher.getProviderFactory().registerProvider(IllegalArgumentExceptionMapper.class); when(service.getDiffCommand()).thenReturn(diffCommandBuilder); subjectThreadState.bind(); ThreadContext.bind(subject); @@ -110,8 +109,8 @@ public class DiffResourceTest extends RepositoryTestBase { } @Test - public void shouldGet404OnMissingRepository() throws URISyntaxException, RepositoryNotFoundException { - when(serviceFactory.create(any(NamespaceAndName.class))).thenThrow(RepositoryNotFoundException.class); + public void shouldGet404OnMissingRepository() throws URISyntaxException { + when(serviceFactory.create(any(NamespaceAndName.class))).thenThrow(new NotFoundException("Text", "x")); MockHttpRequest request = MockHttpRequest .get(DIFF_URL + "revision") .accept(VndMediaType.DIFF); @@ -124,13 +123,15 @@ public class DiffResourceTest extends RepositoryTestBase { public void shouldGet404OnMissingRevision() throws Exception { when(diffCommandBuilder.setRevision(anyString())).thenReturn(diffCommandBuilder); when(diffCommandBuilder.setFormat(any())).thenReturn(diffCommandBuilder); - when(diffCommandBuilder.retrieveContent(any())).thenThrow(RevisionNotFoundException.class); + when(diffCommandBuilder.retrieveContent(any())).thenThrow(new NotFoundException("Text", "x")); MockHttpRequest request = MockHttpRequest .get(DIFF_URL + "revision") .accept(VndMediaType.DIFF); MockHttpResponse response = new MockHttpResponse(); + dispatcher.invoke(request, response); + assertEquals(404, response.getStatus()); } @@ -138,7 +139,7 @@ public class DiffResourceTest extends RepositoryTestBase { public void shouldGet400OnCrlfInjection() throws Exception { when(diffCommandBuilder.setRevision(anyString())).thenReturn(diffCommandBuilder); when(diffCommandBuilder.setFormat(any())).thenReturn(diffCommandBuilder); - when(diffCommandBuilder.retrieveContent(any())).thenThrow(RevisionNotFoundException.class); + when(diffCommandBuilder.retrieveContent(any())).thenThrow(new NotFoundException("Text", "x")); MockHttpRequest request = MockHttpRequest .get(DIFF_URL + "ny%0D%0ASet-cookie:%20Tamper=3079675143472450634") @@ -152,7 +153,7 @@ public class DiffResourceTest extends RepositoryTestBase { public void shouldGet400OnUnknownFormat() throws Exception { when(diffCommandBuilder.setRevision(anyString())).thenReturn(diffCommandBuilder); when(diffCommandBuilder.setFormat(any())).thenReturn(diffCommandBuilder); - when(diffCommandBuilder.retrieveContent(any())).thenThrow(RevisionNotFoundException.class); + when(diffCommandBuilder.retrieveContent(any())).thenThrow(new NotFoundException("Test", "test")); MockHttpRequest request = MockHttpRequest .get(DIFF_URL + "revision?format=Unknown") diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/DispatcherMock.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/DispatcherMock.java index 8a71abb670..9638a8aa49 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/DispatcherMock.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/DispatcherMock.java @@ -5,20 +5,22 @@ import org.jboss.resteasy.mock.MockDispatcherFactory; import sonia.scm.api.rest.AlreadyExistsExceptionMapper; import sonia.scm.api.rest.AuthorizationExceptionMapper; import sonia.scm.api.rest.ConcurrentModificationExceptionMapper; -import sonia.scm.api.rest.IllegalArgumentExceptionMapper; +import sonia.scm.api.v2.NotFoundExceptionMapper; +import sonia.scm.api.v2.NotSupportedFeatureExceptionMapper; public class DispatcherMock { public static Dispatcher createDispatcher(Object resource) { Dispatcher dispatcher = MockDispatcherFactory.createDispatcher(); dispatcher.getRegistry().addSingletonResource(resource); - dispatcher.getProviderFactory().registerProvider(NotFoundExceptionMapper.class); - dispatcher.getProviderFactory().registerProvider(AlreadyExistsExceptionMapper.class); + ExceptionWithContextToErrorDtoMapperImpl mapper = new ExceptionWithContextToErrorDtoMapperImpl(); + dispatcher.getProviderFactory().register(new NotFoundExceptionMapper(mapper)); + dispatcher.getProviderFactory().register(new AlreadyExistsExceptionMapper(mapper)); + dispatcher.getProviderFactory().register(new ConcurrentModificationExceptionMapper(mapper)); dispatcher.getProviderFactory().registerProvider(AuthorizationExceptionMapper.class); - dispatcher.getProviderFactory().registerProvider(ConcurrentModificationExceptionMapper.class); - dispatcher.getProviderFactory().registerProvider(InternalRepositoryExceptionMapper.class); - dispatcher.getProviderFactory().registerProvider(ChangePasswordNotAllowedExceptionMapper.class); - dispatcher.getProviderFactory().registerProvider(InvalidPasswordExceptionMapper.class); - dispatcher.getProviderFactory().registerProvider(IllegalArgumentExceptionMapper.class); + dispatcher.getProviderFactory().register(new InternalRepositoryExceptionMapper(mapper)); + dispatcher.getProviderFactory().register(new ChangePasswordNotAllowedExceptionMapper(mapper)); + dispatcher.getProviderFactory().register(new InvalidPasswordExceptionMapper(mapper)); + dispatcher.getProviderFactory().register(new NotSupportedFeatureExceptionMapper(mapper)); return dispatcher; } } diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileHistoryResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileHistoryResourceTest.java index 934e05d0d1..52c9a434c0 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileHistoryResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileHistoryResourceTest.java @@ -8,7 +8,6 @@ import org.apache.shiro.util.ThreadContext; import org.apache.shiro.util.ThreadState; import org.assertj.core.util.Lists; import org.jboss.resteasy.core.Dispatcher; -import org.jboss.resteasy.mock.MockDispatcherFactory; import org.jboss.resteasy.mock.MockHttpRequest; import org.jboss.resteasy.mock.MockHttpResponse; import org.junit.After; @@ -18,15 +17,14 @@ import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; -import sonia.scm.api.rest.AuthorizationExceptionMapper; +import sonia.scm.ContextEntry; +import sonia.scm.NotFoundException; import sonia.scm.repository.Changeset; import sonia.scm.repository.ChangesetPagingResult; import sonia.scm.repository.InternalRepositoryException; import sonia.scm.repository.NamespaceAndName; import sonia.scm.repository.Person; import sonia.scm.repository.Repository; -import sonia.scm.repository.RepositoryNotFoundException; -import sonia.scm.repository.RevisionNotFoundException; import sonia.scm.repository.api.LogCommandBuilder; import sonia.scm.repository.api.RepositoryService; import sonia.scm.repository.api.RepositoryServiceFactory; @@ -52,7 +50,6 @@ public class FileHistoryResourceTest extends RepositoryTestBase { public static final String FILE_HISTORY_PATH = "space/repo/history/"; public static final String FILE_HISTORY_URL = "/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + FILE_HISTORY_PATH; - private final Dispatcher dispatcher = MockDispatcherFactory.createDispatcher(); private final URI baseUri = URI.create("/"); private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(baseUri); @@ -73,23 +70,21 @@ public class FileHistoryResourceTest extends RepositoryTestBase { private FileHistoryRootResource fileHistoryRootResource; + private Dispatcher dispatcher; private final Subject subject = mock(Subject.class); private final ThreadState subjectThreadState = new SubjectThreadState(subject); - @Before - public void prepareEnvironment() throws Exception { + public void prepareEnvironment() { fileHistoryCollectionToDtoMapper = new FileHistoryCollectionToDtoMapper(changesetToChangesetDtoMapper, resourceLinks); fileHistoryRootResource = new FileHistoryRootResource(serviceFactory, fileHistoryCollectionToDtoMapper); super.fileHistoryRootResource = Providers.of(fileHistoryRootResource); - dispatcher.getRegistry().addSingletonResource(getRepositoryRootResource()); + dispatcher = DispatcherMock.createDispatcher(getRepositoryRootResource()); when(serviceFactory.create(new NamespaceAndName("space", "repo"))).thenReturn(service); when(serviceFactory.create(any(Repository.class))).thenReturn(service); when(service.getRepository()).thenReturn(new Repository("repoId", "git", "space", "repo")); - dispatcher.getProviderFactory().registerProvider(NotFoundExceptionMapper.class); - dispatcher.getProviderFactory().registerProvider(AuthorizationExceptionMapper.class); - dispatcher.getProviderFactory().registerProvider(InternalRepositoryExceptionMapper.class); + ExceptionWithContextToErrorDtoMapperImpl mapper = new ExceptionWithContextToErrorDtoMapperImpl(); when(service.getLogCommand()).thenReturn(logCommandBuilder); subjectThreadState.bind(); ThreadContext.bind(subject); @@ -133,8 +128,8 @@ public class FileHistoryResourceTest extends RepositoryTestBase { @Test - public void shouldGet404OnMissingRepository() throws URISyntaxException, RepositoryNotFoundException { - when(serviceFactory.create(any(NamespaceAndName.class))).thenThrow(RepositoryNotFoundException.class); + public void shouldGet404OnMissingRepository() throws URISyntaxException { + when(serviceFactory.create(any(NamespaceAndName.class))).thenThrow(new NotFoundException("Text", "x")); MockHttpRequest request = MockHttpRequest .get(FILE_HISTORY_URL + "revision/a.txt") .accept(VndMediaType.CHANGESET_COLLECTION); @@ -152,7 +147,7 @@ public class FileHistoryResourceTest extends RepositoryTestBase { when(logCommandBuilder.setPagingLimit(anyInt())).thenReturn(logCommandBuilder); when(logCommandBuilder.setStartChangeset(eq(id))).thenReturn(logCommandBuilder); when(logCommandBuilder.setPath(eq(path))).thenReturn(logCommandBuilder); - when(logCommandBuilder.getChangesets()).thenThrow(RevisionNotFoundException.class); + when(logCommandBuilder.getChangesets()).thenThrow(new NotFoundException("Text", "x")); MockHttpRequest request = MockHttpRequest .get(FILE_HISTORY_URL + id + "/" + path) @@ -171,7 +166,7 @@ public class FileHistoryResourceTest extends RepositoryTestBase { when(logCommandBuilder.setPagingLimit(anyInt())).thenReturn(logCommandBuilder); when(logCommandBuilder.setStartChangeset(eq(id))).thenReturn(logCommandBuilder); when(logCommandBuilder.setPath(eq(path))).thenReturn(logCommandBuilder); - when(logCommandBuilder.getChangesets()).thenThrow(InternalRepositoryException.class); + when(logCommandBuilder.getChangesets()).thenThrow(new InternalRepositoryException(ContextEntry.ContextBuilder.noContext(), "", new RuntimeException())); MockHttpRequest request = MockHttpRequest .get(FILE_HISTORY_URL + id + "/" + path) @@ -182,7 +177,7 @@ public class FileHistoryResourceTest extends RepositoryTestBase { } @Test - public void shouldGet500OnNullChangesets() throws Exception { + public void shouldGet404OnNullChangesets() throws Exception { String id = "revision_123"; String path = "root_dir/sub_dir/file-to-inspect.txt"; @@ -197,6 +192,6 @@ public class FileHistoryResourceTest extends RepositoryTestBase { .accept(VndMediaType.CHANGESET_COLLECTION); MockHttpResponse response = new MockHttpResponse(); dispatcher.invoke(request, response); - assertEquals(500, response.getStatus()); + assertEquals(404, response.getStatus()); } } diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GroupRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GroupRootResourceTest.java index 036579e9dd..f28cf49d03 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GroupRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GroupRootResourceTest.java @@ -62,7 +62,7 @@ public class GroupRootResourceTest { private ArgumentCaptor groupCaptor = ArgumentCaptor.forClass(Group.class); @Before - public void prepareEnvironment() throws Exception { + public void prepareEnvironment() { initMocks(this); when(groupManager.create(groupCaptor.capture())).thenAnswer(invocation -> invocation.getArguments()[0]); doNothing().when(groupManager).modify(groupCaptor.capture()); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MeResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MeResourceTest.java index 143b469650..454e24aa92 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MeResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MeResourceTest.java @@ -13,6 +13,7 @@ import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; +import sonia.scm.ContextEntry; import sonia.scm.user.InvalidPasswordException; import sonia.scm.user.User; import sonia.scm.user.UserManager; @@ -168,7 +169,8 @@ public class MeResourceTest { .content(content.getBytes()); MockHttpResponse response = new MockHttpResponse(); - doThrow(InvalidPasswordException.class).when(userManager).changePasswordForLoggedInUser(any(), any()); + doThrow(new InvalidPasswordException(ContextEntry.ContextBuilder.entity("passwortChange", "-"))) + .when(userManager).changePasswordForLoggedInUser(any(), any()); dispatcher.invoke(request, response); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ModificationsResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ModificationsResourceTest.java index 64942fb84b..fc4598081d 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ModificationsResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ModificationsResourceTest.java @@ -8,7 +8,6 @@ import org.apache.shiro.util.ThreadContext; import org.apache.shiro.util.ThreadState; import org.assertj.core.util.Lists; import org.jboss.resteasy.core.Dispatcher; -import org.jboss.resteasy.mock.MockDispatcherFactory; import org.jboss.resteasy.mock.MockHttpRequest; import org.jboss.resteasy.mock.MockHttpResponse; import org.junit.After; @@ -18,7 +17,6 @@ import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; -import sonia.scm.api.rest.AuthorizationExceptionMapper; import sonia.scm.repository.InternalRepositoryException; import sonia.scm.repository.Modifications; import sonia.scm.repository.NamespaceAndName; @@ -37,6 +35,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static sonia.scm.ContextEntry.ContextBuilder.noContext; @Slf4j @RunWith(MockitoJUnitRunner.Silent.class) @@ -45,7 +44,8 @@ public class ModificationsResourceTest extends RepositoryTestBase { public static final String MODIFICATIONS_PATH = "space/repo/modifications/"; public static final String MODIFICATIONS_URL = "/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + MODIFICATIONS_PATH; - private final Dispatcher dispatcher = MockDispatcherFactory.createDispatcher(); + + private Dispatcher dispatcher; private final URI baseUri = URI.create("/"); private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(baseUri); @@ -70,16 +70,13 @@ public class ModificationsResourceTest extends RepositoryTestBase { @Before - public void prepareEnvironment() throws Exception { + public void prepareEnvironment() { modificationsRootResource = new ModificationsRootResource(serviceFactory, modificationsToDtoMapper); super.modificationsRootResource = Providers.of(modificationsRootResource); - dispatcher.getRegistry().addSingletonResource(getRepositoryRootResource()); + dispatcher = DispatcherMock.createDispatcher(getRepositoryRootResource()); when(serviceFactory.create(new NamespaceAndName("space", "repo"))).thenReturn(repositoryService); when(serviceFactory.create(any(Repository.class))).thenReturn(repositoryService); when(repositoryService.getRepository()).thenReturn(new Repository("repoId", "git", "space", "repo")); - dispatcher.getProviderFactory().registerProvider(NotFoundExceptionMapper.class); - dispatcher.getProviderFactory().registerProvider(AuthorizationExceptionMapper.class); - dispatcher.getProviderFactory().registerProvider(InternalRepositoryExceptionMapper.class); when(repositoryService.getModificationsCommand()).thenReturn(modificationsCommandBuilder); subjectThreadState.bind(); ThreadContext.bind(subject); @@ -107,7 +104,7 @@ public class ModificationsResourceTest extends RepositoryTestBase { @Test public void shouldGet500OnModificationsCommandError() throws Exception { when(modificationsCommandBuilder.revision(any())).thenReturn(modificationsCommandBuilder); - when(modificationsCommandBuilder.getModifications()).thenThrow(InternalRepositoryException.class); + when(modificationsCommandBuilder.getModifications()).thenThrow(new InternalRepositoryException(noContext(), "", new RuntimeException())); MockHttpRequest request = MockHttpRequest .get(MODIFICATIONS_URL + "revision") diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PermissionRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PermissionRootResourceTest.java index 635d994763..c008e0b8db 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PermissionRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PermissionRootResourceTest.java @@ -164,10 +164,7 @@ public class PermissionRootResourceTest extends RepositoryTestBase { @TestFactory @DisplayName("test endpoints on missing permissions and user is Admin") Stream missedPermissionTestFactory() { - Repository mockRepository = mock(Repository.class); - when(mockRepository.getId()).thenReturn(REPOSITORY_NAME); - when(mockRepository.getNamespace()).thenReturn(REPOSITORY_NAMESPACE); - when(mockRepository.getName()).thenReturn(REPOSITORY_NAME); + Repository mockRepository = new Repository(REPOSITORY_NAME, "git", REPOSITORY_NAMESPACE, REPOSITORY_NAME); when(repositoryManager.get(any(NamespaceAndName.class))).thenReturn(mockRepository); return createDynamicTestsToAssertResponses( requestGETPermission.expectedResponseStatus(404), @@ -180,10 +177,6 @@ public class PermissionRootResourceTest extends RepositoryTestBase { @TestFactory @DisplayName("test endpoints on missing permissions and user is not Admin") Stream missedPermissionUserForbiddenTestFactory() { - Repository mockRepository = mock(Repository.class); - when(mockRepository.getId()).thenReturn(REPOSITORY_NAME); - when(mockRepository.getNamespace()).thenReturn(REPOSITORY_NAMESPACE); - when(mockRepository.getName()).thenReturn(REPOSITORY_NAME); doThrow(AuthorizationException.class).when(repositoryManager).get(any(NamespaceAndName.class)); return createDynamicTestsToAssertResponses( requestGETPermission.expectedResponseStatus(403), @@ -409,17 +402,17 @@ public class PermissionRootResourceTest extends RepositoryTestBase { } private Repository createUserWithRepository(String userPermission) { - Repository mockRepository = mock(Repository.class); - when(mockRepository.getId()).thenReturn(REPOSITORY_NAME); - when(mockRepository.getNamespace()).thenReturn(REPOSITORY_NAMESPACE); - when(mockRepository.getName()).thenReturn(REPOSITORY_NAME); + Repository mockRepository = new Repository(); + mockRepository.setId(REPOSITORY_NAME); + mockRepository.setNamespace(REPOSITORY_NAMESPACE); + mockRepository.setName(REPOSITORY_NAME); when(repositoryManager.get(any(NamespaceAndName.class))).thenReturn(mockRepository); when(subject.isPermitted(userPermission != null ? eq(userPermission) : any(String.class))).thenReturn(true); return mockRepository; } private void createUserWithRepositoryAndPermissions(ArrayList permissions, String userPermission) { - when(createUserWithRepository(userPermission).getPermissions()).thenReturn(permissions); + createUserWithRepository(userPermission).setPermissions(permissions); } private Stream createDynamicTestsToAssertResponses(ExpectedRequest... expectedRequests) { @@ -427,10 +420,9 @@ public class PermissionRootResourceTest extends RepositoryTestBase { .map(entry -> dynamicTest("the endpoint " + entry.description + " should return the status code " + entry.expectedResponseStatus, () -> assertExpectedRequest(entry))); } - private MockHttpResponse assertExpectedRequest(ExpectedRequest entry) throws URISyntaxException { + private void assertExpectedRequest(ExpectedRequest entry) throws URISyntaxException { MockHttpResponse response = new MockHttpResponse(); - HttpRequest request = null; - request = MockHttpRequest + HttpRequest request = MockHttpRequest .create(entry.method, "/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + entry.path) .content(entry.content) .contentType(VndMediaType.PERMISSION); @@ -442,7 +434,6 @@ public class PermissionRootResourceTest extends RepositoryTestBase { if (entry.responseValidator != null) { entry.responseValidator.accept(response); } - return response; } @ToString @@ -476,12 +467,12 @@ public class PermissionRootResourceTest extends RepositoryTestBase { return this; } - public ExpectedRequest expectedResponseStatus(int expectedResponseStatus) { + ExpectedRequest expectedResponseStatus(int expectedResponseStatus) { this.expectedResponseStatus = expectedResponseStatus; return this; } - public ExpectedRequest responseValidator(Consumer responseValidator) { + ExpectedRequest responseValidator(Consumer responseValidator) { this.responseValidator = responseValidator; return this; } diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryRootResourceTest.java index 0b2e50b6d9..fe403088f2 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryRootResourceTest.java @@ -4,6 +4,9 @@ import com.github.sdorra.shiro.ShiroRule; import com.github.sdorra.shiro.SubjectAware; import com.google.common.io.Resources; import com.google.inject.util.Providers; +import org.apache.shiro.subject.SimplePrincipalCollection; +import org.apache.shiro.subject.Subject; +import org.assertj.core.api.Assertions; import org.jboss.resteasy.core.Dispatcher; import org.jboss.resteasy.mock.MockHttpRequest; import org.jboss.resteasy.mock.MockHttpResponse; @@ -22,6 +25,7 @@ import sonia.scm.repository.RepositoryIsNotArchivedException; import sonia.scm.repository.RepositoryManager; import sonia.scm.repository.api.RepositoryService; import sonia.scm.repository.api.RepositoryServiceFactory; +import sonia.scm.user.User; import sonia.scm.web.VndMediaType; import javax.servlet.http.HttpServletResponse; @@ -37,6 +41,7 @@ import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND; import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT; import static javax.servlet.http.HttpServletResponse.SC_OK; import static javax.servlet.http.HttpServletResponse.SC_PRECONDITION_FAILED; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -59,6 +64,8 @@ import static sonia.scm.api.v2.resources.DispatcherMock.createDispatcher; ) public class RepositoryRootResourceTest extends RepositoryTestBase { + private static final String REALM = "AdminRealm"; + private Dispatcher dispatcher; @Rule @@ -96,6 +103,13 @@ public class RepositoryRootResourceTest extends RepositoryTestBase { when(serviceFactory.create(any(Repository.class))).thenReturn(service); when(scmPathInfoStore.get()).thenReturn(uriInfo); when(uriInfo.getApiRestUri()).thenReturn(URI.create("/x/y")); + SimplePrincipalCollection trillian = new SimplePrincipalCollection("trillian", REALM); + trillian.add(new User("trillian"), REALM); + shiro.setSubject( + new Subject.Builder() + .principals(trillian) + .authenticated(true) + .buildSubject()); } @Test @@ -257,6 +271,34 @@ public class RepositoryRootResourceTest extends RepositoryTestBase { verify(repositoryManager).create(any(Repository.class)); } + @Test + public void shouldSetCurrentUserAsOwner() throws Exception { + ArgumentCaptor createCaptor = ArgumentCaptor.forClass(Repository.class); + when(repositoryManager.create(createCaptor.capture())).thenAnswer(invocation -> { + Repository repository = (Repository) invocation.getArguments()[0]; + repository.setNamespace("otherspace"); + return repository; + }); + + URL url = Resources.getResource("sonia/scm/api/v2/repository-test-update.json"); + byte[] repositoryJson = Resources.toByteArray(url); + + MockHttpRequest request = MockHttpRequest + .post("/" + RepositoryRootResource.REPOSITORIES_PATH_V2) + .contentType(VndMediaType.REPOSITORY) + .content(repositoryJson); + MockHttpResponse response = new MockHttpResponse(); + + dispatcher.invoke(request, response); + + Assertions.assertThat(createCaptor.getValue().getPermissions()) + .hasSize(1) + .allSatisfy(p -> { + assertThat(p.getName()).isEqualTo("trillian"); + assertThat(p.getType()).isEqualTo(PermissionType.OWNER); + }); + } + @Test public void shouldNotOverwriteExistingPermissionsOnUpdate() throws Exception { Repository existingRepository = mockRepository("space", "repo"); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/SourceRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/SourceRootResourceTest.java index 96b8ac45f7..542c0d017f 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/SourceRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/SourceRootResourceTest.java @@ -14,7 +14,6 @@ import sonia.scm.NotFoundException; import sonia.scm.repository.BrowserResult; import sonia.scm.repository.FileObject; import sonia.scm.repository.NamespaceAndName; -import sonia.scm.repository.RepositoryNotFoundException; import sonia.scm.repository.api.BrowseCommandBuilder; import sonia.scm.repository.api.RepositoryService; import sonia.scm.repository.api.RepositoryServiceFactory; @@ -60,7 +59,7 @@ public class SourceRootResourceTest extends RepositoryTestBase { } @Test - public void shouldReturnSources() throws URISyntaxException, IOException, NotFoundException { + public void shouldReturnSources() throws URISyntaxException, IOException { BrowserResult result = createBrowserResult(); when(browseCommandBuilder.getBrowserResult()).thenReturn(result); MockHttpRequest request = MockHttpRequest.get("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo/sources"); @@ -74,8 +73,8 @@ public class SourceRootResourceTest extends RepositoryTestBase { } @Test - public void shouldReturn404IfRepoNotFound() throws URISyntaxException, RepositoryNotFoundException { - when(serviceFactory.create(new NamespaceAndName("idont", "exist"))).thenThrow(RepositoryNotFoundException.class); + public void shouldReturn404IfRepoNotFound() throws URISyntaxException { + when(serviceFactory.create(new NamespaceAndName("idont", "exist"))).thenThrow(new NotFoundException("Test", "a")); MockHttpRequest request = MockHttpRequest.get("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "idont/exist/sources"); MockHttpResponse response = new MockHttpResponse(); @@ -84,7 +83,7 @@ public class SourceRootResourceTest extends RepositoryTestBase { } @Test - public void shouldGetResultForSingleFile() throws URISyntaxException, IOException, NotFoundException { + public void shouldGetResultForSingleFile() throws URISyntaxException, IOException { FileObject fileObject = new FileObject(); fileObject.setName("File Object!"); fileObject.setPath("/"); @@ -100,8 +99,8 @@ public class SourceRootResourceTest extends RepositoryTestBase { } @Test - public void shouldGet404ForSingleFileIfRepoNotFound() throws URISyntaxException, RepositoryNotFoundException { - when(serviceFactory.create(new NamespaceAndName("idont", "exist"))).thenThrow(RepositoryNotFoundException.class); + public void shouldGet404ForSingleFileIfRepoNotFound() throws URISyntaxException { + when(serviceFactory.create(new NamespaceAndName("idont", "exist"))).thenThrow(new NotFoundException("Test", "a")); MockHttpRequest request = MockHttpRequest.get("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "idont/exist/sources/revision/fileabc"); MockHttpResponse response = new MockHttpResponse(); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/TagRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/TagRootResourceTest.java index 5f49f31183..803f2b106c 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/TagRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/TagRootResourceTest.java @@ -18,6 +18,7 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.api.rest.AuthorizationExceptionMapper; +import sonia.scm.api.v2.NotFoundExceptionMapper; import sonia.scm.repository.NamespaceAndName; import sonia.scm.repository.Repository; import sonia.scm.repository.Tag; diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserRootResourceTest.java index b363b66bfb..284e7d1d7b 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserRootResourceTest.java @@ -14,6 +14,7 @@ import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; +import sonia.scm.ContextEntry; import sonia.scm.NotFoundException; import sonia.scm.PageResult; import sonia.scm.user.ChangePasswordNotAllowedException; @@ -102,7 +103,7 @@ public class UserRootResourceTest { @Test public void shouldGet400OnCreatingNewUserWithNotAllowedCharacters() throws URISyntaxException { // the @ character at the begin of the name is not allowed - String userJson = "{ \"name\": \"@user\", \"type\": \"db\" }"; + String userJson = "{ \"name\": \"@user\",\"active\": true,\"admin\": false,\"displayName\": \"someone\",\"mail\": \"x@example.com\",\"type\": \"db\" }"; MockHttpRequest request = MockHttpRequest .post("/" + UserRootResource.USERS_PATH_V2) .contentType(VndMediaType.USER) @@ -114,7 +115,7 @@ public class UserRootResourceTest { assertEquals(400, response.getStatus()); // the whitespace at the begin opf the name is not allowed - userJson = "{ \"name\": \" user\", \"type\": \"db\" }"; + userJson = "{ \"name\": \" user\",\"active\": true,\"admin\": false,\"displayName\": \"someone\",\"mail\": \"x@example.com\",\"type\": \"db\" }"; request = MockHttpRequest .post("/" + UserRootResource.USERS_PATH_V2) .contentType(VndMediaType.USER) @@ -167,7 +168,7 @@ public class UserRootResourceTest { .content(content.getBytes()); MockHttpResponse response = new MockHttpResponse(); - doThrow(ChangePasswordNotAllowedException.class).when(userManager).overwritePassword(any(), any()); + doThrow(new ChangePasswordNotAllowedException(ContextEntry.ContextBuilder.entity("passwordChange", "-"), "xml")).when(userManager).overwritePassword(any(), any()); dispatcher.invoke(request, response); @@ -185,7 +186,7 @@ public class UserRootResourceTest { .content(content.getBytes()); MockHttpResponse response = new MockHttpResponse(); - doThrow(NotFoundException.class).when(userManager).overwritePassword(any(), any()); + doThrow(new NotFoundException("Test", "x")).when(userManager).overwritePassword(any(), any()); dispatcher.invoke(request, response); diff --git a/scm-webapp/src/test/java/sonia/scm/filter/AdminSecurityFilterTest.java b/scm-webapp/src/test/java/sonia/scm/filter/AdminSecurityFilterTest.java deleted file mode 100644 index b1d7f54ec9..0000000000 --- a/scm-webapp/src/test/java/sonia/scm/filter/AdminSecurityFilterTest.java +++ /dev/null @@ -1,85 +0,0 @@ -/** - * Copyright (c) 2014, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ - -package sonia.scm.filter; - -import com.github.sdorra.shiro.ShiroRule; -import com.github.sdorra.shiro.SubjectAware; -import org.apache.shiro.SecurityUtils; -import org.junit.Test; -import static org.junit.Assert.*; -import org.junit.Before; -import org.junit.Rule; -import org.junit.runner.RunWith; -import org.mockito.junit.MockitoJUnitRunner; -import sonia.scm.config.ScmConfiguration; - -/** - * Unit tests for {@link AdminSecurityFilter}. - * - * @author Sebastian Sdorra - */ -@RunWith(MockitoJUnitRunner.class) -@SubjectAware(configuration = "classpath:sonia/scm/shiro-001.ini") -public class AdminSecurityFilterTest { - - private AdminSecurityFilter securityFilter; - - @Rule - public ShiroRule shiro = new ShiroRule(); - - /** - * Prepare object under test and mocks. - */ - @Before - public void setUp(){ - this.securityFilter = new AdminSecurityFilter(new ScmConfiguration()); - } - - /** - * Tests {@link AdminSecurityFilter#hasPermission(org.apache.shiro.subject.Subject)} as administrator. - */ - @Test - @SubjectAware(username = "dent", password = "secret") - public void testHasPermissionAsAdministrator() { - assertTrue(securityFilter.hasPermission(SecurityUtils.getSubject())); - } - - /** - * Tests {@link AdminSecurityFilter#hasPermission(org.apache.shiro.subject.Subject)} as user. - */ - @Test - @SubjectAware(username = "trillian", password = "secret") - public void testHasPermissionAsUser() { - assertFalse(securityFilter.hasPermission(SecurityUtils.getSubject())); - } - -} diff --git a/scm-webapp/src/test/java/sonia/scm/filter/MDCFilterTest.java b/scm-webapp/src/test/java/sonia/scm/filter/MDCFilterTest.java index ac1b7335fc..efd3dbbbc0 100644 --- a/scm-webapp/src/test/java/sonia/scm/filter/MDCFilterTest.java +++ b/scm-webapp/src/test/java/sonia/scm/filter/MDCFilterTest.java @@ -98,9 +98,10 @@ public class MDCFilterTest extends AbstractTestBase { assertNotNull(chain.ctx); assertEquals("trillian", chain.ctx.get(MDCFilter.MDC_USERNAME)); assertEquals("api/v1/repositories", chain.ctx.get(MDCFilter.MDC_REQUEST_URI)); - assertEquals("127.0.0.1", chain.ctx.get(MDCFilter.MDC_CLIEN_IP)); - assertEquals("localhost", chain.ctx.get(MDCFilter.MDC_CLIEN_HOST)); + assertEquals("127.0.0.1", chain.ctx.get(MDCFilter.MDC_CLIENT_IP)); + assertEquals("localhost", chain.ctx.get(MDCFilter.MDC_CLIENT_HOST)); assertEquals("GET", chain.ctx.get(MDCFilter.MDC_REQUEST_METHOD)); + assertNotNull(chain.ctx.get(MDCFilter.MDC_TRANSACTION_ID)); } /** diff --git a/scm-webapp/src/test/java/sonia/scm/filter/SecurityFilterTest.java b/scm-webapp/src/test/java/sonia/scm/filter/PropagatePrincipleFilterTest.java similarity index 60% rename from scm-webapp/src/test/java/sonia/scm/filter/SecurityFilterTest.java rename to scm-webapp/src/test/java/sonia/scm/filter/PropagatePrincipleFilterTest.java index 50aa6980f6..0f2146beab 100644 --- a/scm-webapp/src/test/java/sonia/scm/filter/SecurityFilterTest.java +++ b/scm-webapp/src/test/java/sonia/scm/filter/PropagatePrincipleFilterTest.java @@ -33,38 +33,38 @@ package sonia.scm.filter; import com.github.sdorra.shiro.ShiroRule; import com.github.sdorra.shiro.SubjectAware; -import java.io.IOException; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.apache.shiro.subject.SimplePrincipalCollection; import org.apache.shiro.subject.Subject; -import org.junit.Test; -import static org.junit.Assert.*; -import static org.hamcrest.Matchers.*; import org.junit.Before; import org.junit.Rule; +import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; -import org.mockito.Mockito; -import static org.mockito.Mockito.*; import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.SCMContext; import sonia.scm.config.ScmConfiguration; import sonia.scm.user.User; import sonia.scm.user.UserTestData; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.verify; + /** - * Unit tests for {@link SecurityFilter}. + * Unit tests for {@link PropagatePrincipleFilter}. * * @author Sebastian Sdorra */ @RunWith(MockitoJUnitRunner.class) @SubjectAware(configuration = "classpath:sonia/scm/shiro-001.ini") -public class SecurityFilterTest { +public class PropagatePrincipleFilterTest { @Mock private HttpServletRequest request; @@ -83,7 +83,7 @@ public class SecurityFilterTest { private ScmConfiguration configuration; - private SecurityFilter securityFilter; + private PropagatePrincipleFilter propagatePrincipleFilter; @Rule public ShiroRule shiro = new ShiroRule(); @@ -94,38 +94,7 @@ public class SecurityFilterTest { @Before public void setUp(){ this.configuration = new ScmConfiguration(); - this.securityFilter = new SecurityFilter(configuration); - - when(request.getContextPath()).thenReturn("/scm"); - } - - /** - * Tests filter on authentication endpoint v1. - * - * @throws IOException - * @throws ServletException - */ - @Test - public void testDoOnAuthenticationUrlV1() throws IOException, ServletException { - checkIfAuthenticationUrlIsPassedThrough("/scm/api/auth/access_token"); - } - - /** - * Tests filter on authentication endpoint v2. - * - * @throws IOException - * @throws ServletException - */ - @Test - public void testDoOnAuthenticationUrlV2() throws IOException, ServletException { - checkIfAuthenticationUrlIsPassedThrough("/scm/api/v2/auth/access_token"); - } - - private void checkIfAuthenticationUrlIsPassedThrough(String uri) throws IOException, ServletException { - when(request.getRequestURI()).thenReturn(uri); - securityFilter.doFilter(request, response, chain); - verify(request, never()).setAttribute(Mockito.anyString(), Mockito.any()); - verify(chain).doFilter(request, response); + this.propagatePrincipleFilter = new PropagatePrincipleFilter(configuration); } /** @@ -136,8 +105,7 @@ public class SecurityFilterTest { */ @Test public void testAnonymous() throws IOException, ServletException { - when(request.getRequestURI()).thenReturn("/scm/api"); - securityFilter.doFilter(request, response, chain); + propagatePrincipleFilter.doFilter(request, response, chain); response.sendError(HttpServletResponse.SC_FORBIDDEN); } @@ -149,14 +117,13 @@ public class SecurityFilterTest { */ @Test public void testAnonymousWithAccessEnabled() throws IOException, ServletException { - when(request.getRequestURI()).thenReturn("/scm/api"); configuration.setAnonymousAccessEnabled(true); // execute - securityFilter.doFilter(request, response, chain); + propagatePrincipleFilter.doFilter(request, response, chain); // verify and capture - verify(request).setAttribute(SecurityFilter.ATTRIBUTE_REMOTE_USER, SCMContext.USER_ANONYMOUS); + verify(request).setAttribute(PropagatePrincipleFilter.ATTRIBUTE_REMOTE_USER, SCMContext.USER_ANONYMOUS); verify(chain).doFilter(requestCaptor.capture(), responseCaptor.capture()); // assert @@ -173,13 +140,12 @@ public class SecurityFilterTest { @Test public void testAuthenticated() throws IOException, ServletException { authenticateUser(UserTestData.createTrillian()); - when(request.getRequestURI()).thenReturn("/scm/api"); - + // execute - securityFilter.doFilter(request, response, chain); + propagatePrincipleFilter.doFilter(request, response, chain); // verify and capture - verify(request).setAttribute(SecurityFilter.ATTRIBUTE_REMOTE_USER, "trillian"); + verify(request).setAttribute(PropagatePrincipleFilter.ATTRIBUTE_REMOTE_USER, "trillian"); verify(chain).doFilter(requestCaptor.capture(), responseCaptor.capture()); // assert @@ -187,42 +153,6 @@ public class SecurityFilterTest { assertEquals("trillian", captured.getRemoteUser()); } - /** - * Tests filter without permissions. - * - * @throws IOException - * @throws ServletException - */ - @Test - public void testForbidden() throws IOException, ServletException { - authenticateUser(UserTestData.createTrillian()); - when(request.getRequestURI()).thenReturn("/scm/api"); - - // execute - securityFilter = new AccessForbiddenSecurityFilter(configuration); - securityFilter.doFilter(request, response, chain); - - // assert - verify(response).sendError(HttpServletResponse.SC_FORBIDDEN); - } - - /** - * Tests filter unauthenticated and without permissions. - * - * @throws IOException - * @throws ServletException - */ - @Test - public void testUnauthorized() throws IOException, ServletException { - when(request.getRequestURI()).thenReturn("/scm/api"); - - // execute - securityFilter = new AccessForbiddenSecurityFilter(configuration); - securityFilter.doFilter(request, response, chain); - - // assert - verify(response).sendError(HttpServletResponse.SC_UNAUTHORIZED); - } private void authenticateUser(User user) { SimplePrincipalCollection spc = new SimplePrincipalCollection(); @@ -236,18 +166,4 @@ public class SecurityFilterTest { shiro.setSubject(subject); } - - private static class AccessForbiddenSecurityFilter extends SecurityFilter { - - private AccessForbiddenSecurityFilter(ScmConfiguration configuration) { - super(configuration); - } - - @Override - protected boolean hasPermission(Subject subject) { - return false; - } - - } - } diff --git a/scm-webapp/src/test/java/sonia/scm/repository/DefaultRepositoryManagerPerfTest.java b/scm-webapp/src/test/java/sonia/scm/repository/DefaultRepositoryManagerPerfTest.java index 448c2561f3..9d03fa02ca 100644 --- a/scm-webapp/src/test/java/sonia/scm/repository/DefaultRepositoryManagerPerfTest.java +++ b/scm-webapp/src/test/java/sonia/scm/repository/DefaultRepositoryManagerPerfTest.java @@ -184,7 +184,7 @@ private long calculateAverage(List times) { private Repository createTestRepository(int number) { Repository repository = new Repository(keyGenerator.createKey(), REPOSITORY_TYPE, "namespace", "repo-" + number); - repository.getPermissions().add(new Permission("trillian", PermissionType.READ)); + repository.addPermission(new Permission("trillian", PermissionType.READ)); return repository; } diff --git a/scm-webapp/src/test/java/sonia/scm/repository/DefaultRepositoryManagerTest.java b/scm-webapp/src/test/java/sonia/scm/repository/DefaultRepositoryManagerTest.java index 4a28c3bc83..2b5aeb7f53 100644 --- a/scm-webapp/src/test/java/sonia/scm/repository/DefaultRepositoryManagerTest.java +++ b/scm-webapp/src/test/java/sonia/scm/repository/DefaultRepositoryManagerTest.java @@ -110,7 +110,7 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase { private String mockedNamespace = "default_namespace"; @Test - public void testCreate() throws AlreadyExistsException { + public void testCreate() { Repository heartOfGold = createTestRepository(); Repository dbRepo = manager.get(heartOfGold.getId()); @@ -122,18 +122,18 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase { username = "unpriv" ) @Test(expected = UnauthorizedException.class) - public void testCreateWithoutPrivileges() throws AlreadyExistsException { + public void testCreateWithoutPrivileges() { createTestRepository(); } @Test(expected = AlreadyExistsException.class) - public void testCreateExisting() throws AlreadyExistsException { + public void testCreateExisting() { createTestRepository(); createTestRepository(); } @Test - public void testDelete() throws Exception { + public void testDelete() { delete(manager, createTestRepository()); } @@ -141,12 +141,12 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase { username = "unpriv" ) @Test(expected = UnauthorizedException.class) - public void testDeleteWithoutPrivileges() throws Exception { + public void testDeleteWithoutPrivileges() { delete(manager, createTestRepository()); } @Test(expected = RepositoryIsNotArchivedException.class) - public void testDeleteNonArchived() throws Exception { + public void testDeleteNonArchived() { configuration.setEnableRepositoryArchive(true); delete(manager, createTestRepository()); } @@ -157,7 +157,7 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase { } @Test - public void testDeleteWithEnabledArchive() throws Exception { + public void testDeleteWithEnabledArchive() { Repository repository = createTestRepository(); repository.setArchived(true); @@ -167,7 +167,7 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase { } @Test - public void testGet() throws AlreadyExistsException { + public void testGet() { Repository heartOfGold = createTestRepository(); String id = heartOfGold.getId(); String description = heartOfGold.getDescription(); @@ -185,7 +185,7 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase { @SubjectAware( username = "crato" ) - public void testGetWithoutRequiredPrivileges() throws AlreadyExistsException { + public void testGetWithoutRequiredPrivileges() { Repository heartOfGold = RepositoryTestData.createHeartOfGold(); manager.create(heartOfGold); @@ -194,7 +194,7 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase { } @Test - public void testGetAll() throws AlreadyExistsException { + public void testGetAll() { Repository heartOfGold = createTestRepository(); Repository happyVerticalPeopleTransporter = createSecondTestRepository(); boolean foundHeart = false; @@ -232,7 +232,7 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase { @Test @SuppressWarnings("unchecked") @SubjectAware(username = "dent") - public void testGetAllWithPermissionsForTwoOrThreeRepos() throws AlreadyExistsException { + public void testGetAllWithPermissionsForTwoOrThreeRepos() { // mock key generator KeyGenerator keyGenerator = mock(KeyGenerator.class); Stack keys = new Stack<>(); @@ -273,7 +273,7 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase { } @Test - public void testEvents() throws Exception { + public void testEvents() { RepositoryManager repoManager = createRepositoryManager(false); repoManager.init(contextProvider); TestListener listener = new TestListener(); @@ -304,7 +304,7 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase { } @Test - public void testModify() throws AlreadyExistsException { + public void testModify() { Repository heartOfGold = createTestRepository(); heartOfGold.setDescription("prototype ship"); @@ -318,7 +318,7 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase { @Test @SubjectAware(username = "crato") - public void testModifyWithoutRequiredPermissions() throws AlreadyExistsException, NotFoundException { + public void testModifyWithoutRequiredPermissions() { Repository heartOfGold = RepositoryTestData.createHeartOfGold(); manager.create(heartOfGold); heartOfGold.setDescription("prototype ship"); @@ -333,7 +333,7 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase { } @Test - public void testRefresh() throws AlreadyExistsException { + public void testRefresh() { Repository heartOfGold = createTestRepository(); String description = heartOfGold.getDescription(); @@ -344,7 +344,7 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase { @Test @SubjectAware(username = "crato") - public void testRefreshWithoutRequiredPermissions() throws AlreadyExistsException, NotFoundException { + public void testRefreshWithoutRequiredPermissions() { Repository heartOfGold = RepositoryTestData.createHeartOfGold(); manager.create(heartOfGold); heartOfGold.setDescription("prototype ship"); @@ -353,13 +353,13 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase { manager.refresh(heartOfGold); } - @Test(expected = RepositoryNotFoundException.class) + @Test(expected = NotFoundException.class) public void testRefreshNotFound(){ manager.refresh(createRepositoryWithId()); } @Test - public void testRepositoryHook() throws AlreadyExistsException { + public void testRepositoryHook() { CountingReceiveHook hook = new CountingReceiveHook(); RepositoryManager repoManager = createRepositoryManager(false); @@ -379,23 +379,23 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase { } @Test - public void testNamespaceSet() throws Exception { + public void testNamespaceSet() { RepositoryManager repoManager = createRepositoryManager(false); Repository repository = spy(createTestRepository()); repository.setName("Testrepo"); - ((DefaultRepositoryManager) repoManager).create(repository); + repoManager.create(repository); assertEquals("default_namespace", repository.getNamespace()); } @Test - public void shouldSetNamespace() throws AlreadyExistsException { + public void shouldSetNamespace() { Repository repository = new Repository(null, "hg", null, "scm"); manager.create(repository); assertNotNull(repository.getId()); assertNotNull(repository.getNamespace()); } - private void createUriTestRepositories(RepositoryManager m) throws AlreadyExistsException { + private void createUriTestRepositories(RepositoryManager m) { mockedNamespace = "namespace"; createRepository(m, new Repository("1", "hg", "namespace", "scm")); createRepository(m, new Repository("2", "hg", "namespace", "scm-test")); @@ -448,7 +448,7 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase { keyGenerator, repositoryDAO, handlerSet, namespaceStrategy); } - private void createRepository(RepositoryManager m, Repository repository) throws AlreadyExistsException { + private void createRepository(RepositoryManager m, Repository repository) { m.create(repository); } @@ -471,7 +471,7 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase { assertEquals(repo.getLastModified(), other.getLastModified()); } - private Repository createRepository(Repository repository) throws AlreadyExistsException { + private Repository createRepository(Repository repository) { manager.create(repository); assertNotNull(repository.getId()); assertNotNull(manager.get(repository.getId())); @@ -486,12 +486,12 @@ public class DefaultRepositoryManagerTest extends ManagerTestBase { return repository; } - private Repository createSecondTestRepository() throws AlreadyExistsException { + private Repository createSecondTestRepository() { return createRepository( RepositoryTestData.createHappyVerticalPeopleTransporter()); } - private Repository createTestRepository() throws AlreadyExistsException { + private Repository createTestRepository() { return createRepository(RepositoryTestData.createHeartOfGold()); } diff --git a/scm-webapp/src/test/java/sonia/scm/security/SecurityRequestFilterTest.java b/scm-webapp/src/test/java/sonia/scm/security/SecurityRequestFilterTest.java new file mode 100644 index 0000000000..663198dff2 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/security/SecurityRequestFilterTest.java @@ -0,0 +1,62 @@ +package sonia.scm.security; + +import com.github.sdorra.shiro.ShiroRule; +import com.github.sdorra.shiro.SubjectAware; +import org.apache.shiro.authc.AuthenticationException; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ResourceInfo; + +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +@SubjectAware(configuration = "classpath:sonia/scm/shiro-001.ini") +public class SecurityRequestFilterTest { + + @Rule + public ShiroRule shiroRule = new ShiroRule(); + + @Mock + private ResourceInfo resourceInfo; + @Mock + private ContainerRequestContext context; + @InjectMocks + private SecurityRequestFilter securityRequestFilter; + + @Test + public void shouldAllowUnauthenticatedAccessForAnnotatedMethod() throws NoSuchMethodException { + when(resourceInfo.getResourceMethod()).thenReturn(SecurityTestClass.class.getMethod("anonymousAccessAllowed")); + + securityRequestFilter.filter(context); + } + + @Test(expected = AuthenticationException.class) + public void shouldRejectUnauthenticatedAccessForAnnotatedMethod() throws NoSuchMethodException { + when(resourceInfo.getResourceMethod()).thenReturn(SecurityTestClass.class.getMethod("loginRequired")); + + securityRequestFilter.filter(context); + } + + @Test + @SubjectAware(username = "trillian", password = "secret") + public void shouldAllowAuthenticatedAccessForMethodWithoutAnnotation() throws NoSuchMethodException { + when(resourceInfo.getResourceMethod()).thenReturn(SecurityTestClass.class.getMethod("loginRequired")); + + securityRequestFilter.filter(context); + } + + private static class SecurityTestClass { + @AllowAnonymousAccess + public void anonymousAccessAllowed() { + } + + public void loginRequired() { + } + } +} diff --git a/scm-webapp/src/test/java/sonia/scm/selenium/AuthenticationITCase.java b/scm-webapp/src/test/java/sonia/scm/selenium/AuthenticationITCase.java deleted file mode 100644 index 34013e7e76..0000000000 --- a/scm-webapp/src/test/java/sonia/scm/selenium/AuthenticationITCase.java +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Copyright (c) 2010, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ - - -package sonia.scm.selenium; - -//~--- non-JDK imports -------------------------------------------------------- - -import sonia.scm.selenium.page.Pages; -import sonia.scm.selenium.page.MainPage; -import sonia.scm.selenium.page.LoginPage; -import static org.junit.Assert.*; -import org.junit.Test; - -/** - * Authentication related selenium integration tests. - * - * @author Sebastian Sdorra - */ -public class AuthenticationITCase extends SeleniumITCaseBase { - - /** - * Authenticates an user and call logout function. - */ - @Test - public void testAuthentication() { - MainPage main = Pages.get(driver, LoginPage.class).login("scmadmin", "scmadmin"); - assertEquals("scmadmin", main.getUserInfo()); - main.logout(); - } - -} diff --git a/scm-webapp/src/test/java/sonia/scm/selenium/RepositoriesITCase.java b/scm-webapp/src/test/java/sonia/scm/selenium/RepositoriesITCase.java deleted file mode 100644 index 05898f6529..0000000000 --- a/scm-webapp/src/test/java/sonia/scm/selenium/RepositoriesITCase.java +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Copyright (c) 2010, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ - - -package sonia.scm.selenium; - -//~--- non-JDK imports -------------------------------------------------------- - -import sonia.scm.selenium.page.Pages; -import sonia.scm.selenium.page.MainPage; -import sonia.scm.selenium.page.LoginPage; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import sonia.scm.repository.Repository; - -/** - * Repository related selenium integration tests. - * - * @author Sebastian Sdorra - */ -public class RepositoriesITCase extends SeleniumITCaseBase { - - private MainPage main; - - /** - * Authenticates admin user, before each test. - */ - @Before - public void login() { - main = Pages.get(driver, LoginPage.class) - .login("scmadmin", "scmadmin"); - } - - /** - * Creates, select and removes a repository. - */ - @Test - public void createRepository() { - Repository repository = new Repository(); - repository.setName("scm"); - repository.setType("git"); - repository.setContact("scmadmin@scm-manager.org"); - repository.setDescription("SCM-Manager"); - - main.repositories() - .add(repository) - .select(repository.getName()) - .remove(); - } - - /** - * Logs the user out, after each test. - */ - @After - public void logout() { - main.logout(); - } -} diff --git a/scm-webapp/src/test/java/sonia/scm/selenium/SeleniumITCaseBase.java b/scm-webapp/src/test/java/sonia/scm/selenium/SeleniumITCaseBase.java deleted file mode 100644 index fde04d9cad..0000000000 --- a/scm-webapp/src/test/java/sonia/scm/selenium/SeleniumITCaseBase.java +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright (c) 2014, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ -package sonia.scm.selenium; - -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.firefox.FirefoxDriver; - -/** - * Base class for selenium integration tests. - * - * @author Sebastian Sdorra - */ -public class SeleniumITCaseBase { - - /** - * Selenium test driver. - */ - protected static WebDriver driver; - - /** - * Setup selenium test driver. - */ - @BeforeClass - public static void setUpDriver() { - // DesiredCapabilities capa = DesiredCapabilities.chrome(); - // capa.setBrowserName("firefox"); - // capa.setPlatform(Platform.ANY); - // RemoteWebDriver driver = new RemoteWebDriver(new URL("http://localhost:4444/wd/hub"), capa); - - driver = new FirefoxDriver(); - driver.get("http://localhost:8082/scm/index.html"); - } - - /** - * Closes the selenium test driver. - */ - @AfterClass - public static void tearDownDriver() { - driver.close(); - } -} diff --git a/scm-webapp/src/test/java/sonia/scm/selenium/page/BasePage.java b/scm-webapp/src/test/java/sonia/scm/selenium/page/BasePage.java deleted file mode 100644 index 28c27457a8..0000000000 --- a/scm-webapp/src/test/java/sonia/scm/selenium/page/BasePage.java +++ /dev/null @@ -1,168 +0,0 @@ -/** - * Copyright (c) 2014, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ -package sonia.scm.selenium.page; - -import com.google.common.base.Throwables; -import com.google.common.io.Files; -import java.io.File; -import java.io.IOException; -import java.util.List; -import java.util.concurrent.TimeUnit; -import org.openqa.selenium.By; -import org.openqa.selenium.OutputType; -import org.openqa.selenium.TakesScreenshot; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.ui.ExpectedConditions; -import org.openqa.selenium.support.ui.WebDriverWait; - -/** - * Abstract selenium base page. - * - * @author Sebastian Sdorra - * - * @param

concrete page implementation - */ -public abstract class BasePage

{ - - /** - * Selenium test driver. - */ - protected final WebDriver driver; - - /** - * Constructs a new base page. - * - * @param driver selenium test driver - */ - protected BasePage(WebDriver driver) { - this.driver = driver; - } - - /** - * Performs a {@link Thread#sleep(long)} for the given timeout. - * - * @param time timeout - * @param unit time unit of timeout - */ - protected void sleep(long time, TimeUnit unit) { - try { - unit.sleep(time); - } catch (InterruptedException ex) { - throw Throwables.propagate(ex); - } - } - - /** - * Wait for the element until it is clickable. - * - * @param by element selector - * - * @return web element - */ - protected WebElement waitToBeClickable(By by){ - return waitToBeClickable(driver.findElement(by)); - } - - /** - * Waits for the element until it is clickable. - * - * @param element web element - * - * @return web element - */ - protected WebElement waitToBeClickable(WebElement element) { - WebDriverWait wait = new WebDriverWait(driver, 5); - - return wait.until(ExpectedConditions.elementToBeClickable(element)); - } - - /** - * Waits until the element is present. - * - * @param by element locator - * - * @return web element - */ - protected WebElement waitFor(By by){ - WebDriverWait wait = new WebDriverWait(driver, 1); - return wait.until(ExpectedConditions.presenceOfElementLocated(by)); - } - - /** - * Waits until the elements are present. - * - * @param by element selector - * - * @return list of web elements - */ - protected List waitForAll(By by){ - WebDriverWait wait = new WebDriverWait(driver, 1); - return wait.until(ExpectedConditions.presenceOfAllElementsLocatedBy(by)); - } - - /** - * Creates a screenshot of the current browser content and stores it at the given path. - * - * @param target target file path - * - * @return {@code this} - */ - public P screenshot(String target) { - return screenshot(new File(target)); - } - - /** - * Creates a screenshot of the current browser content and stores it at the file. - * - * @param target target file - * - * @return {@code this} - */ - public P screenshot(File target) { - try { - File scrFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE); - - Files.copy(scrFile, target); - } catch (IOException ex) { - throw Throwables.propagate(ex); - } - return self(); - } - - /** - * Returns {@code this}. - * - * @return {@code this} - */ - protected abstract P self(); - -} diff --git a/scm-webapp/src/test/java/sonia/scm/selenium/page/LoginPage.java b/scm-webapp/src/test/java/sonia/scm/selenium/page/LoginPage.java deleted file mode 100644 index cf9d231510..0000000000 --- a/scm-webapp/src/test/java/sonia/scm/selenium/page/LoginPage.java +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Copyright (c) 2014, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ -package sonia.scm.selenium.page; - -import java.util.concurrent.TimeUnit; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; - -/** - * Page object for the scm-manager login page. - * - * @author Sebastian Sdorra - */ -public class LoginPage extends BasePage { - - @FindBy(css = "input[name=username]") - private WebElement usernameInput; - - @FindBy(css = "input[name=password]") - private WebElement passwordInput; - - @FindBy(css = "#loginButton button") - private WebElement loginButton; - - /** - * Constructs a new page. This constructor should only be called from {@link Pages}. - * - * @param driver selenium test driver - */ - LoginPage(WebDriver driver) { - super(driver); - } - - @Override - protected LoginPage self() { - return this; - } - - /** - * Authenticates the user and returns the {@link MainPage}. - * - * @param username username - * @param password password - * - * @return {@link MainPage} after successful authentication - */ - public MainPage login(String username, String password) { - usernameInput.clear(); - usernameInput.sendKeys(username); - - passwordInput.clear(); - passwordInput.sendKeys(password); - - sleep(250, TimeUnit.MILLISECONDS); - - waitToBeClickable(loginButton).click(); - - return Pages.get(driver, MainPage.class); - } -} diff --git a/scm-webapp/src/test/java/sonia/scm/selenium/page/MainPage.java b/scm-webapp/src/test/java/sonia/scm/selenium/page/MainPage.java deleted file mode 100644 index 42fc807e67..0000000000 --- a/scm-webapp/src/test/java/sonia/scm/selenium/page/MainPage.java +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Copyright (c) 2014, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ -package sonia.scm.selenium.page; - -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; - -/** - * Page object for scm-manager's main page. - * - * @author Sebastian Sdorra - */ -public class MainPage extends BasePage { - - @FindBy(css = "#navLogout a") - private WebElement logoutLink; - - @FindBy(linkText = "Repositories") - private WebElement repositoriesLink; - - @FindBy(css = "#scm-userinfo-tip") - private WebElement userInfoTip; - - /** - * Constructs a new page. This constructor should only be called from {@link Pages}. - * - * @param driver selenium test driver - */ - MainPage(WebDriver driver) { - super(driver); - } - - @Override - protected MainPage self() { - return this; - } - - /** - * Returns the name of the current authenticated user from the user info tip. - * - * @return name of the current authenticated user - */ - public String getUserInfo(){ - return userInfoTip.getText(); - } - - /** - * Navigates to the repositories page and returns the page object for this page. - * - * @return page object for repositories page - */ - public RepositoriesPage repositories(){ - repositoriesLink.click(); - return Pages.get(driver, RepositoriesPage.class); - } - - /** - * Logs the current user out. - * - * @return page object for the login - */ - public LoginPage logout(){ - waitToBeClickable(logoutLink).click(); - return Pages.get(driver, LoginPage.class); - } -} diff --git a/scm-webapp/src/test/java/sonia/scm/selenium/page/Pages.java b/scm-webapp/src/test/java/sonia/scm/selenium/page/Pages.java deleted file mode 100644 index b8d85ea08b..0000000000 --- a/scm-webapp/src/test/java/sonia/scm/selenium/page/Pages.java +++ /dev/null @@ -1,113 +0,0 @@ -/** - * Copyright (c) 2014, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ -package sonia.scm.selenium.page; - -import com.google.common.base.Throwables; -import com.google.common.collect.Lists; -import java.lang.reflect.Constructor; -import java.util.Arrays; -import java.util.List; -import org.openqa.selenium.By; -import org.openqa.selenium.SearchContext; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.PageFactory; -import org.openqa.selenium.support.pagefactory.DefaultElementLocatorFactory; -import org.openqa.selenium.support.ui.ExpectedConditions; -import org.openqa.selenium.support.ui.WebDriverWait; - -/** - * Helper class for selenium page objects. - * - * @author Sebastian Sdorra - */ -public final class Pages { - - private Pages() { - } - - /** - * Creates an instance of the given page object. - * - * @param page object type - * @param driver selenium driver - * @param clazz page object type - * @param otherArguments other constructor arguments - * - * @return instance of page object - */ - public static T get(WebDriver driver, Class clazz, Object... otherArguments) - { - T page = null; - try { - List> argumentTypes = Lists.newArrayList(); - argumentTypes.add(WebDriver.class); - for (Object argument : otherArguments) { - argumentTypes.add(argument.getClass()); - } - - List arguments = Lists.newArrayList(); - arguments.add(driver); - arguments.addAll(Arrays.asList(otherArguments)); - - Constructor constructor = clazz.getDeclaredConstructor( - argumentTypes.toArray(new Class[argumentTypes.size()]) - ); - page = constructor.newInstance(arguments.toArray(new Object[arguments.size()])); - - PageFactory.initElements(new DefaultElementLocatorFactory(new WaitingSearchContext(driver)), page); - } catch (Exception ex) { - throw Throwables.propagate(ex); - } - return page; - } - - private static class WaitingSearchContext implements SearchContext { - - private final WebDriver driver; - private final WebDriverWait wait; - - private WaitingSearchContext(WebDriver driver) { - this.driver = driver; - this.wait = new WebDriverWait(driver, 1); - } - - @Override - public List findElements(By by) { - return wait.until(ExpectedConditions.presenceOfAllElementsLocatedBy(by)); - } - - @Override - public WebElement findElement(By by) { - return wait.until(ExpectedConditions.presenceOfElementLocated(by)); - } - } -} diff --git a/scm-webapp/src/test/java/sonia/scm/selenium/page/RepositoriesAddPage.java b/scm-webapp/src/test/java/sonia/scm/selenium/page/RepositoriesAddPage.java deleted file mode 100644 index a6b6798b7f..0000000000 --- a/scm-webapp/src/test/java/sonia/scm/selenium/page/RepositoriesAddPage.java +++ /dev/null @@ -1,137 +0,0 @@ -/** - * Copyright (c) 2014, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ -package sonia.scm.selenium.page; - -import com.google.common.base.MoreObjects; -import org.openqa.selenium.By; -import org.openqa.selenium.JavascriptExecutor; -import org.openqa.selenium.NotFoundException; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; -import org.openqa.selenium.support.ui.WebDriverWait; -import sonia.scm.repository.Repository; - -import java.util.List; - -/** - * Page object for scm-manager's repository creation page. - * - * @author Sebastian Sdorra - */ -public class RepositoriesAddPage extends BasePage { - - @FindBy(css = "input[name=name]") - private WebElement nameInput; - - @FindBy(css = "input[name=contact]") - private WebElement contactInput; - - @FindBy(css = "#x-form-el-repositoryType img") - private WebElement typeInput; - - @FindBy(css = "textarea[name=description]") - private WebElement descriptionInput; - - @FindBy(css = "div.x-panel-btns button:nth-of-type(1)") - private WebElement okButton; - - private final RepositoriesPage repositoriesPage; - - /** - * Constructs a new page. This constructor should only be called from {@link Pages}. - * - * @param driver selenium test driver - * @param repositoriesPage repositories page object - */ - RepositoriesAddPage(WebDriver driver, RepositoriesPage repositoriesPage) { - super(driver); - this.repositoriesPage = repositoriesPage; - } - - @Override - protected RepositoriesAddPage self() { - return this; - } - - /** - * Creates a new {@link Repository}. - * - * @param repository repository for creation - * - * @return repositories overview page - */ - public RepositoriesPage add(Repository repository) { - nameInput.sendKeys(repository.getName()); - - selectType(repository.getType()); - - contactInput.sendKeys(repository.getContact()); - descriptionInput.sendKeys(repository.getDescription()); - - waitToBeClickable(okButton).click(); - - return repositoriesPage; - } - - private void selectType(String type) { - typeInput.click(); - - String displayName = findDisplayName(type); - - WebDriverWait wait = new WebDriverWait(driver, 1); - List elements = waitForAll(By.className("x-combo-list-item")); - WebElement typeElement = null; - for (WebElement te : elements){ - if (te.getText().trim().equalsIgnoreCase(displayName)){ - typeElement = te; - break; - } - } - - if (typeElement == null){ - throw new NotFoundException("could not find type element with type " + displayName); - } - - typeElement.click(); - } - - private String findDisplayName(String type) { - String displayName = null; - if (driver instanceof JavascriptExecutor) { - // TODO seams not to work - String script = "Sonia.repository.getTypeByName('" + type + "').displayName;"; - displayName = (String) ((JavascriptExecutor)driver).executeScript(script); - } - return MoreObjects.firstNonNull(displayName, type); - } - -} diff --git a/scm-webapp/src/test/java/sonia/scm/selenium/page/RepositoriesPage.java b/scm-webapp/src/test/java/sonia/scm/selenium/page/RepositoriesPage.java deleted file mode 100644 index fef75683ff..0000000000 --- a/scm-webapp/src/test/java/sonia/scm/selenium/page/RepositoriesPage.java +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Copyright (c) 2014, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ -package sonia.scm.selenium.page; - -import java.util.List; -import java.util.Locale; -import org.openqa.selenium.By; -import org.openqa.selenium.NotFoundException; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; -import sonia.scm.repository.Repository; - -/** - * Page object for scm-manager's repositories overview page. - * - * @author Sebastian Sdorra - */ -public class RepositoriesPage extends BasePage { - - @FindBy(id = "repositoryAddButton") - private WebElement addButton; - - /** - * Constructs a new page. This constructor should only be called from {@link Pages}. - * - * @param driver selenium test driver - */ - RepositoriesPage(WebDriver driver) { - super(driver); - } - - @Override - protected RepositoriesPage self() { - return this; - } - - /** - * Creates a new {@link Repository}. - * - * @param repository repository for creation - * - * @return {@link this} - */ - public RepositoriesPage add(Repository repository){ - addButton.click(); - RepositoriesAddPage addPage = Pages.get(driver, RepositoriesAddPage.class, this); - return addPage.add(repository); - } - - /** - * Selects the repository with the given name and returns the detail page object for the selected repository. - * - * @param repositoryName name of the repository - * - * @return page object for selected repository - */ - public RepositoryPage select(String repositoryName){ - WebElement repositoryNameColumn = null; - - List elements = waitForAll(By.className("x-grid3-col-name")); - for (WebElement element : elements){ - if (element.getText().trim().toLowerCase(Locale.ENGLISH).equals(repositoryName)){ - repositoryNameColumn = element; - break; - } - } - - if ( repositoryNameColumn == null ) { - throw new NotFoundException("could not find repository " + repositoryName); - } - - return Pages.get(driver, RepositoryPage.class, this); - } -} diff --git a/scm-webapp/src/test/java/sonia/scm/selenium/page/RepositoryPage.java b/scm-webapp/src/test/java/sonia/scm/selenium/page/RepositoryPage.java deleted file mode 100644 index d09a0debb5..0000000000 --- a/scm-webapp/src/test/java/sonia/scm/selenium/page/RepositoryPage.java +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright (c) 2014, Sebastian Sdorra - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of SCM-Manager; nor the names of its - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * http://bitbucket.org/sdorra/scm-manager - * - */ -package sonia.scm.selenium.page; - -import org.openqa.selenium.By; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.support.FindBy; - -/** - * Page object for scm-manager's repository detail page. - * - * @author Sebastian Sdorra - */ -public class RepositoryPage extends BasePage { - - @FindBy(css = "#repoRmButton button") - private WebElement removeButton; - - private final RepositoriesPage repositoriesPage; - - /** - * Constructs a new page. This constructor should only be called from {@link Pages}. - * - * @param driver selenium test driver - * @param repositoriesPage repositories page object - */ - RepositoryPage(WebDriver driver, RepositoriesPage repositoriesPage) { - super(driver); - this.repositoriesPage = repositoriesPage; - } - - @Override - protected RepositoryPage self() { - return this; - } - - /** - * Removes the selected repository. - * - * @return repositories overview page object - */ - public RepositoriesPage remove(){ - removeButton.click(); - waitToBeClickable(By.cssSelector("div.x-window button:nth-of-type(1)")).click(); - return repositoriesPage; - } - -} diff --git a/scm-webapp/src/test/java/sonia/scm/user/DefaultUserManagerTest.java b/scm-webapp/src/test/java/sonia/scm/user/DefaultUserManagerTest.java index df32528e5e..1614bf790a 100644 --- a/scm-webapp/src/test/java/sonia/scm/user/DefaultUserManagerTest.java +++ b/scm-webapp/src/test/java/sonia/scm/user/DefaultUserManagerTest.java @@ -40,7 +40,6 @@ import com.github.sdorra.shiro.SubjectAware; import com.google.common.collect.Lists; import org.assertj.core.api.Assertions; -import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -48,7 +47,6 @@ import org.mockito.ArgumentCaptor; import sonia.scm.NotFoundException; import sonia.scm.store.JAXBConfigurationStoreFactory; import sonia.scm.user.xml.XmlUserDAO; -import sonia.scm.util.MockUtil; import static org.mockito.Mockito.*; @@ -57,7 +55,6 @@ import static org.mockito.Mockito.*; import java.util.Collections; import java.util.List; import org.junit.Rule; -import sonia.scm.store.ConfigurationStoreFactory; /** * diff --git a/scm-webapp/src/test/java/sonia/scm/web/protocol/HttpProtocolServletTest.java b/scm-webapp/src/test/java/sonia/scm/web/protocol/HttpProtocolServletTest.java index 077020f60c..1bd6358c95 100644 --- a/scm-webapp/src/test/java/sonia/scm/web/protocol/HttpProtocolServletTest.java +++ b/scm-webapp/src/test/java/sonia/scm/web/protocol/HttpProtocolServletTest.java @@ -4,11 +4,11 @@ import org.junit.Before; import org.junit.Test; import org.mockito.InjectMocks; import org.mockito.Mock; +import sonia.scm.NotFoundException; import sonia.scm.PushStateDispatcher; import sonia.scm.repository.DefaultRepositoryProvider; import sonia.scm.repository.NamespaceAndName; import sonia.scm.repository.Repository; -import sonia.scm.repository.RepositoryNotFoundException; import sonia.scm.repository.api.RepositoryService; import sonia.scm.repository.api.RepositoryServiceFactory; import sonia.scm.repository.spi.HttpScmProtocol; @@ -58,12 +58,12 @@ public class HttpProtocolServletTest { private HttpScmProtocol protocol; @Before - public void init() throws RepositoryNotFoundException { + public void init() { initMocks(this); when(userAgentParser.parse(request)).thenReturn(userAgent); when(userAgent.isBrowser()).thenReturn(false); NamespaceAndName existingRepo = new NamespaceAndName("space", "repo"); - when(serviceFactory.create(not(eq(existingRepo)))).thenThrow(RepositoryNotFoundException.class); + when(serviceFactory.create(not(eq(existingRepo)))).thenThrow(new NotFoundException("Test", "a")); when(serviceFactory.create(existingRepo)).thenReturn(repositoryService); when(requestProvider.get()).thenReturn(httpServletRequest); } @@ -97,7 +97,7 @@ public class HttpProtocolServletTest { } @Test - public void shouldDelegateToProvider() throws RepositoryNotFoundException, IOException, ServletException { + public void shouldDelegateToProvider() throws IOException, ServletException { when(request.getPathInfo()).thenReturn("/space/name"); NamespaceAndName namespaceAndName = new NamespaceAndName("space", "name"); doReturn(repositoryService).when(serviceFactory).create(namespaceAndName);