File Coverage

blib/lib/WebService/Mattermost/V4/API/Resource.pm
Criterion Covered Total %
statement 108 131 82.4
branch 18 24 75.0
condition 10 12 83.3
subroutine 24 28 85.7
pod n/a
total 160 195 82.0


line stmt bran cond sub pod time code
1             package WebService::Mattermost::V4::API::Resource;
2:

3: # ABSTRACT: Base class for API resources. 4:
5: use List::MoreUtils 'all';
6: use Moo;
7: use Types::Standard qw(Bool HashRef Maybe Object Str);
8:
9: use WebService::Mattermost::Helper::Alias 'view';
10: use WebService::Mattermost::V4::API::Object::Channel;
11: use WebService::Mattermost::V4::API::Object::Icon;
12: use WebService::Mattermost::V4::API::Object::Status;
13: use WebService::Mattermost::V4::API::Object::Team;
14: use WebService::Mattermost::V4::API::Object::TeamMember;
15: use WebService::Mattermost::V4::API::Object::TeamStats;
16: use WebService::Mattermost::V4::API::Object::Plugins;
17: use WebService::Mattermost::V4::API::Object::Results;
18: use WebService::Mattermost::V4::API::Object::User;
19: use WebService::Mattermost::V4::API::Request;
20: use WebService::Mattermost::V4::API::Response;
21:
22: with qw(
23: WebService::Mattermost::Role::Logger
24: WebService::Mattermost::Role::Returns
25: WebService::Mattermost::Role::UserAgent
26: WebService::Mattermost::V4::API::Role::RequireID
27: WebService::Mattermost::V4::API::Role::NewRelatedResource
28: );
29:
30: ################################################################################
31:
32: has api => (is => 'ro', isa => Object, required => 1);
33: has base_url => (is => 'ro', isa => Str, required => 1);
34: has resource => (is => 'ro', isa => Str, required => 1);
35: has auth_token => (is => 'rw', isa => Str, required => 1);
36:
37: has DELETE => (is => 'ro', isa => Str, default => 'DELETE');
38: has GET => (is => 'ro', isa => Str, default => 'GET');
39: has headers => (is => 'ro', isa => HashRef, default => sub { {} });
40: has POST => (is => 'ro', isa => Str, default => 'POST');
41: has PUT => (is => 'ro', isa => Str, default => 'PUT');
42: has debug => (is => 'ro', isa => Bool, default => 0);
43:
44: has id => (is => 'rw', isa => Maybe[Str]);
45:
46: ################################################################################
47:
48: sub _delete {
49: my $self = shift;
50: my $args = shift;
51:
52: $args->{method} = $self->DELETE;
53:
54: return $self->_call($args);
55: }
56:
57: sub _single_view_delete {
58: my $self = shift;
59: my $args = shift;
60:
61: $args->{single} = 1;
62:
63: return $self->_delete($args);
64: }
65:
66: sub _get {
67: my $self = shift;
68: my $args = shift;
69:
70: $args->{method} = $self->GET;
71:
72: return $self->_call($args);
73: }
74:
75: sub _single_view_get {
76: my $self = shift;
77: my $args = shift;
78:
79: $args->{single} = 1;
80:
81: return $self->_get($args);
82: }
83:
84: sub _post {
85: my $self = shift;
86: my $args = shift;
87:
88: $args->{method} = $self->POST;
89:
90: return $self->_call($args);
91: }
92:
93: sub _single_view_post {
94: my $self = shift;
95: my $args = shift;
96:
97: $args->{single} = 1;
98:
99: return $self->_post($args);
100: }
101:
102: sub _put {
103: my $self = shift;
104: my $args = shift;
105:
106: $args->{method} = $self->PUT;
107:
108: return $self->_call($args);
109: }
110:
111: sub _single_view_put {
112: my $self = shift;
113: my $args = shift;
114:
115: $args->{method} = $self->PUT;
116:
117: return $self->_put($args);
118: }
119:
120: sub _call {
121: my $self = shift;
122: my $args = shift;
123:
124: if ($args->{required}) {
125: my $validation = $self->_validate($args->{parameters}, $args->{required});
126:
127: return $validation unless $validation->{valid};
128: }
129:
130: my %headers = ('Keep-Alive' => 1);
131:
132: if ($self->auth_token) {
133: $headers{Authorization} = $self->bearer($self->auth_token);
134: }
135:
136: my $request = $self->_as_request($args);
137: my $method = lc $request->method;
138:
139: my $form_type;
140:
141: if (grep { $_ eq $request->method } ($self->PUT, $self->POST)) {
142: $form_type = 'json';
143: } else {
144: $form_type = 'form';
145: }
146:
147: $form_type = $args->{override_data_type} if $args->{override_data_type};
148:
149: my $tx = $self->ua->$method(
150: $request->url => \%headers,
151: $form_type => $request->parameters,
152: );
153:
154: if (my $error = $tx->req->error) {
155: $self->logger->warn('No HTTP code was received from Mattermost. Is your server alive?');
156: $self->logger->warnf('The following may be useful: %s', $error->{message});
157: }
158:
159: return $self->_as_response($tx->res, $args);
160: }
161:
162: sub _as_request {
163: my $self = shift;
164: my $args = shift;
165:
166: $args->{auth_token} = $self->auth_token;
167: $args->{base_url} = $self->base_url;
168: $args->{resource} = $self->resource;
169: $args->{debug} = $self->debug;
170:
171: $args->{endpoint} ||= '';
172: $args->{parameters} ||= {};
173:
174: return WebService::Mattermost::V4::API::Request->new($args);
175: }
176:
177: sub _as_response {
178: my $self = shift;
179: my $res = shift;
180: my $args = shift;
181:
182: my $view_name = $self->can('view_name') && $self->view_name;
183:
184: if ($args->{view}) {
185: $view_name = $args->{view};
186: }
187:
188: if ($res->is_error && $self->debug) {
189: $self->logger->warnf('An API error occurred: %s', $res->message);
190: }
191:
192: return WebService::Mattermost::V4::API::Response->new({
193: auth_token => $self->auth_token,
194: base_url => $self->base_url,
195: code => $res->code || 0,
196: headers => $res->headers,
197: is_error => $res->is_error ? 1 : 0,
198: is_success => $res->is_success ? 1 : 0,
199: message => $res->message,
200: prev => $res,
201: raw_content => $res->body,
202: item_view => $view_name,
203: single_item => $args->{single},
204: });
205: }
206:
207: sub _validate {
208: my $self = shift;
209: my $args = shift;
210: my $required = shift;
211:
212: my %slice;
213:
214: # Grab a slice of the keys from given arguments
215: @slice{@{$required}} = @{$args}{@{$required}};
216:
217: # Return early, all's well
218: return { valid => 1 } if all { defined($_) } values %slice;
219:
220: my @missing;
221:
222: foreach my $kx (@{$required}) {
223: push @missing, $kx unless $args->{$kx};
224: }
225:
226: return {
227: valid => 0,
228: missing => \@missing,
229: error => sprintf('Required parameters missing: %s', join(', ', @missing)),
230: };
231: }
232:
233: ################################################################################
234:
235: 1;
236:
237: __END__
238:
239: =pod
240:
241: =encoding UTF-8
242:
243: =head1 NAME
244:
245: WebService::Mattermost::V4::API::Resource - Base class for API resources.
246:
247: =head1 VERSION
248:
249: version 0.28
250:
251: =head1 DESCRIPTION
252:
253: =head2 ATTRIBUTES
254:
255: =over 4
256:
257: =item C<auth_token>
258:
259: An auth token to use in the headers for every API call. Authentication is
260: required to use the Mattermost API.
261:
262: =item C<base_url>
263:
264: The API's base URL.
265:
266: =item C<resource>
267:
268: The name of the API resource, for example L<WebService::Mattermost::V4::API::Brand>'s
269: resource is 'brand'.
270:
271: =back
272:
273: =head1 AUTHOR
274:
275: Mike Jones <mike@netsplit.org.uk>
276:
277: =head1 COPYRIGHT AND LICENSE
278:
279: This software is Copyright (c) 2020 by Mike Jones.
280:
281: This is free software, licensed under:
282:
283: The MIT (X11) License
284:
285: =cut
286: