File Coverage

blib/lib/WWW/LogicBoxes/Role/Command/Raw.pm
Criterion Covered Total %
statement 30 84 35.7
branch 0 16 0.0
condition n/a
subroutine 10 15 66.6
pod 0 2 0.0
total 40 117 34.1


line stmt bran cond sub pod time code
1             package WWW::LogicBoxes::Role::Command::Raw;
2              
3 39     39   487087 use strict;
  39         136  
  39         3612  
4 39     39   292 use warnings;
  39         147  
  39         1240  
5              
6             #use Smart::Comments;
7             #use Data::Dumper;
8              
9 39     39   743 use Moose::Role;
  39         205814  
  39         334  
10 39     39   222491 use MooseX::Params::Validate;
  39         104599  
  39         303  
11              
12 39     39   21298 use WWW::LogicBoxes::Types qw( HashRef Str );
  39         108  
  39         356  
13              
14 39     39   437845 use HTTP::Tiny;
  39         1649319  
  39         1903  
15 39     39   22136 use URI::Escape qw( uri_escape );
  39         55583  
  39         3424  
16 39     39   23051 use XML::LibXML::Simple qw(XMLin);
  39         2193062  
  39         3358  
17 39     39   450 use Carp;
  39         110  
  39         3406  
18              
19             requires 'username', 'password', 'api_key', '_base_uri', 'response_type';
20              
21             our $VERSION = '1.11.0'; # VERSION
22             # ABSTRACT: Construct Methods For Making Raw LogicBoxes Requests
23              
24 39     39   3131 use Readonly;
  39         8833  
  39         49914  
