Artifactory-Client

 view release on metacpan or  search on metacpan

lib/Artifactory/Client.pm  view on Meta::CPAN

use JSON::MaybeXS;
use LWP::UserAgent;
use Path::Tiny qw();
use MooseX::StrictConstructor;
use URI::Escape qw(uri_escape);
use File::Basename qw(basename);
use HTTP::Request::StreamingUpload;

use namespace::autoclean;

=head1 NAME

Artifactory::Client - Perl client for Artifactory REST API

=head1 VERSION

Version 1.8.0

=cut

our $VERSION = 'v1.8.0';

=head1 SYNOPSIS

This is a Perl client for Artifactory REST API:
https://www.jfrog.com/confluence/display/RTF/Artifactory+REST+API Every public method provided in this module returns a
HTTP::Response object.

    use Artifactory::Client;

    my $h = HTTP::Headers->new();
    $h->authorization_basic( 'admin', 'password' );
    my $ua = LWP::UserAgent->new( default_headers => $h );

    my $args = {
        artifactory  => 'http://artifactory.server.com',
        port         => 8080,
        repository   => 'myrepository',
        context_root => '/', # Context root for artifactory. Defaults to 'artifactory'.
        ua           => $ua  # Dropping in custom UA with default_headers set.  Default is a plain LWP::UserAgent.
    };

    my $client = Artifactory::Client->new( $args );
    my $path = '/foo'; # path on artifactory

    # Properties are a hashref of key-arrayref pairs.  Note that value must be an arrayref even for a single element.
    # This is to conform with Artifactory which treats property values as a list.
    my $properties = {
        one => ['two'],
        baz => ['three'],
    };
    my $file = '/local/file.xml';

    # Name of methods are taken straight from Artifactory REST API documentation.  'Deploy Artifact' would map to
    # deploy_artifact method, like below.  The caller gets HTTP::Response object back.
    my $resp = $client->deploy_artifact( path => $path, properties => $properties, file => $file );

    # Custom requests can also be made via usual get / post / put / delete requests.
    my $resp = $client->get( 'http://artifactory.server.com/path/to/resource' );

    # Repository override for calls that have a repository in the endpoint.  The passed-in repository will not persist.
    my $resp = $client->calculate_yum_repository_metadata( repository => 'different_repo', async => 1 );

=cut

=head1 Dev Env Setup / Running Tests

    carton install

    # to run unit tests
    prove -r t

=cut

has 'artifactory' => (
    is       => 'ro',
    isa      => 'Str',
    required => 1,
    writer   => '_set_artifactory',
);

has 'port' => (
    is      => 'ro',
    isa     => 'Int',
    default => 80,
);

has 'context_root' => (
    is      => 'ro',
    isa     => 'Str',
    default => 'artifactory',
);

has 'ua' => (
    is      => 'rw',
    isa     => 'LWP::UserAgent',
    builder => '_build_ua',
    lazy    => 1,
);

has 'repository' => (
    is      => 'ro',
    isa     => 'Str',
    default => '',
    writer  => '_set_repository',
);

has '_json' => (
    is      => 'ro',
    builder => '_build_json',
    lazy    => 1,
);

has '_api_url' => (
    is       => 'ro',
    isa      => 'Str',
    init_arg => undef,
    writer   => '_set_api_url',
);

