@@ -70,10 +70,16 @@ public interface ErrorKeys | |||
String REPOSITORY_GROUP_DELETE_FAILED = REPOSITORY_GROUP_PREFIX + "delete.failed"; | |||
String REPOSITORY_NOT_FOUND = REPOSITORY_PREFIX + "notfound"; | |||
String REPOSITORY_MANAGED_NOT_FOUND = REPOSITORY_PREFIX + ".managed.notfound"; | |||
String REPOSITORY_MANAGED_NOT_FOUND = REPOSITORY_PREFIX + "managed.notfound"; | |||
String REPOSITORY_REMOTE_NOT_FOUND = REPOSITORY_PREFIX + "remote.notfound"; | |||
String REPOSITORY_METADATA_ERROR = REPOSITORY_PREFIX + "metadata_error"; | |||
String TASK_QUEUE_FAILED = PREFIX + "task.queue_failed"; | |||
String REPOSITORY_SCAN_FAILED = REPOSITORY_PREFIX + "scan.failed"; | |||
String ARTIFACT_EXISTS_AT_DEST = REPOSITORY_PREFIX + "artifact.dest.exists"; | |||
String REPOSITORY_REMOTE_INDEX_DOWNLOAD_FAILED = REPOSITORY_PREFIX + "remote.index.download_failed"; | |||
} |
@@ -27,6 +27,7 @@ import io.swagger.v3.oas.annotations.security.SecurityRequirement; | |||
import io.swagger.v3.oas.annotations.tags.Tag; | |||
import org.apache.archiva.components.rest.model.PagedResult; | |||
import org.apache.archiva.redback.authorization.RedbackAuthorization; | |||
import org.apache.archiva.rest.api.model.v2.Artifact; | |||
import org.apache.archiva.rest.api.model.v2.FileInfo; | |||
import org.apache.archiva.rest.api.model.v2.MavenManagedRepository; | |||
import org.apache.archiva.security.common.ArchivaRoleConstants; | |||
@@ -202,7 +203,7 @@ public interface MavenManagedRepositoryService | |||
throws ArchivaRestServiceException; | |||
@Path( "{id}/files/{filePath: .+}" ) | |||
@Path( "{id}/a/{filePath: .+}" ) | |||
@GET | |||
@Produces( {MediaType.APPLICATION_JSON} ) | |||
@RedbackAuthorization( permissions = ArchivaRoleConstants.OPERATION_MANAGE_CONFIGURATION ) | |||
@@ -227,7 +228,59 @@ public interface MavenManagedRepositoryService | |||
throws ArchivaRestServiceException; | |||
@Path ("{id}/content/{namespace}/{projectId}/{version}") | |||
/** | |||
* permissions are checked in impl | |||
* will copy an artifact from the source repository to the target repository | |||
*/ | |||
@Path ("{srcId}/a/{path: .+}/copyto/{dstId}") | |||
@POST | |||
@Produces({APPLICATION_JSON}) | |||
@RedbackAuthorization (noPermission = true) | |||
@Operation( summary = "Copies a artifact from the source repository to the destination repository", | |||
security = { | |||
@SecurityRequirement( | |||
name = ArchivaRoleConstants.OPERATION_RUN_INDEXER | |||
) | |||
}, | |||
responses = { | |||
@ApiResponse( responseCode = "200", | |||
description = "If the artifact was copied" | |||
), | |||
@ApiResponse( responseCode = "403", description = "Authenticated user is not permitted to gather the information", | |||
content = @Content( mediaType = APPLICATION_JSON, schema = @Schema( implementation = ArchivaRestError.class ) ) ), | |||
@ApiResponse( responseCode = "404", description = "The repository does not exist, or if the artifact was not found", | |||
content = @Content( mediaType = APPLICATION_JSON, schema = @Schema( implementation = ArchivaRestError.class ) ) ) | |||
} | |||
) | |||
Response copyArtifact( @PathParam( "srcId" ) String srcRepositoryId, @PathParam( "dstId" ) String dstRepositoryId, | |||
@PathParam( "path" ) String path ) | |||
throws ArchivaRestServiceException; | |||
@Path ("{id}/a/{path: .+}") | |||
@DELETE | |||
@Consumes ({ APPLICATION_JSON }) | |||
@RedbackAuthorization (noPermission = true) | |||
@Operation( summary = "Deletes a artifact in the repository.", | |||
security = { | |||
@SecurityRequirement( | |||
name = ArchivaRoleConstants.OPERATION_RUN_INDEXER | |||
) | |||
}, | |||
responses = { | |||
@ApiResponse( responseCode = "200", | |||
description = "If the artifact was deleted" | |||
), | |||
@ApiResponse( responseCode = "403", description = "Authenticated user is not permitted to gather the information", | |||
content = @Content( mediaType = APPLICATION_JSON, schema = @Schema( implementation = ArchivaRestError.class ) ) ), | |||
@ApiResponse( responseCode = "404", description = "The repository or the artifact does not exist", | |||
content = @Content( mediaType = APPLICATION_JSON, schema = @Schema( implementation = ArchivaRestError.class ) ) ) | |||
} | |||
) | |||
Response deleteArtifact( @PathParam( "id" ) String repositoryId, @PathParam( "path" ) String path ) | |||
throws ArchivaRestServiceException; | |||
@Path ("{id}/c/{namespace}/{projectId}/{version}") | |||
@DELETE | |||
@Produces ({ MediaType.APPLICATION_JSON }) | |||
@RedbackAuthorization (noPermission = true) | |||
@@ -253,7 +306,7 @@ public interface MavenManagedRepositoryService | |||
throws org.apache.archiva.rest.api.services.ArchivaRestServiceException; | |||
@Path ( "{id}/content/{namespace}/{projectId}" ) | |||
@Path ( "{id}/c/{namespace}/{projectId}" ) | |||
@DELETE | |||
@Produces ({ MediaType.APPLICATION_JSON }) | |||
@RedbackAuthorization (noPermission = true) | |||
@@ -276,7 +329,7 @@ public interface MavenManagedRepositoryService | |||
Response deleteProject( @PathParam ("id") String repositoryId, @PathParam ( "namespace" ) String namespace, @PathParam ("projectId") String projectId ) | |||
throws org.apache.archiva.rest.api.services.ArchivaRestServiceException; | |||
@Path ( "{id}/content/{namespace}" ) | |||
@Path ( "{id}/c/{namespace}" ) | |||
@DELETE | |||
@Produces ({ MediaType.APPLICATION_JSON }) | |||
@RedbackAuthorization (noPermission = true) |
@@ -26,8 +26,6 @@ import io.swagger.v3.oas.annotations.security.SecurityRequirement; | |||
import io.swagger.v3.oas.annotations.tags.Tag; | |||
import org.apache.archiva.components.rest.model.PagedResult; | |||
import org.apache.archiva.redback.authorization.RedbackAuthorization; | |||
import org.apache.archiva.rest.api.model.v2.Artifact; | |||
import org.apache.archiva.rest.api.model.v2.ArtifactTransferRequest; | |||
import org.apache.archiva.rest.api.model.v2.Repository; | |||
import org.apache.archiva.rest.api.model.v2.RepositoryStatistics; | |||
import org.apache.archiva.rest.api.model.v2.ScanStatus; | |||
@@ -42,8 +40,9 @@ 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.MediaType; | |||
import javax.ws.rs.core.Context; | |||
import javax.ws.rs.core.Response; | |||
import javax.ws.rs.core.UriInfo; | |||
import java.util.List; | |||
import static javax.ws.rs.core.MediaType.APPLICATION_JSON; | |||
@@ -170,13 +169,6 @@ public interface RepositoryService | |||
throws ArchivaRestServiceException; | |||
/** | |||
* Returns the scan status of the given repository | |||
* @param repositoryId the repository identifier | |||
* @return the status | |||
* @throws ArchivaRestServiceException | |||
* @since 3.0 | |||
*/ | |||
@Path ("managed/{id}/scan/status") | |||
@GET | |||
@Produces ({ APPLICATION_JSON }) | |||
@@ -205,7 +197,7 @@ public interface RepositoryService | |||
@DELETE | |||
@Produces ({ APPLICATION_JSON }) | |||
@RedbackAuthorization (permissions = ArchivaRoleConstants.OPERATION_RUN_INDEXER) | |||
@Operation( summary = "Removes a scan task from the queue.", | |||
@Operation( summary = "Cancels and removes all tasks for the given repository.", | |||
security = { | |||
@SecurityRequirement( | |||
name = ArchivaRoleConstants.OPERATION_RUN_INDEXER | |||
@@ -224,40 +216,17 @@ public interface RepositoryService | |||
Response removeScanningTaskFromQueue( @PathParam ("id") String repositoryId ) | |||
throws ArchivaRestServiceException; | |||
/** | |||
* permissions are checked in impl | |||
* will copy an artifact from the source repository to the target repository | |||
*/ | |||
@Path ("managed/{id}/artifact/copy") | |||
@POST | |||
@Produces({APPLICATION_JSON}) | |||
@Consumes({ APPLICATION_JSON }) | |||
@RedbackAuthorization (noPermission = true) | |||
@Operation( summary = "Copies a artifact between repositories.", | |||
security = { | |||
@SecurityRequirement( | |||
name = ArchivaRoleConstants.OPERATION_RUN_INDEXER | |||
) | |||
}, | |||
responses = { | |||
@ApiResponse( responseCode = "200", | |||
description = "If the artifact was copied" | |||
), | |||
@ApiResponse( responseCode = "403", description = "Authenticated user is not permitted to gather the information", | |||
content = @Content( mediaType = APPLICATION_JSON, schema = @Schema( implementation = ArchivaRestError.class ) ) ), | |||
@ApiResponse( responseCode = "404", description = "The repository does not exist, or if the artifact was not found", | |||
content = @Content( mediaType = APPLICATION_JSON, schema = @Schema( implementation = ArchivaRestError.class ) ) ) | |||
} | |||
) | |||
Response copyArtifact( ArtifactTransferRequest artifactTransferRequest ) | |||
throws ArchivaRestServiceException; | |||
@Path ("managed/{id}/index/download/start") | |||
@Path ("remote/{id}/index/download/start") | |||
@POST | |||
@Produces ({ APPLICATION_JSON }) | |||
@Consumes({ APPLICATION_JSON }) | |||
@RedbackAuthorization (permissions = ArchivaRoleConstants.OPERATION_RUN_INDEXER) | |||
@Operation( summary = "Schedules a task for remote index download.", | |||
parameters = { | |||
@Parameter( name = "full", description = "If true, download the full index, otherwise try a update download." ) | |||
}, | |||
security = { | |||
@SecurityRequirement( | |||
name = ArchivaRoleConstants.OPERATION_RUN_INDEXER | |||
@@ -274,38 +243,15 @@ public interface RepositoryService | |||
} | |||
) | |||
Response scheduleDownloadRemoteIndex( @PathParam ("id") String repositoryId, | |||
@QueryParam ("now") boolean now, | |||
@QueryParam ("fullDownload") boolean fullDownload ) | |||
@QueryParam( "immediate" ) boolean immediately, | |||
@QueryParam ("full") boolean full, @Context UriInfo uriInfo ) | |||
throws ArchivaRestServiceException; | |||
@Path ("managed/{id}/artifact") | |||
@DELETE | |||
@Consumes ({ APPLICATION_JSON }) | |||
@RedbackAuthorization (noPermission = true) | |||
@Operation( summary = "Deletes a artifact in the repository.", | |||
security = { | |||
@SecurityRequirement( | |||
name = ArchivaRoleConstants.OPERATION_RUN_INDEXER | |||
) | |||
}, | |||
responses = { | |||
@ApiResponse( responseCode = "200", | |||
description = "If the artifact was deleted" | |||
), | |||
@ApiResponse( responseCode = "403", description = "Authenticated user is not permitted to gather the information", | |||
content = @Content( mediaType = APPLICATION_JSON, schema = @Schema( implementation = ArchivaRestError.class ) ) ), | |||
@ApiResponse( responseCode = "404", description = "The repository or the artifact does not exist", | |||
content = @Content( mediaType = APPLICATION_JSON, schema = @Schema( implementation = ArchivaRestError.class ) ) ) | |||
} | |||
) | |||
Response deleteArtifact( Artifact artifact ) | |||
throws ArchivaRestServiceException; | |||
@Path ("index_downloads") | |||
@Path ("remote/index/downloads") | |||
@GET | |||
@Produces ({ APPLICATION_JSON }) | |||
@RedbackAuthorization (noPermission = true) | |||
@RedbackAuthorization (permissions = ArchivaRoleConstants.OPERATION_RUN_INDEXER) | |||
@Operation( summary = "Returns a list of running downloads from the remote repository.", | |||
security = { | |||
@SecurityRequirement( |
@@ -21,6 +21,9 @@ import org.apache.archiva.admin.model.RepositoryAdminException; | |||
import org.apache.archiva.admin.model.managed.ManagedRepositoryAdmin; | |||
import org.apache.archiva.components.rest.model.PagedResult; | |||
import org.apache.archiva.components.rest.util.QueryHelper; | |||
import org.apache.archiva.repository.ManagedRepository; | |||
import org.apache.archiva.repository.RepositoryRegistry; | |||
import org.apache.archiva.rest.api.model.v2.Artifact; | |||
import org.apache.archiva.rest.api.model.v2.FileInfo; | |||
import org.apache.archiva.rest.api.model.v2.MavenManagedRepository; | |||
import org.apache.archiva.rest.api.services.v2.ArchivaRestServiceException; | |||
@@ -29,14 +32,17 @@ import org.apache.archiva.rest.api.services.v2.ErrorMessage; | |||
import org.apache.archiva.rest.api.services.v2.MavenManagedRepositoryService; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.stereotype.Service; | |||
import javax.inject.Inject; | |||
import javax.ws.rs.PathParam; | |||
import javax.ws.rs.core.Response; | |||
import java.util.List; | |||
/** | |||
* @author Martin Stockhammer <martin_s@apache.org> | |||
*/ | |||
@Service("v2.managedMavenRepositoryService#rest") | |||
public class DefaultMavenManagedRepositoryService implements MavenManagedRepositoryService | |||
{ | |||
private static final Logger log = LoggerFactory.getLogger( DefaultMavenManagedRepositoryService.class ); | |||
@@ -52,9 +58,13 @@ public class DefaultMavenManagedRepositoryService implements MavenManagedReposit | |||
QUERY_HELPER.addNullsafeFieldComparator( "name", org.apache.archiva.admin.model.beans.ManagedRepository::getName ); | |||
} | |||
@Inject | |||
private ManagedRepositoryAdmin managedRepositoryAdmin; | |||
private RepositoryRegistry repositoryRegistry; | |||
public DefaultMavenManagedRepositoryService( RepositoryRegistry repositoryRegistry, ManagedRepositoryAdmin managedRepositoryAdmin ) | |||
{ | |||
this.managedRepositoryAdmin = managedRepositoryAdmin; | |||
} | |||
@Override | |||
public PagedResult<MavenManagedRepository> getManagedRepositories( String searchTerm, Integer offset, Integer limit, List<String> orderBy, String order ) throws ArchivaRestServiceException | |||
@@ -106,6 +116,33 @@ public class DefaultMavenManagedRepositoryService implements MavenManagedReposit | |||
return null; | |||
} | |||
@Override | |||
public Response copyArtifact( String srcRepositoryId, String dstRepositoryId, | |||
String path ) throws ArchivaRestServiceException | |||
{ | |||
ManagedRepository srcRepo = repositoryRegistry.getManagedRepository( srcRepositoryId ); | |||
if (srcRepo==null) { | |||
throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.REPOSITORY_NOT_FOUND, srcRepositoryId ), 404 ); | |||
} | |||
ManagedRepository dstRepo = repositoryRegistry.getManagedRepository( dstRepositoryId ); | |||
if (dstRepo==null) { | |||
throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.REPOSITORY_NOT_FOUND, dstRepositoryId ), 404 ); | |||
} | |||
if (dstRepo.getAsset( path ).exists()) { | |||
throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.ARTIFACT_EXISTS_AT_DEST, path ) ); | |||
} | |||
return null; | |||
} | |||
@Override | |||
public Response deleteArtifact( String repositoryId, String path ) throws ArchivaRestServiceException | |||
{ | |||
return null; | |||
} | |||
@Override | |||
public Response removeProjectVersion( String repositoryId, String namespace, String projectId, String version ) throws org.apache.archiva.rest.api.services.ArchivaRestServiceException | |||
{ |
@@ -21,13 +21,13 @@ import org.apache.archiva.admin.model.RepositoryAdminException; | |||
import org.apache.archiva.admin.model.admin.RepositoryTaskAdministration; | |||
import org.apache.archiva.components.rest.model.PagedResult; | |||
import org.apache.archiva.components.rest.util.QueryHelper; | |||
import org.apache.archiva.components.rest.util.RestUtil; | |||
import org.apache.archiva.metadata.repository.MetadataRepositoryException; | |||
import org.apache.archiva.metadata.repository.stats.model.RepositoryStatisticsManager; | |||
import org.apache.archiva.repository.RemoteRepository; | |||
import org.apache.archiva.repository.RepositoryRegistry; | |||
import org.apache.archiva.repository.scanner.RepositoryScanner; | |||
import org.apache.archiva.repository.scanner.RepositoryScannerException; | |||
import org.apache.archiva.rest.api.model.v2.Artifact; | |||
import org.apache.archiva.rest.api.model.v2.ArtifactTransferRequest; | |||
import org.apache.archiva.rest.api.model.v2.Repository; | |||
import org.apache.archiva.rest.api.model.v2.RepositoryStatistics; | |||
import org.apache.archiva.rest.api.model.v2.ScanStatus; | |||
@@ -35,6 +35,8 @@ import org.apache.archiva.rest.api.services.v2.ArchivaRestServiceException; | |||
import org.apache.archiva.rest.api.services.v2.ErrorKeys; | |||
import org.apache.archiva.rest.api.services.v2.ErrorMessage; | |||
import org.apache.archiva.rest.api.services.v2.RepositoryService; | |||
import org.apache.archiva.scheduler.indexing.DownloadRemoteIndexException; | |||
import org.apache.archiva.scheduler.indexing.DownloadRemoteIndexScheduler; | |||
import org.apache.commons.lang3.StringUtils; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
@@ -42,6 +44,7 @@ import org.springframework.stereotype.Service; | |||
import javax.inject.Named; | |||
import javax.ws.rs.core.Response; | |||
import javax.ws.rs.core.UriInfo; | |||
import java.util.Comparator; | |||
import java.util.List; | |||
import java.util.Locale; | |||
@@ -66,6 +69,7 @@ public class DefaultRepositoryService implements RepositoryService | |||
private final RepositoryScanner repoScanner; | |||
private final DownloadRemoteIndexScheduler downloadRemoteIndexScheduler; | |||
private static final Logger log = LoggerFactory.getLogger( DefaultRepositoryService.class ); | |||
private static final QueryHelper<org.apache.archiva.repository.Repository> QUERY_HELPER = new QueryHelper<>( new String[]{"id", "name"} ); | |||
@@ -85,12 +89,13 @@ public class DefaultRepositoryService implements RepositoryService | |||
public DefaultRepositoryService( RepositoryRegistry repositoryRegistry, RepositoryStatisticsManager repositoryStatisticsManager, | |||
@Named( value = "repositoryTaskAdministration#default") RepositoryTaskAdministration repositoryTaskAdministration, | |||
RepositoryScanner repoScanner ) | |||
RepositoryScanner repoScanner, DownloadRemoteIndexScheduler downloadRemoteIndexScheduler ) | |||
{ | |||
this.repositoryRegistry = repositoryRegistry; | |||
this.repositoryStatisticsManager = repositoryStatisticsManager; | |||
this.repoScanner = repoScanner; | |||
this.repositoryTaskAdministration = repositoryTaskAdministration; | |||
this.downloadRemoteIndexScheduler = downloadRemoteIndexScheduler; | |||
} | |||
private void handleAdminException( RepositoryAdminException e ) throws ArchivaRestServiceException | |||
@@ -187,37 +192,53 @@ public class DefaultRepositoryService implements RepositoryService | |||
catch ( RepositoryAdminException e ) | |||
{ | |||
handleAdminException( e ); | |||
return null; | |||
return new ScanStatus(); | |||
} | |||
} | |||
@Override | |||
public Response removeScanningTaskFromQueue( String repositoryId ) throws ArchivaRestServiceException | |||
{ | |||
return null; | |||
try | |||
{ | |||
repositoryTaskAdministration.cancelTasks( repositoryId ); | |||
return Response.ok( ).build( ); | |||
} | |||
catch ( RepositoryAdminException e ) | |||
{ | |||
handleAdminException( e ); | |||
return Response.serverError( ).build( ); | |||
} | |||
} | |||
@Override | |||
public Response copyArtifact( ArtifactTransferRequest artifactTransferRequest ) throws ArchivaRestServiceException | |||
{ | |||
return null; | |||
} | |||
@Override | |||
public Response scheduleDownloadRemoteIndex( String repositoryId, boolean now, boolean fullDownload ) throws ArchivaRestServiceException | |||
public Response scheduleDownloadRemoteIndex( String repositoryId, boolean immediately, boolean full, | |||
UriInfo uriInfo ) throws ArchivaRestServiceException | |||
{ | |||
return null; | |||
boolean immediateSet = RestUtil.isFlagSet( uriInfo, "immediate" ); | |||
boolean fullSet = RestUtil.isFlagSet( uriInfo, "full" ); | |||
RemoteRepository repo = repositoryRegistry.getRemoteRepository( repositoryId ); | |||
if (repo==null) { | |||
throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.REPOSITORY_REMOTE_NOT_FOUND, repositoryId ), 404 ); | |||
} | |||
try | |||
{ | |||
downloadRemoteIndexScheduler.scheduleDownloadRemote( repositoryId, immediateSet, fullSet ); | |||
return Response.ok( ).build( ); | |||
} | |||
catch ( DownloadRemoteIndexException e ) | |||
{ | |||
log.error( "Could not schedule index download for repository {}: {}", repositoryId, e.getMessage(), e ); | |||
throw new ArchivaRestServiceException( ErrorMessage.of( ErrorKeys.REPOSITORY_REMOTE_INDEX_DOWNLOAD_FAILED, e.getMessage( ) ) ); | |||
} | |||
} | |||
@Override | |||
public Response deleteArtifact( Artifact artifact ) throws ArchivaRestServiceException | |||
{ | |||
return null; | |||
} | |||
@Override | |||
public List<String> getRunningRemoteDownloads( ) | |||
{ | |||
return null; | |||
return downloadRemoteIndexScheduler.getRunningRemoteDownloadIds( ); | |||
} | |||
} |
@@ -159,4 +159,33 @@ public class NativeRepositoryServiceTest extends AbstractNativeRestServices | |||
assertNotNull( response ); | |||
} | |||
@Test | |||
void scheduleIndexDownload() { | |||
String token = getAdminToken( ); | |||
Response response = given( ).spec( getRequestSpec( token ) ).contentType( JSON ) | |||
.when( ) | |||
.post( "remote/central/index/download/start" ) | |||
.then( ).statusCode( 200 ).extract( ).response( ); | |||
assertNotNull( response ); | |||
} | |||
@Test | |||
void getIndexDownloadList() { | |||
String token = getAdminToken( ); | |||
given( ).spec( getRequestSpec( token ) ).contentType( JSON ) | |||
.when( ) | |||
.queryParam( "immediate", "true" ) | |||
.post( "remote/central/index/download/start" ) | |||
.then( ).statusCode( 200 ).extract( ).response( ); | |||
Response response = given( ).spec( getRequestSpec( token ) ).contentType( JSON ) | |||
.when( ) | |||
.get( "remote/index/downloads" ) | |||
.then( ).statusCode( 200 ).extract( ).response( ); | |||
assertNotNull( response ); | |||
List<String> downloads = response.getBody( ).jsonPath( ).getList( "", String.class ); | |||
assertEquals( 1, downloads.size() ); | |||
assertEquals( "central", downloads.get( 0 ) ); | |||
} | |||
} |