package Gogs; # ABSTRACT: Subclass of Pithub use strict; use warnings; use Moo; use MIME::Base64; #use Gogs::Repos; extends 'Pithub'; =head1 DESCRIPTION L is an acceptable API client for Gogs, since their APIs are compatible. However the two have a number of differences, so they must be accounted for. The most important of which being that all requests require an API token. =head1 METHODS =head2 get_token(%options) We can simplify tooling around this to not require you enter the "Applications" section of the gogs interface. Requires that the object is instantiated with a username, that you pass a password, and that the api_uri is secure unless you pass the 'insecure' option. If the named token exists already it will delete and re-create the named token. It is HIGHLY RECOMMENDED you delete the token at the end of your session with the delete_token() method. =cut # All API access requires a token on Gogs EXCEPT users/$user/tokens. # As such, I'm restricting every call not ending in /tokens my @TOKEN_REQUIRED_REGEXP = (qr{(?!/tokens$)}); sub _validate_tok_args { my ($self,%options) = @_; die "Must instantiate with username" unless $self->user; die "Must pass token name or sha1" unless $options{name} || $options{sha1}; die "Refuse to operate over insecure connections" if !$options{insecure} && _insecure($self->api_uri); die "Must provide password to fetch tokens" unless $options{password}; } sub _build_tok_headers { my ($self, %options) = @_; return ( 'Authorization' => "Basic ".encode_base64($self->user.":$options{password}"), 'Content-type' => "application/json", ); } sub get_token { my ($self, %options) = @_; # unset the token if passed, so that we use simple auth my $tok = $self->token(); $self->token("") if $tok; $self->_validate_tok_args(%options); my %auth_headers = $self->_build_tok_headers(%options); my $token_endpoint = "/users/".$self->user."/tokens"; my %req = ( method => 'GET', path => $token_endpoint, headers => \%auth_headers, ); my $result = $self->request(%req); my $content = $result->content(); die "Got bad response from server" unless ref $content eq "ARRAY"; my $token_actual; foreach my $token (@$content) { # If it exists, we have no means of deleteing it unless we actually have the real SHA1 (the one in list is not real). # As such just die. die "Token with name $options{name} already exists, cannot continue. Please delete it manually." if $token->{name} eq $options{name}; } # No such token named, so let's just make one. $req{method} = 'POST'; $req{data} = qq|{ "name":"$options{name}" }|; $result = $self->request(%req); $content = $result->content(); # Set the token again, since we are done with the requests $self->token($tok) if $tok; die "Got bad response from server" unless ref $content eq "HASH"; return $content->{sha1}; } =head2 delete_token(%options) Delete the token identified by the provided sha1 in %options. Should return a token name. Dies when unsuccessful. =cut sub delete_token { my ($self, %options) = @_; # unset the token if passed, so that we use simple auth my $tok = $self->token(); $self->token("") if $tok; $self->_validate_tok_args(%options); my %auth_headers = $self->_build_tok_headers(%options); my %req = ( method => 'DELETE', path => "/users/".$self->user."/tokens", headers => \%auth_headers, ); $req{data} = qq|{ "sha1":"$options{sha1}" }|; my $result = $self->request(%req); # Set the token again, since we are done with the requests $self->token($tok) if $tok; die "Got bad response from server on DELETE of token" unless $result && $result->response->is_success; return $result; } sub _insecure { my $uri = shift; return $uri =~ m/^http:\/\//; } 1;