/**
* Copyright (C) 2013 Red Hat, Inc. (jdcasey@commonjava.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.commonjava.cartographer.client;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpStatus;
import org.commonjava.cartographer.CartoAPIObjectMapperModules;
import org.commonjava.cartographer.request.GraphAnalysisRequest;
import org.commonjava.cartographer.request.GraphCalculation;
import org.commonjava.cartographer.request.MetadataCollationRequest;
import org.commonjava.cartographer.request.MetadataExtractionRequest;
import org.commonjava.cartographer.request.MetadataUpdateRequest;
import org.commonjava.cartographer.request.MultiGraphRequest;
import org.commonjava.cartographer.request.MultiRenderRequest;
import org.commonjava.cartographer.request.PathsRequest;
import org.commonjava.cartographer.request.PomRequest;
import org.commonjava.cartographer.request.ProjectGraphRelationshipsRequest;
import org.commonjava.cartographer.request.ProjectGraphRequest;
import org.commonjava.cartographer.request.RepositoryContentRequest;
import org.commonjava.cartographer.request.SingleGraphRequest;
import org.commonjava.cartographer.request.SourceAliasRequest;
import org.commonjava.cartographer.request.build.GraphAnalysisRequestBuilder;
import org.commonjava.cartographer.request.build.GraphCompositionBuilder;
import org.commonjava.cartographer.request.build.GraphDescriptionBuilder;
import org.commonjava.cartographer.request.build.MetadataCollationRequestBuilder;
import org.commonjava.cartographer.request.build.MetadataExtractionRequestBuilder;
import org.commonjava.cartographer.request.build.MetadataUpdateRequestBuilder;
import org.commonjava.cartographer.request.build.MultiGraphRequestBuilder;
import org.commonjava.cartographer.request.build.MultiRenderRequestBuilder;
import org.commonjava.cartographer.request.build.PathsRequestBuilder;
import org.commonjava.cartographer.request.build.PomRequestBuilder;
import org.commonjava.cartographer.request.build.ProjectGraphRelationshipsRequestBuilder;
import org.commonjava.cartographer.request.build.ProjectGraphRequestBuilder;
import org.commonjava.cartographer.request.build.RepositoryContentRequestBuilder;
import org.commonjava.cartographer.rest.dto.DownlogRequest;
import org.commonjava.cartographer.rest.dto.DownlogRequestBuilder;
import org.commonjava.cartographer.rest.dto.RepoContentResult;
import org.commonjava.cartographer.rest.dto.UrlMapResult;
import org.commonjava.cartographer.rest.dto.WorkspaceList;
import org.commonjava.cartographer.result.GraphDifference;
import org.commonjava.cartographer.result.GraphExport;
import org.commonjava.cartographer.result.MappedProjectRelationshipsResult;
import org.commonjava.cartographer.result.MappedProjectResult;
import org.commonjava.cartographer.result.MappedProjectsResult;
import org.commonjava.cartographer.result.MetadataCollationResult;
import org.commonjava.cartographer.result.MetadataResult;
import org.commonjava.cartographer.result.ProjectErrors;
import org.commonjava.cartographer.result.ProjectListResult;
import org.commonjava.cartographer.result.ProjectPathsResult;
import org.commonjava.cartographer.result.SourceAliasMapResult;
import org.commonjava.maven.atlas.graph.rel.ProjectRelationship;
import org.commonjava.cartographer.graph.traverse.model.BuildOrder;
import org.commonjava.maven.atlas.ident.ref.ProjectVersionRef;
import org.commonjava.propulsor.client.http.ClientHttpException;
import org.commonjava.propulsor.client.http.ClientHttpResponseErrorDetails;
import org.commonjava.propulsor.client.http.ClientHttpSupport;
import org.commonjava.propulsor.client.http.helper.HttpResources;
import org.commonjava.propulsor.client.http.helper.UrlUtils;
import org.commonjava.util.jhttpc.HttpFactory;
import org.commonjava.util.jhttpc.auth.ClientAuthenticator;
import org.commonjava.util.jhttpc.auth.PasswordManager;
import org.commonjava.util.jhttpc.model.SiteConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.ANY;
import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.NONE;
import static com.fasterxml.jackson.annotation.PropertyAccessor.*;
import static com.fasterxml.jackson.databind.DeserializationFeature.*;
/**
* Created by jdcasey on 8/12/15.
*/
public class CartographerRESTClient
implements AutoCloseable
{
private final ClientHttpSupport http;
public CartographerRESTClient( ClientHttpSupport http )
{
this.http = http;
}
public CartographerRESTClient( final String baseUrl, final PasswordManager passwordManager )
throws ClientHttpException
{
this.http = new ClientHttpSupport( baseUrl, passwordManager, newObjectMapper() );
}
public CartographerRESTClient( final String baseUrl, final ClientAuthenticator authenticator )
throws ClientHttpException
{
this.http = new ClientHttpSupport( baseUrl, authenticator, newObjectMapper() );
}
public CartographerRESTClient( final SiteConfig siteConfig, final HttpFactory httpFactory )
throws ClientHttpException
{
this.http = new ClientHttpSupport( siteConfig, newObjectMapper(), httpFactory );
}
public ObjectMapper newObjectMapper()
{
ObjectMapper mapper = new ObjectMapper();
mapper.registerModules( new CartoAPIObjectMapperModules().getSerializerModules() );
mapper.setVisibility(ALL, NONE);
mapper.setVisibility(FIELD, ANY);
mapper.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);
return mapper;
}
public ClientHttpSupport getHttp()
{
return http;
}
public ObjectMapper getObjectMapper()
{
return getHttp().getObjectMapper();
}
public ProjectListResult list( ProjectGraphRequest request )
throws CartoClientException, ClientHttpException
{
return getHttp().postWithResponse( "depgraph/project/list", request, ProjectListResult.class );
}
public MappedProjectResult parents( ProjectGraphRequest request )
throws CartoClientException, ClientHttpException
{
return getHttp().postWithResponse( "depgraph/project/parents", request, MappedProjectResult.class );
}
public ProjectErrors errors( ProjectGraphRequest request )
throws CartoClientException, ClientHttpException
{
return getHttp().postWithResponse( "depgraph/graph/errors", request, ProjectErrors.class );
}
public ProjectListResult reindex( ProjectGraphRequest request )
throws CartoClientException, ClientHttpException
{
return getHttp().postWithResponse( "depgraph/graph/reindex", request, ProjectListResult.class );
}
public ProjectListResult incomplete( ProjectGraphRequest request )
throws CartoClientException, ClientHttpException
{
return getHttp().postWithResponse( "depgraph/graph/incomplete", request, ProjectListResult.class );
}
public ProjectListResult missing( ProjectGraphRequest request )
throws CartoClientException, ClientHttpException
{
return incomplete( request );
}
public ProjectListResult variable( ProjectGraphRequest request )
throws CartoClientException, ClientHttpException
{
return getHttp().postWithResponse( "depgraph/graph/variable", request, ProjectListResult.class );
}
public ProjectPathsResult paths( PathsRequest request )
throws CartoClientException, ClientHttpException
{
return getHttp().postWithResponse( "depgraph/graph/paths", request, ProjectPathsResult.class );
}
public MappedProjectsResult ancestors( ProjectGraphRequest request )
throws CartoClientException, ClientHttpException
{
return getHttp().postWithResponse( "depgraph/graph/ancestry", request, MappedProjectsResult.class );
}
public BuildOrder buildOrder( ProjectGraphRequest request )
throws CartoClientException, ClientHttpException
{
return getHttp().postWithResponse( "depgraph/graph/build-order", request, BuildOrder.class );
}
public GraphExport graph( SingleGraphRequest request )
throws CartoClientException, ClientHttpException
{
return getHttp().postWithResponse( "depgraph/graph/export", request, GraphExport.class );
}
public GraphExport export( SingleGraphRequest request )
throws CartoClientException, ClientHttpException
{
return graph( request );
}
public MappedProjectRelationshipsResult relationshipsDeclaredBy( ProjectGraphRelationshipsRequest request )
throws CartoClientException, ClientHttpException
{
return getHttp().postWithResponse( "depgraph/project/relationships", request,
MappedProjectRelationshipsResult.class );
}
public MappedProjectRelationshipsResult relationshipsTargeting( ProjectGraphRelationshipsRequest request )
throws CartoClientException, ClientHttpException
{
return getHttp().postWithResponse( "depgraph/project/targeting", request,
MappedProjectRelationshipsResult.class );
}
public MetadataResult getMetadata( MetadataExtractionRequest request )
throws CartoClientException, ClientHttpException
{
return getHttp().postWithResponse( "depgraph/meta", request, MetadataResult.class );
}
public ProjectListResult updateMetadata( MetadataUpdateRequest request )
throws CartoClientException, ClientHttpException
{
return getHttp().postWithResponse( "depgraph/meta/updates", request, ProjectListResult.class );
}
public ProjectListResult rescanMetadata( ProjectGraphRequest request )
throws CartoClientException, ClientHttpException
{
return getHttp().postWithResponse( "depgraph/meta/rescan", request, ProjectListResult.class );
}
public MetadataCollationResult collateMetadata( MetadataCollationRequest request )
throws CartoClientException, ClientHttpException
{
return getHttp().postWithResponse( "depgraph/meta/collation", request, MetadataCollationResult.class );
}
public void deleteWorkspace( String workspaceId )
throws CartoClientException, ClientHttpException
{
getHttp().delete( UrlUtils.buildUrl( "depgraph/ws", workspaceId ) );
}
public WorkspaceList listWorkspaces()
throws CartoClientException, ClientHttpException
{
return getHttp().get( "depgraph/ws", WorkspaceList.class );
}
public GraphDifference<ProjectVersionRef> calculateGraphDrift( GraphAnalysisRequest request )
throws CartoClientException, ClientHttpException
{
return getHttp().postWithResponse( "depgraph/calc/drift", request,
new TypeReference<GraphDifference<ProjectVersionRef>>()
{
} );
}
public GraphDifference<ProjectRelationship<?, ?>> graphDiff( GraphAnalysisRequest request )
throws CartoClientException, ClientHttpException
{
return getHttp().postWithResponse( "depgraph/calc/drift", request,
new TypeReference<GraphDifference<ProjectRelationship<?, ?>>>()
{
} );
}
public GraphCalculation calculate( MultiGraphRequest request )
throws CartoClientException, ClientHttpException
{
return getHttp().postWithResponse( "depgraph/calc/drift", request, GraphCalculation.class );
}
public UrlMapResult repositoryUrlMap( RepositoryContentRequest request )
throws CartoClientException, ClientHttpException
{
return getHttp().postWithResponse( "depgraph/repo/urlmap", request, UrlMapResult.class );
}
public RepoContentResult repositoryContent( RepositoryContentRequest request )
throws CartoClientException, ClientHttpException
{
return getHttp().postWithResponse( "depgraph/repo/content", request, RepoContentResult.class );
}
public String repositoryDownloadLog( DownlogRequest request )
throws CartoClientException, ClientHttpException
{
return postWithStringOutput( "depgraph/repo/downlog", request );
}
public InputStream repositoryZip( RepositoryContentRequest request )
throws CartoClientException, IOException, ClientHttpException
{
HttpResources resources = getHttp().postRaw( "depgraph/repo/zip", request );
if ( resources.getStatusCode() != HttpStatus.SC_OK )
{
throw new ClientHttpException( resources.getStatusCode(), "Error retrieving repo zip.\n%s",
new ClientHttpResponseErrorDetails( resources.getResponse() ) );
}
return resources.getResponseEntityContent();
}
public String pom( PomRequest request )
throws CartoClientException, ClientHttpException
{
return postWithStringOutput( "depgraph/render/pom", request );
}
public String dotfile( MultiRenderRequest request )
throws CartoClientException, ClientHttpException
{
Map<String, String> params = request.getRenderParams();
if ( params != null && !params.containsKey( "name" ) && !params.containsKey( "coord" ) )
{
Logger logger = LoggerFactory.getLogger( getClass() );
logger.warn(
"You have not specified a 'name' or 'coord' parameter to be used in naming your Graphviz dotfile!" );
}
return postWithStringOutput( "depgraph/render/dotfile", request );
}
public String depTree( RepositoryContentRequest request )
throws CartoClientException, ClientHttpException
{
return postWithStringOutput( "depgraph/render/depTree", request );
}
public String depList( RepositoryContentRequest request )
throws CartoClientException, ClientHttpException
{
return postWithStringOutput( "depgraph/render/depList", request );
}
public ProjectGraphRequestBuilder newProjectGraphRequest()
{
return ProjectGraphRequestBuilder.newProjectGraphRequestBuilder();
}
public ProjectGraphRelationshipsRequestBuilder newProjectGraphRelationshipsRequest()
{
return ProjectGraphRelationshipsRequestBuilder.newProjectGraphRelationshipsRequestBuilder();
}
public MultiGraphRequestBuilder newMultiGraphRequest()
{
return MultiGraphRequestBuilder.newMultiGraphResolverRequestBuilder();
}
public PomRequestBuilder newPomRequest()
{
return PomRequestBuilder.newPomRequestBuilder();
}
public DownlogRequestBuilder newDownlogRequest()
{
return DownlogRequestBuilder.newDownlogRequestBuilder();
}
public GraphAnalysisRequestBuilder newGraphAnalysisRequest()
{
return GraphAnalysisRequestBuilder.newAnalysisRequestBuilder();
}
public MetadataExtractionRequestBuilder newMetadataExtractionRequest()
{
return MetadataExtractionRequestBuilder.newMetadataRecipeBuilder();
}
public MetadataUpdateRequestBuilder newMetadataUpdateRequest()
{
return MetadataUpdateRequestBuilder.newMetadataRecipeBuilder();
}
public MetadataCollationRequestBuilder newMetadataCollationRequest()
{
return MetadataCollationRequestBuilder.newMetadataRecipeBuilder();
}
public MultiRenderRequestBuilder newMultiRenderRequest()
{
return MultiRenderRequestBuilder.newMultiRenderRecipeBuilder();
}
public PathsRequestBuilder newPathsRequest()
{
return PathsRequestBuilder.newPathsRecipeBuilder();
}
public RepositoryContentRequestBuilder newRepositoryContentRequest()
{
return RepositoryContentRequestBuilder.newRepositoryContentRecipeBuilder();
}
public GraphDescriptionBuilder newGraphDescription()
{
return GraphDescriptionBuilder.newGraphDescriptionBuilder();
}
public GraphCompositionBuilder newGraphComposition()
{
return GraphCompositionBuilder.newGraphCompositionBuilder();
}
public Iterable<Module> getSerializerModules()
{
return new CartoAPIObjectMapperModules().getSerializerModules();
}
private String postWithStringOutput( String path, Object request )
throws CartoClientException, ClientHttpException
{
String result = null;
try (HttpResources resources = getHttp().postRaw( path, request ))
{
if ( resources.getStatusCode() != HttpStatus.SC_OK )
{
throw new ClientHttpException( resources.getStatusCode(), "Error retrieving response string.\n%s",
new ClientHttpResponseErrorDetails( resources.getResponse() ) );
}
result = IOUtils.toString( resources.getResponseEntityContent() );
}
catch ( IOException e )
{
Logger logger = LoggerFactory.getLogger( getClass() );
logger.warn( "Error closing response to path: " + path + ". Error: " + e.getMessage(), e );
}
return result;
}
@Override
public void close()
throws Exception
{
if ( http != null )
{
http.close();
}
}
public boolean addSourceAlias( String alias, String url )
throws ClientHttpException, CartoClientException
{
SourceAliasRequest req = new SourceAliasRequest( alias, url );
Map<String, String> headers = new HashMap<>();
headers.put( "Accept", "application/json" );
headers.put( "Content-Type", "application/json" );
try(HttpResources resources = http.postRaw( "admin/sources/aliases", req, headers ))
{
return resources.getResponse().getStatusLine().getStatusCode() == 200;
}
catch ( IOException e )
{
throw new CartoClientException( "Failed to add source alias. Reason: %s", e, e.getMessage() );
}
}
public SourceAliasMapResult getSourceAliasMap()
throws ClientHttpException
{
return http.get( "admin/sources/aliases", SourceAliasMapResult.class );
}
}