File Coverage

lib/API/Medium.pm
Criterion Covered Total %
statement 15 38 39.4
branch 0 4 0.0
condition 0 4 0.0
subroutine 5 10 50.0
pod 3 3 100.0
total 23 59 38.9


line stmt bran cond sub pod time code
1             package API::Medium;
2 1     1   913 use Moose;
  1         374528  
  1         6  
3 1     1   5378 use HTTP::Tiny;
  1         35719  
  1         42  
4 1     1   547 use Log::Any qw($log);
  1         8700  
  1         4  
5 1     1   3682 use JSON::MaybeXS;
  1         4244  
  1         53  
6 1     1   5 use Module::Runtime 'use_module';
  1         2  
  1         8  
7              
8             # ABSTRACT: Talk with medium.com using their REST API
9              
10             our $VERSION = '0.900';
11              
12             has 'server' => (
13             isa => 'Str',
14             is => 'ro',
15             default => 'https://api.medium.com/v1',
16             );
17              
18             has 'access_token' => (
19             isa => 'Str',
20             is => 'rw',
21             required => 1,
22             );
23              
24             has 'refresh_token' => (
25             isa => 'Str',
26             is => 'ro',
27             );
28              
29             has '_client' => (
30             isa => 'HTTP::Tiny',
31             is => 'ro',
32             lazy_build => 1,
33             );
34              
35             sub _build__client {
36 0     0     my $self = shift;
37              
38 0           return HTTP::Tiny->new(
39             agent => join( '/', __PACKAGE__, $VERSION ),
40             default_headers => {
41             'Authorization' => 'Bearer ' . $self->access_token,
42             'Accept' => 'application/json',
43             'Content-Type' => 'application/json',
44             }
45             );
46             }
47              
48             sub get_current_user {
49 0     0 1   my $self = shift;
50              
51 0           my $res = $self->_request( 'GET', 'me' );
52              
53 0           return $res->{data};
54             }
55              
56             sub create_post {
57 0     0 1   my ( $self, $user_id, $post ) = @_;
58              
59 0   0       $post->{publishStatus} ||= 'draft';
60              
61 0           my $res = $self->_request( 'POST', 'users/' . $user_id . '/posts', $post );
62 0           return $res->{data}{url};
63             }
64              
65             sub create_publication_post {
66 0     0 1   my ( $self, $publication_id, $post ) = @_;
67              
68 0   0       $post->{publishStatus} ||= 'draft';
69              
70 0           my $res =
71             $self->_request( 'POST', 'publications/' . $publication_id . '/posts',
72             $post );
73 0           return $res->{data}{url};
74             }
75              
76             sub _request {
77 0     0     my ( $self, $method, $endpoint, $data ) = @_;
78              
79 0           my $url = join( '/', $self->server, $endpoint );
80              
81 0           my $res;
82 0 0         if ($data) {
83 0           $res = $self->_client->request( $method, $url,
84             { content => encode_json($data) } );
85             }
86             else {
87 0           $res = $self->_client->request( $method, $url );
88             }
89 0 0         if ( $res->{success} ) {
90 0           return decode_json( $res->{content} );
91             }
92             else {
93             $log->errorf( "Could not talk to medium: %i %s",
94 0           $res->{status}, $res->{reason} );
95 0           die join( ' ', $res->{status}, $res->{reason} );
96             }
97             }
98              
99             __PACKAGE__->meta->make_immutable;
100             1;
101              
102             __END__
103              
104             =pod
105              
106             =encoding UTF-8
107              
108             =head1 NAME
109              
110             API::Medium - Talk with medium.com using their REST API
111              
112             =head1 VERSION
113              
114             version 0.900
115              
116             =head1 SYNOPSIS
117              
118             use API::Medium;
119             my $m = new({
120             access_token=>'your_token',
121             });
122             my $hash = $m->get_current_user;
123             say $hash->{id};
124              
125             my $url = $m->create_post( $user_id, $post );
126              
127             my $other_url = $m->create_publication_post( $publication_id, $post );
128              
129             =head1 DESCRIPTION
130              
131             It's probably a good idea to read L<the Medium API
132             docs|https://github.com/Medium/medium-api-docs> first, especially as
133             the various data structures you have to send (or might get back) are
134             B<not> documented here.
135              
136             See F<example/hello_medium.pl> for a complete script.
137              
138             =head2 Authentication
139              
140             =head3 OAuth2 Login
141              
142             Not implemented yet, mostly because medium only support the "web
143             server" flow and I'm using C<API::Medium> for an installed
144             application.
145              
146             =head3 Self-issued access token / Integration token
147              
148             Go to your L<settings|https://medium.com/me/settings>, scroll down to
149             "Integration tokens", and either create a new one, or pick the one you
150             want to use.
151              
152             =head1 Methods
153              
154             =head2 new
155              
156             my $m = API::Medium->new({
157             access_token => $token,
158             });
159              
160             Create a new API client. You will need to pass in your C<$token>, see
161             above on how to get it. Please make sure no not leak your Integration
162             Token. If you do, anybody who has it can take over your Medium page!
163              
164             =head2 get_current_user
165              
166             my $data = $m->get_current_user;
167              
168             Fetch the User "object".
169              
170             You will need this to get the user C<id> for posting. Depending on
171             your app you might want to store your C<id> in some config file to
172             save one API call.
173              
174             =head2 publications
175              
176             Not implemented yet. Listing the user's publications
177              
178             /users/{{userId}}/publications
179              
180             =head2 contributors
181              
182             Not implemented yet. Fetching contributors for a publication.
183              
184             /publications/{{publicationId}}/contributors
185              
186             =head2 create_post
187              
188             my $url = $m->create_post( $user_id, $post_data );
189              
190             Create a new post. If you pass in bad data, Medium will probably
191             report an error.
192              
193             C<publishStatus> is set to 'draft' unless you pass in another value.
194              
195             =head2 create_publication_post
196              
197             my $url = $m->create_publication_post( $publication_id, $post_data );
198              
199             Create a new post under a publication. You will need to figure out the
200             publication_id by calling the API from the commandline (until
201             C<publications> is implemented.)
202              
203             If you pass in bad data, Medium will probably report an error.
204              
205             C<publishStatus> is set to 'draft' unless you pass in another value.
206              
207             =head2 TODO
208              
209             =over
210              
211             =item * OAuth2 Login
212              
213             =item * Get a new access_token from refresh_token
214              
215             =item * C<publications>
216              
217             =item * C<contributors>
218              
219             =back
220              
221             =head2 See Also
222              
223             Jonathan Stowe is working on a L<Perl 6 version|https://github.com/jonathanstowe/Medium-API>
224              
225             =head2 Thanks
226              
227             Thanks to Dave Cross for starting L<Cultured
228             Perl|https://medium.com/cultured-perl>, which prompted me to write
229             this module so I can auto-post blogposts from L<my private
230             blog|http://domm.plix.at> to medium.
231              
232             =head1 AUTHOR
233              
234             Thomas Klausner <domm@cpan.org>
235              
236             =head1 COPYRIGHT AND LICENSE
237              
238             This software is copyright (c) 2016 by Thomas Klausner.
239              
240             This is free software; you can redistribute it and/or modify it under
241             the same terms as the Perl 5 programming language system itself.
242              
243             =cut