25             Readonly our $API_METHODS => {
26             domains => {
27             GET => [
28             qw(available suggest-names v5/suggest-names validate-transfer search customer-default-ns orderid details details-by-name locks tel/cth-details)
29             ],
30             POST => [
31             qw(register transfer eu/transfer eu/trade uk/transfer renew modify-ns add-cns modify-cns-name modify-cns-ip delete-cns-ip modify-contact modify-privacy-protection modify-auth-code enable-theft-protection disable-theft-protection tel/modify-whois-pref resend-rfa uk/release cancel-transfer delete restore de/recheck-ns dotxxx/assoication-details raa/resend-verification transfer/submit-auth-code)
32             ],
33             },
34             contacts => {
35             GET => [qw(details search sponsors dotca/registrantagreement)],
36             POST => [qw(add modify default set-details delete coop/add-sponsor)],
37             },
38             customers => {
39             GET =>
40             [qw(details details-by-id generate-token authenticate-token search)],
41             POST => [qw(signup modify change-password delete)],
42             },
43             resellers => {
44             GET => [
45             qw(details generate-token authenticate-token promo-details temp-password search)
46             ],
47             POST => [qw(signup modify-details)],
48             },
49             products => {
50             GET => [
51             qw(availability details plan-details customer-price reseller-price reseller-cost-price)
52             ],
53             POST => [qw(category-keys-mapping move)],
54             },
55             webservices => {
56             GET => [
57             qw(details active-plan-categories mock/products/reseller-price orderid search modify-pricing)
58             ],
59             POST => [qw(add renew modify enable-ssl enable-maintenance delete)],
60             },
61             multidomainhosting => {
62             GET => [qw(details orderid search modify-pricing)],
63             POST => [qw(add renew modify enable-ssl delete)],
64             },
65             'multidomainhosting/windows' => {
66             GET => [qw(details orderid search modify-pricing)],
67             POST => [qw(add renew modify enable-ssl delete)],
68             },
69             resellerhosting => {
70             GET => [qw(details orderid search modify-pricing)],
71             POST => [
72             qw(add renew modify add-dedicated-ip delete-dedicated-ip delete generate-license-key)
73             ],
74             },
75             mail => {
76             GET => [qw(user mailinglists)],
77             POST => [qw(activate)],
78             },
79             'mail/user' => {
80             GET => [qw(authenticate)],
81             POST => [
82             qw(add add-forward-only-account modify suspend unsuspend change-password reset-password update-autoresponder delete add-admin-forwards delete-admin-forwards add-user-forwards delete-user-forwards)
83             ],
84             },
85             'mail/users' => {
86             GET => [qw(search)],
87             POST => [qw(suspend unsuspend delete)],
88             },
89             'mail/domain' => {
90             GET => [qw(is-owernship-verified catchall dns-records)],
91             POST => [
92             qw(add-alias delete-alias update-notification-email active-catchall deactivate-catchall)
93             ],
94             },
95             'mail/mailinglist' => {
96             GET => [qw(subscribers)],
97             POST => [
98             qw(add update add-subscribers delete-subscribers delete add-moderators delete-moderators)
99             ],
100             },
101             dns => {
102             GET => [],
103             POST => [qw(activate)],
104             },
105             'dns/manage' => {
106             GET => [qw(search-records delete-record)],
107             POST => [
108             qw(add-ipv4-record add-ipv6-record add-cname-record add-mx-record add-ns-record add-txt-record add-srv-record update-ipv4-record update-ipv6-record update-cname-record update-mx-record update-ns-record update-txt-record update-srv-record update-soa-record delete-ipv4-record delete-ipv6-record delete-cname-record delete-mx-record delete-ns-record delete-txt-record delete-srv-record)
109             ],
110             },
111             domainforward => {
112             GET => [qw(details dns-records)],
113             POST => [qw(activate manage)],
114             },
115             digitalcertificate => {
116             GET => [qw(check-status details search orderid)],
117             POST =>
118             [qw(add cancel delete enroll-for-thawtecertificate reissue renew)],
119             },
120             billing => {
121             GET => [
122             qw(customer-transactions reseller-transactions customer-greedy-transactions reseller-greedy-transactions customer-balance customer-transactions/search reseller-transactions/search customer-archived-transactions/search customer-balanced-transactions reseller-balance)
123             ],
124             POST => [
125             qw(customer-pay execute-order-without-payment add-customer-fund add-reseller-fund add-customer-debit-note add-reseller-debit-note add-customer-misc-invoice add-reseller-misc-invoice)
126             ],
127             },
128             orders => {
129             GET => [qw()],
130             POST => [qw(suspend unsuspend)],
131             },
132             actions => {
133             GET => [qw(search-current search-archived)],
134             POST => [qw()],
135             },
136             commons => {
137             GET => [qw(legal-agreements)],
138             POST => [qw()],
139             },
140             pg => {
141             GET => [
142             qw(allowedlist-for-customer list-for-reseller customer-transactions)
143             ],
144             POST => [qw()],
145             },
146             };
147              
148             has api_methods => (
149             is => 'ro',
150             isa => HashRef,
151             default => sub { $API_METHODS },
152             init_arg => undef,
153             );
154              
155             sub BUILD {
156 0     0 0   my $self = shift;
157              
158 0           $self->install_methods();
159              
160 0           return;
161             }
162              
163             sub install_methods {
164 0     0 0   my $self = shift;
165              
166 0           my $ua = HTTP::Tiny->new;
167 0           for my $api_class ( keys %{ $self->api_methods } ) {
  0            
168 0           for my $http_method ( keys %{ $self->api_methods->{ $api_class } } ) {
  0            
169 0           for my $api_method (@{ $self->api_methods->{ $api_class }{ $http_method } }) {
  0            
170 0           my $method_name = $api_class . '__' . $api_method;
171              
172 0           $method_name =~ s|-|_|g;
173 0           $method_name =~ s|/|__|g;
174              
175             $self->meta->add_method(
176             $method_name => sub {
177 0     0     my $self = shift;
178 0           my $args = shift;
179              
180 0 0         if( !grep { $_ eq $http_method } qw( GET POST ) ) {
  0            
181 0           croak 'Unable to determine if this is a GET or POST request';
182             }
183              
184 0 0         my $uri = $self->_make_query_string(
185             api_class => $api_class,
186             api_method => $api_method,
187             $args ? ( params => $args ) : ( ),
188             );
189              
190             ### Method Name: ( $method_name )
191             ### HTTP Method: ( $http_method )
192             ### URI: ( $uri )
193              
194 0           my $response = $ua->request( $http_method, $uri );
195              
196             ### Response: ( Dumper( $response ) )
197              
198 0 0         if ( $self->response_type eq "xml_simple" ) {
199 0           return XMLin( $response->{content} );
200             }
201             else {
202 0           return $response->{content};
203             }
204             }
205 0           );
206             }
207             }
208             }
209              
210 0           return;
211             }
212              
213             sub _make_query_string {
214 0     0     my $self = shift;
215 0           my ( %args ) = validated_hash(
216             \@_,
217             api_class => { isa => Str },
218             api_method => { isa => Str },
219             params => { isa => HashRef, optional => 1 },
220             );
221              
222 0           my $api_class = $args{api_class};
223 0           $api_class =~ s/_/-/g;
224 0           $api_class =~ s/__/\//g;
225              
226 0           my $api_method = $args{api_method};
227 0           $api_method =~ s/_/-/g;
228 0           $api_method =~ s/__/\//g;
229              
230 0 0         my $response_type = ( $self->response_type eq 'xml_simple' ) ? 'xml' : $self->response_type;
231              
232 0           my $query_uri = sprintf('%s/api/%s/%s.%s?auth-userid=%s',
233             $self->_base_uri, $api_class, $api_method, $response_type, uri_escape( $self->username ) );
234              
235 0 0         if( $self->has_password ) {
    0          
236 0           $query_uri .= "&auth-password=" . uri_escape( $self->password )
237             }
238             elsif($self->has_api_key) {
239 0           $query_uri .= "&api-key=" . uri_escape( $self->apikey )
240             }
241             else {
242 0           croak 'Unable to construct query string without a password or api_key';
243             }
244              
245 0 0         if( $args{params} ) {
246 0           $query_uri .= $self->_construct_get_args( $args{params} );
247             }
248              
249 0           return $query_uri;
250             }
251              
252             sub _construct_get_args {
253 0     0     my $self = shift;
254 0           my ( $params ) = pos_validated_list( \@_, { isa => HashRef } );
255              
256 0           my $get_args;
257 0           for my $param_name ( keys %{ $params } ) {
  0            
258 0 0         if( ref $params->{ $param_name } eq 'ARRAY' ) {
259 0           for my $param_value (@{ $params->{ $param_name } }) {
  0            
260 0           $get_args .= sprintf('&%s=%s', uri_escape( $param_name ), uri_escape( $param_value ) );
261             }
262             }
263             else {
264 0           $get_args .= sprintf('&%s=%s', uri_escape( $param_name ), uri_escape( $params->{ $param_name } ) );
265             }
266             }
267              
268 0           return $get_args;
269             }
270              
271             1;
272              
273             __END__
274             =pod
275              
276             =head1 NAME
277              
278             WWW::LogicBoxes::Role::Command::Raw - Low Level Access to LogicBoxes API
279              
280             =head1 SYNOPSIS
281              
282             use WWW::LogicBoxes;
283              
284             my $logic_boxes = WWW::LogicBoxes->new( ... );
285              
286             my $response = $logic_boxes->domains__suggest_names({
287             'keyword' => 'car',
288             'tlds' => ['com', 'net', 'org'],
289             'no-of-results' => 10,
290             'hypehn-allowed'=> 'false',
291             'add-related' => 'true',
292             });
293              
294             =head1 REQUIRES
295              
296             =over 4
297              
298             =item username
299              
300             =item password
301              
302             =item api_key
303              
304             =item _base_uri
305              
306             =item response_Type
307              
308             =back
309              
310             =head1 DESCRIPTION
311              
312             This role composes a series of methods into the consuming class (L<WWW::LogicBoxes>) that directly expose methods of the L<LogicBoxes|http://www.logicboxes.com> API. It is the lowest level of access to the LogicBoxes API and is intended only for the most advanced usages that are not covered by other commands.
313              
314             B<NOTE> You almost never want to make this low level of a call. You really should be looking at the L<commands|WWW::LogicBoxes/COMMANDS> to find the specific method to accomplish your goals.
315              
316             =head1 METHODS
317              
318             Methods are constructed by abstracting out the need to specify the HTTP method (POST or GET) and automagically building the request URI according to the documentation provided by L<LogicBoxes|http://www.logicboxes.com> (see the Logic Boxes API user guide at L<http://manage.logicboxes.com/kb/answer/744> for additional information).
319              
320             =head2 Method Naming
321              
322             To fully understand the method names it's best to take a specific example (in this case the suggestion of domain names).
323              
324             =head3 Suggest Domains (and many others)
325              
326             my $response = $logic_boxes->domains__suggest_names({
327             'keyword' => 'car',
328             'tlds' => ['com', 'net', 'org'],
329             'no-of-results' => 10,
330             'hypehn-allowed'=> 'false',
331             'add-related' => 'true',
332             });
333              
334             L<LogicBoxes|http://www.logicboxes.com>' API states that this method is part of their HTTP API, specifically the Domain Category and more specifically the Suggest Names method. The sample URI for this request would then be:
335              
336             https://test.httpapi.com/api/domains/suggest-names.json?auth-userid=0&auth-password=password&keyword=domain&tlds=com&tlds=net&no-of-results=0&hyphen-allowed=true&add-related=true
337              
338             The method name is built using the URI that the request is expected at in a logical way. Since this method is a member of the Domains Category and is specifically Suggest Names we end up:
339              
340             $logic_boxes->domains__suggest_names
341              
342             Where everything before the first "__" is the category and everything following it is the specific method (with - replaced with _ and / replaced with __).
343              
344             =head2 Arguments Passed to Methods
345              
346             The specific arguments each method requires is not enforced by this module, rather it is left to the developer to reference the L<LogicBoxes API|http://manage.logicboxes.com/kb/answer/744> and to pass the correct arguments to each method as a hash. Again, this is a module of last resort, you should really be using the exposed Commands if at all posible.
347              
348             There are two I<odd> cases that you should be aware of with respect to the way arguments must be passed.
349              
350             =head3 Repeated Elements
351              
352             For methods such as domains__check that accept the same I<key> multiple times:
353              
354             https://test.httpapi.com/api/domains/available.json?auth-userid=0&auth-password=password&domain-name=domain1&domain-name=domain2&tlds=com&tlds=net
355              
356             This module accepts a hash where the key is the name of the argument (such as domain-name) and the value is an array of values you wish to pass:
357              
358             $logic_boxes->domains__available({
359             'domain-name' => ["google", "cnn"],
360             'tlds' => ["com","net"]
361             });
362              
363             This is interpreted for you automagically into the repeating elements when the API's URI is built.
364              
365             =head3 Array of Numbered Elements
366              
367             For methods such as contacts__set_details that accept the same key multiple times except an incrementing digit is appended:
368              
369             https://test.httpapi.com/api/contacts/set-details.json?auth-userid=0&auth-password=password&contact-id=0&attr-name1=sponsor1&attr-value1=0&product-key=dotcoop
370              
371             This module still accepts a hash and leaves it to the developer to handle the appending of the incrementing digit to the keys of the hash:
372              
373             $logic_boxes->contacts__set_details({
374             'contact-id' => 1337,
375             'attr-name1' => 'sponsor',
376             'attr-value1' => '0',
377             'attr-name2' => 'CPR',
378             'attr-value2' => 'COO',
379             'product-key' => 'dotcoop'
380             });
381              
382             In this way you are able to overcome the need for unique keys and still pass the needed values onto LogicBoxes' API.
383              
384             =cut