has '_art_url' => (

lib/Artifactory/Client.pm  view on Meta::CPAN


=cut

sub calculate_bower_index {
    my ( $self, %args ) = @_;
    my $repository = $args{repository} || $self->repository();
    return $self->_handle_repository_reindex("/bower/$repository/reindex");
}

=head2 calculate_helm_chart_index

Calculates Helm chart index on the specified path (local repositories only).

=cut

sub calculate_helm_chart_index {
    my ( $self, %args ) = @_;
    my $repository = $args{repository} || $self->repository();
    return $self->_handle_repository_reindex("/helm/$repository/reindex");
}

=head2 calculate_cran_repository_metadata

Calculates/recalculates the Packages and Release metadata for this repository, based on the CRAN packages in it.

=cut

sub calculate_cran_repository_metadata {
    my ( $self, %args ) = @_;
    my $repository = $args{repository} || $self->repository();
    return $self->_handle_repository_reindex("/cran/reindex/$repository", %args);
}

=head2 calculate_conda_repository_metadata

Calculates/recalculates the Conda packages and release metadata for this repository.

=cut

sub calculate_conda_repository_metadata {
    my ( $self, %args ) = @_;
    my $repository = $args{repository} || $self->repository();
    return $self->_handle_repository_reindex("/conda/reindex/$repository", %args);
}

=head1 SYSTEM & CONFIGURATION

=cut

=head2 system_info

Get general system information

=cut

sub system_info {
    my $self = shift;
    return $self->_handle_system();
}

=head2 verify_connection( endpoint => 'http://server/foobar', username => 'admin', password => 'password' )

Verifies a two-way connection between Artifactory and another product

=cut

sub verify_connection {
    my ( $self, %args ) = @_;
    my $url = $self->_api_url() . "/system/verifyconnection";

    return $self->post(
        $url,
        'Content-Type' => 'application/json',
        content        => $self->_json->encode( \%args )
    );
}

=head2 system_health_ping

Get a simple status response about the state of Artifactory

=cut

sub system_health_ping {
    my $self = shift;
    return $self->_handle_system('ping');
}

=head2 general_configuration

Get the general configuration (artifactory.config.xml)

=cut

sub general_configuration {
    my $self = shift;
    return $self->_handle_system('configuration');
}

=head2 save_general_configuration( $file )

Save the general configuration (artifactory.config.xml)

=cut

sub save_general_configuration {
    my ( $self, $xml ) = @_;

    my $file = Path::Tiny::path($xml)->slurp( { binmode => ":raw" } );
    my $url = $self->_api_url() . "/system/configuration";
    return $self->post(
        $url,
        'Content-Type' => 'application/xml',
        content        => $file
    );
}

=head2 update_custom_url_base( $url )

Changes the Custom URL base

lib/Artifactory/Client.pm  view on Meta::CPAN

=head2 import_repository_content( path => 'foobar', repo => 'repo', metadata => 1, verbose => 0 )

Import one or more repositories

=cut

sub import_repository_content {
    my ( $self, %args ) = @_;

    my $url = $self->_api_url() . "/import/repositories?";
    $url .= $self->_stringify_hash( '&', %args );
    return $self->post($url);
}

=head2 import_system_settings_example

Returned default Import Settings JSON

=cut

sub import_system_settings_example {
    my $self = shift;
    return $self->_handle_system_settings('import');
}

=head2 full_system_import( importPath => '/import/path', includeMetadata => 'false' etc )

Import full system from a server local Artifactory export directory

=cut

sub full_system_import {
    my ( $self, %args ) = @_;
    return $self->_handle_system_settings( 'import', %args );
}

=head2 export_system_settings_example

Returned default Export Settings JSON

=cut

sub export_system_settings_example {
    my $self = shift;
    return $self->_handle_system_settings('export');
}

=head2 export_system( exportPath => '/export/path', includeMetadata => 'true' etc )

Export full system to a server local directory

=cut

sub export_system {
    my ( $self, %args ) = @_;
    return $self->_handle_system_settings( 'export', %args );
}

=head2 ignore_xray_alert( $path )

Sets an alert to be ignored until next time the repository hosting the artifact about which the alert was issued, is scanned. Note that this endpoint does not
affect artifacts that are blocked because they have not been scanned at all.

=cut

sub ignore_xray_alert {
    my ( $self, $path ) = @_;
    my $url = $self->_api_url() . "/xray/setAlertIgnored?path=$path";
    return $self->post($url);
}

=head2 allow_download_of_blocked_artifacts( 'true'|'false' )

When a repository is configured to block downloads of artifacts, you may override that configuration (and allow download of blocked artifacts). Note that this
setting cannot override the blocking of unscanned artifacts.

=cut

sub allow_download_of_blocked_artifacts {
    my ( $self, $bool ) = @_;
    my $url = $self->_api_url() . "/xray/allowBlockedArtifactsDownload?allow=$bool";
    return $self->post($url);
}

=head2 allow_download_when_xray_is_unavailable( 'true'|'false' )

You may configure Artifactory to block downloads of artifacts when the connected Xray instance is unavailable. This endpoint lets you override that
configuration (and allow download of artifacts).

=cut

sub allow_download_when_xray_is_unavailable {
    my ( $self, $bool ) = @_;
    my $url = $self->_api_url() . "/xray/allowDownloadWhenUnavailable?allow=$bool";
    return $self->post($url);
}

=head2 create_bundle( %hash of data structure )

Create a new support bundle

=cut

sub create_bundle {
    my ( $self, %args ) = @_;
    my $url = $self->_api_url() . '/support/bundles';
    %args = () unless %args;

    return $self->post(
        $url,
        "Content-Type" => 'application/json',
        Content        => $self->_json->encode( \%args )
    );
}

=head2 list_bundles

Lists previously created bundle currently stored in the system

=cut

sub list_bundles {
    my $self = shift;
    my $url  = $self->_api_url() . '/support/bundles';
    return $self->get( $url, "Content-Type" => 'application/json', );
}

=head2 get_bundle_metadata( $name )

Downloads a previously created bundle currently stored in the system

=cut

sub get_bundle_metadata {
    my ( $self, $bundle ) = @_;
    my $url = $self->_api_url() . '/support/bundles/' . $bundle;
    return $self->get( $url, "Content-Type" => 'application/json', );
}

=head2 get_bundle( $name )

Downloads a previously created bundle currently stored in the system

=cut

sub get_bundle {
    my ( $self, $bundle ) = @_;

lib/Artifactory/Client.pm  view on Meta::CPAN

    $repo =~ s{\/$}{}xi;

    my $url =
      (%args)
      ? $self->_api_url() . "/repositories/$repo?"
      : $self->_api_url() . "/repositories/$repo";
    $url .= $self->_stringify_hash( '&', %args ) if (%args);

    if ($payload) {
        return $self->$method(
            $url,
            'Content-Type' => 'application/json',
            content        => $self->_json->encode($payload)
        );
    }
    return $self->$method($url);
}

sub _handle_system {
    my ( $self, $arg ) = @_;

    my $url =
      ($arg)
      ? $self->_api_url() . "/system/$arg"
      : $self->_api_url() . "/system";
    return $self->get($url);
}

sub _handle_plugins {
    my ( $self, $type ) = @_;

    my $url =
      ($type)
      ? $self->_api_url() . "/plugins/$type"
      : $self->_api_url() . "/plugins";
    return $self->get($url);
}

sub _handle_system_settings {
    my ( $self, $action, %args ) = @_;

    my $url = $self->_api_url() . "/$action/system";

    if (%args) {
        return $self->post(
            $url,
            'Content-Type' => 'application/json',
            content        => $self->_json->encode( \%args )
        );
    }
    return $self->get($url);
}

sub _handle_gpg_key {
    my ( $self, $type, $method, %args ) = @_;
    my $url = $self->_api_url() . "/gpg/key/$type";
    return $self->$method( $url, %args );
}

sub _handle_repository_reindex {
    my ( $self, $endpoint, %args ) = @_;
    my $url =
      (%args)
      ? $self->_api_url() . $endpoint . "?"
      : $self->_api_url() . $endpoint;
    $url .= $self->_stringify_hash( '&', %args ) if (%args);
    return $self->post($url);
}

sub _handle_multi_push_replication {
    my ( $self, $payload, $method ) = @_;

    my $url = $self->_api_url() . '/replications/multiple';
    return $self->$method(
        $url,
        "Content-Type" => 'application/json',
        Content        => $self->_json->encode($payload)
    );
}

sub _merge_repo_and_path {
    my ( $self, $_path ) = @_;

    $_path = '' if not defined $_path;
    $_path =~ s{^\/}{}xi;

    return join( '/', grep { $_ } $self->repository(), $_path );
}

sub _gather_delete_builds_params {
    my ( $self, $buildnumbers, $artifacts, $deleteall ) = @_;

    my @params;
    if ( ref($buildnumbers) eq 'ARRAY' ) {
        my $str = "buildNumbers=";
        $str .= join( ",", @{$buildnumbers} );
        push @params, $str;
    }
    push @params, "artifacts=$artifacts" if ( defined $artifacts );
    push @params, "deleteAll=$deleteall" if ( defined $deleteall );
    return @params;
}

sub _handle_api_key {
    my ( $self, $method, %args ) = @_;

    my $url = $self->_api_url() . "/apiKey/auth";
    return $self->$method(
        $url,
        'Content-Type' => 'application/json',
        content        => $self->_json->encode( \%args )
    );
}

sub _handle_revoke_api_key {
    my ( $self, $endpoint ) = @_;

    my $resp    = $self->get_api_key();
    my $content = $self->_json->decode( $resp->content );
    my %header;
    $header{'X-Api-Key'} = $content->{apiKey};
    my $url = $self->_api_url() . $endpoint;
    return $self->delete( $url, %header );
}

sub _handle_block_system_replication {
    my ( $self, $ep, %args ) = @_;
    my %merged = (
        push => 'true',
        pull => 'true',
        %args    # overriding defaults
    );
    my $repo = $self->repository();
    my $url = $self->_api_url() . "/system/replications/$ep?" . $self->_stringify_hash( '&', %merged );
    return $self->post($url);
}

__PACKAGE__->meta->make_immutable;

=head1 AUTHOR

Satoshi Yagi, C<< <satoshi.yagi at yahoo.com> >>

=head1 BUGS

Please report any bugs or feature requests to C<bug-artifactory-client at
rt.cpan.org>, or through the web interface at
L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Artifactory-Client>.  I will
be notified, and then you'll automatically be notified of progress on your bug
as I make changes.

=head1 SUPPORT

You can find documentation for this module with the perldoc command.

    perldoc Artifactory::Client

You can also look for information at:

=over 4

=item * RT: CPAN's request tracker (report bugs here)

L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Artifactory-Client>

=item * AnnoCPAN: Annotated CPAN documentation

L<http://annocpan.org/dist/Artifactory-Client>

=item * CPAN Ratings

L<http://cpanratings.perl.org/d/Artifactory-Client>

=item * Search CPAN

L<http://search.cpan.org/dist/Artifactory-Client/>

=back

=head1 ACKNOWLEDGEMENTS

=head1 LICENSE AND COPYRIGHT



( run in 1.560 second using v1.01-cache-2.11-cpan-63c85eba8c4 )