File Coverage

blib/lib/WebService/Mattermost/V4/API/Resource/User.pm
Criterion Covered Total %
statement 6 11 54.5
branch n/a
condition n/a
subroutine 2 4 50.0
pod 1 1 100.0
total 9 16 56.2


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

3: # ABSTRACT: Wrapped API methods for the users API endpoints. 4:
5: use Moo;
6: use Types::Standard qw(HashRef Str);
7:
8: extends 'WebService::Mattermost::V4::API::Resource';
9: with qw(
10: WebService::Mattermost::V4::API::Resource::Role::Single
11: WebService::Mattermost::V4::API::Resource::Role::View::User
12: );
13:
14: ################################################################################
15:
16: has available_user_roles => (is => 'ro', isa => HashRef, lazy => 1, builder => 1);
17:
18: has role_system_admin => (is => 'ro', isa => Str, default => 'system_admin');
19: has role_system_user => (is => 'ro', isa => Str, default => 'system_user');
20:
21: ################################################################################
22:
23: around [ qw(
24: get
25: update
26: teams
27: patch
28:
29: update_roles
30: update_active_status
31: update_password
32: update_authentication_method
33:
34: generate_mfa_secret
35: update_mfa
36:
37: get_profile_image
38: set_profile_image
39:
40: get_status
41: set_status
42:
43: get_sessions
44: revoke_session
45: revoke_all_sessions
46:
47: get_preferences
48: set_preferences
49: delete_preferences
50: list_preferences_by_category
51: get_preference_by_category_and_name
52:
53: get_flagged_posts
54:
55: remove_reaction
56:
57: get_authorized_apps
58: get_authorised_apps
59: ) ] => sub {
60: my $orig = shift;
61: my $self = shift;
62: my $id = shift;
63:
64: return $self->validate_id($orig, $id, @_);
65: };
66:
67: sub get {
68: my $self = shift;
69: my $id = shift;
70:
71: return $self->_single_view_get({
72: endpoint => '%s',
73: ids => [ $id ],
74: });
75: }
76:
77: sub update {
78: my $self = shift;
79: my $id = shift;
80: my $args = shift;
81:
82: return $self->_put({
83: endpoint => '%s',
84: ids => [ $id ],
85: parameters => $args,
86: });
87: }
88:
89: sub teams {
90: my $self = shift;
91: my $id = shift;
92:
93: return $self->_get({
94: endpoint => '%s/teams',
95: ids => [ $id ],
96: view => 'Team',
97: });
98: }
99:
100: sub deactivate {
101: my $self = shift;
102: my $id = shift;
103:
104: return $self->_delete({
105: endpoint => '%s',
106: ids => [ $id ],
107: });
108: }
109:
110: sub patch {
111: my $self = shift;
112: my $id = shift;
113: my $args = shift;
114:
115: return $self->_put({
116: endpoint => '%s/patch',
117: ids => [ $id ],
118: parameters => $args,
119: });
120: }
121:
122: sub update_roles {
123: my $self = shift;
124: my $id = shift;
125: my $roles = shift; # ArrayRef
126:
127: foreach my $role (@{$roles}) {
128: unless ($self->available_user_roles->{$role}) {
129: my $err = sprintf('"%s" is not a valid role. Valid roles: %s',
130: $role, join(', ', keys %{$self->available_user_roles}));
131:
132: return $self->error_return($err);
133: }
134: }
135:
136: return $self->_put({
137: endpoint => '%s/roles',
138: ids => [ $id ],
139: parameters => {
140: roles => $roles,
141: },
142: });
143: }
144:
145: sub generate_mfa_secret {
146: my $self = shift;
147: my $id = shift;
148:
149: return $self->_post({
150: endpoint => '%s/mfa/generate',
151: ids => [ $id ],
152: });
153: }
154:
155: sub update_mfa {
156: my $self = shift;
157: my $id = shift;
158: my $args = shift;
159:
160: return $self->_put({
161: endpoint => '%s/mfa',
162: ids => [ $id ],
163: parameters => $args,
164: });
165: }
166:
167: sub get_profile_image {
168: my $self = shift;
169: my $id = shift;
170:
171: return $self->_get({
172: endpoint => '%s/image',
173: ids => [ $id ],
174: });
175: }
176:
177: sub set_profile_image {
178: my $self = shift;
179: my $id = shift;
180: my $filename = shift;
181:
182: unless ($filename && -f $filename) {
183: return $self->error_return(sprintf('%s is not a valid file', $filename));
184: }
185:
186: return $self->_post({
187: endpoint => '%s/image',
188: ids => [ $id ],
189: override_data_type => 'form',
190: parameters => {
191: image => { file => $filename },
192: },
193: });
194: }
195:
196: sub update_active_status {
197: my $self = shift;
198: my $id = shift;
199: my $args = shift;
200:
201: return $self->_put({
202: endpoint => '%s/active',
203: ids => [ $id ],
204: parameters => $args,
205: required => [ 'active' ],
206: });
207: }
208:
209: sub update_password {
210: my $self = shift;
211: my $id = shift;
212: my $args = shift;
213:
214: return $self->_put({
215: endpoint => '%s/password',
216: ids => [ $id ],
217: parameters => $args,
218: });
219: }
220:
221: sub update_authentication_method {
222: my $self = shift;
223: my $id = shift;
224: my $args = shift;
225:
226: return $self->_put({
227: endpoint => '%s/auth',
228: ids => [ $id ],
229: paramters => $args,
230: });
231: }
232:
233: sub get_status {
234: my $self = shift;
235: my $id = shift;
236:
237: return $self->_single_view_get({
238: endpoint => '%s/status',
239: ids => [ $id ],
240: view => 'User::Status',
241: });
242: }
243:
244: sub set_status {
245: my $self = shift;
246: my $id = shift;
247: my $status = shift;
248:
249: # online, away, offline, dnd
250:
251: return $self->_single_view_put({
252: endpoint => '%s/status',
253: ids => [ $id ],
254: parameters => {
255: status => $status,
256: },
257: required => [ 'status' ],
258: view => 'User::Status',
259: });
260: }
261:
262: sub get_sessions {
263: my $self = shift;
264: my $id = shift;
265:
266: return $self->_get({
267: endpoint => '%s/sessions',
268: ids => [ $id ],
269: view => 'User::Session',
270: });
271: }
272:
273: sub revoke_session {
274: my $self = shift;
275: my $id = shift;
276: my $session_id = shift;
277:
278: return $self->_single_view_post({
279: endpoint => '%s/sessions/revoke',
280: ids => [ $id ],
281: parameters => {
282: session_id => $session_id,
283: },
284: required => [ 'session_id' ],
285: view => 'Status',
286: });
287: }
288:
289: sub revoke_all_sessions {
290: my $self = shift;
291: my $id = shift;
292:
293: return $self->_single_view_post({
294: endpoint => '%s/sessions/revoke/all',
295: view => 'Status',
296: });
297: }
298:
299: sub get_preferences {
300: my $self = shift;
301: my $id = shift;
302:
303: return $self->_get({
304: endpoint => '%s/preferences',
305: ids => [ $id ],
306: view => 'User::Preference',
307: });
308: }
309:
310: sub set_preferences {
311: my $self = shift;
312: my $id = shift;
313: my $args = shift;
314:
315: unless (ref $args eq 'ARRAY') {
316: return $self->error_return('An ArrayRef of preferences must be passed');
317: }
318:
319: return $self->_single_view_put({
320: endpoint => '%s/preferences',
321: ids => [ $id ],
322: parameters => $args,
323: view => 'Status',
324: });
325: }
326:
327: sub delete_preferences {
328: my $self = shift;
329: my $id = shift;
330: my $args = shift;
331:
332: unless (ref $args eq 'ARRAY') {
333: return $self->error_return('An ArrayRef of preferences must be passed');
334: }
335:
336: return $self->_single_view_post({
337: endpoint => '%s/preferences/delete',
338: ids => [ $id ],
339: parameters => $args,
340: view => 'Status',
341: });
342: }
343:
344: sub list_preferences_by_category {
345: my $self = shift;
346: my $id = shift;
347: my $category = shift;
348:
349: unless ($category) {
350: return $self->error_return('A category is required');
351: }
352:
353: return $self->_get({
354: endpoint => '%s/preferences/%s',
355: ids => [ $id, $category ],
356: view => 'User::Category',
357: });
358: }
359:
360: sub get_preference_by_category_and_name {
361: my $self = shift;
362: my $id = shift;
363: my $category = shift;
364: my $name = shift;
365:
366: unless ($category && $name) {
367: return $self->error_return('A category and a name must be passed');
368: }
369:
370: return $self->_single_view_get({
371: endpoint => '%s/preferences/%s/name/%s',
372: ids => [ $id, $category, $name ],
373: view => 'User::Category',
374: });
375: }
376:
377: sub get_flagged_posts {
378: my $self = shift;
379: my $id = shift;
380: my $args = shift;
381:
382: return $self->_single_view_get({
383: endpoint => '%s/posts/flagged',
384: ids => [ $id ],
385: parameters => $args,
386: view => 'Thread',
387: });
388: }
389:
390: sub remove_reaction {
391: my $self = shift;
392: my $user_id = shift;
393: my $post_id = shift;
394: my $emoji_name = shift;
395:
396: unless ($post_id && $emoji_name) {
397: return $self->error_return('A post ID and an emoji name are required');
398: }
399:
400: return $self->_single_view_delete({
401: endpoint => '%s/posts/%s/reactions/%s',
402: ids => [ $user_id, $post_id, $emoji_name ],
403: view => 'Status',
404: });
405: }
406:
407: sub get_authorized_apps { shift->get_authorised_apps(@_) }
408:
409: sub get_authorised_apps {
410: my $self = shift;
411: my $id = shift;
412: my $args = shift;
413:
414: return $self->_get({
415: endpoint => '%s/oauth/apps/authorized',
416: ids => [ $id ],
417: parameters => $args,
418: view => 'Application',
419: });
420: }
421:
422: ################################################################################
423:
424: sub _build_available_user_roles {
425: my $self = shift;
426:
427: return {
428: $self->role_system_admin => 1,
429: $self->role_system_user => 1,
430: };
431: }
432:
433: ################################################################################
434:
435: 1;
436:
437: __END__
438:
439: =pod
440:
441: =encoding UTF-8
442:
443: =head1 NAME
444:
445: WebService::Mattermost::V4::API::Resource::User - Wrapped API methods for the users API endpoints.
446:
447: =head1 VERSION
448:
449: version 0.30
450:
451: =head1 DESCRIPTION
452:
453: API methods relating to a single user by ID.
454:
455: =head2 USAGE
456:
457: use WebService::Mattermost;
458:
459: my $mm = WebService::Mattermost->new({
460: authenticate => 1,
461: username => 'me@somewhere.com',
462: password => 'hunter2',
463: base_url => 'https://my.mattermost.server.com/api/v4/',
464: });
465:
466: my $resource = $mm->api->user;
467:
468: Optionally, you can set a global user ID for the resource and not pass the ID
469: to every method:
470:
471: $resource->id('USER-ID-HERE');
472:
473: =head2 METHODS
474:
475: All of the below methods can either be called as documented under each item, or
476: from a user result object:
477:
478: my $user = $resource->get('USER-ID-HERE')->item;
479:
480: # Calls method "teams"
481: my $response = $user->call('teams');
482:
483: # Calls method "update"
484: $response = $user->call('update', {
485: # parameters
486: });
487:
488: =over 4
489:
490: =item C<get()>
491:
492: L<Get a user|https://api.mattermost.com/#tag/users%2Fpaths%2F~1users~1%7Buser_id%7D%2Fget>
493:
494: Get a user by their ID.
495:
496: my $response = $resource->get('USER-ID-HERE');
497:
498: =item C<update()>
499:
500: L<Update a user|https://api.mattermost.com/#tag/users%2Fpaths%2F~1users~1%7Buser_id%7D%2Fput>
501:
502: Update a user by their ID.
503:
504: my $response = $resource->update('USER-ID-HERE', {
505: # Optional arguments
506: email => '...',
507: username => '...',
508: first_name => '...',
509: last_name => '...',
510: nickname => '...',
511: locale => '...',
512: position => '...',
513: props => {
514: # ...
515: },
516: notify_props => {
517: email => \1,
518: push => \1,
519: desktop => \1,
520: desktop_sound => \1,
521: mention_keys => \1,
522: channel => \1,
523: first_name => \1,
524: },
525: });
526:
527: =item C<teams()>
528:
529: L<Get a user's teams|https://api.mattermost.com/#tag/teams%2Fpaths%2F~1users~1%7Buser_id%7D~1teams%2Fget>
530:
531: =item C<deactivate()>
532:
533: L<Deactivate a user account|https://api.mattermost.com/#tag/users%2Fpaths%2F~1users~1%7Buser_id%7D%2Fdelete>
534:
535: Set a user as inactive by ID.
536:
537: $response->deactivate('USER-ID-HERE');
538:
539: =item C<patch()>
540:
541: L<Patch a user|https://api.mattermost.com/#tag/users%2Fpaths%2F~1users~1%7Buser_id%7D~1patch%2Fput>
542:
543: my $response = $resource->patch('USER-ID-HERE', {
544: # Optional parameters:
545: email => '...',
546: username => '...',
547: first_name => '...',
548: last_name => '...',
549: nickname => '...',
550: locale => '...',
551: position => '...',
552: props => {
553: # ...
554: },
555: notify_props => {
556: email => \1,
557: push => \1,
558: desktop => \1,
559: desktop_sound => \1,
560: mention_keys => \1,
561: channel => \1,
562: first_name => \1,
563: },
564: });
565:
566: =item C<update_roles()>
567:
568: L<Update a user's roles|https://api.mattermost.com/#tag/users%2Fpaths%2F~1users~1%7Buser_id%7D~1roles%2Fput>
569:
570: Valid roles are C<system_user> and C<system_admin>.
571:
572: my $response = $resource->update_roles('USER-ID-HERE', [
573: 'ROLE-NAME-HERE',
574: 'ANOTHER-ROLE-HERE',
575: ]);
576:
577: =item C<generate_mfa_secret()>
578:
579: L<Generate MFA secret|https://api.mattermost.com/#tag/users%2Fpaths%2F~1users~1%7Buser_id%7D~1mfa~1generate%2Fpost>
580:
581: Returns a base64 encoded QR code image.
582:
583: my $response = $resource->generate_mfa_secret('USER-ID-HERE');
584:
585: =item C<update_mfa()>
586:
587: L<Update a user's MFA|https://api.mattermost.com/#tag/users%2Fpaths%2F~1users~1%7Buser_id%7D~1mfa%2Fput>
588:
589: Set whether a user requires multi-factor auth. If the user currently has MFA
590: active, a code from the MFA client is required.
591:
592: my $response = $resource->update_mfa('ID-HERE', {
593: activate => \1, # or \0 for false
594: code => 1234, # required if MFA is already active
595: });
596:
597: =item C<get_profile_image()>
598:
599: L<Get user's profile image|https://api.mattermost.com/#tag/users%2Fpaths%2F~1users~1%7Buser_id%7D~1image%2Fget>
600:
601: Get a user's profile image. Warning: returns binary content.
602:
603: my $response = $resource->get_profile_image('ID-HERE');
604:
605: # $response->raw_content contains the image as binary
606:
607: =item C<set_profile_image()>
608:
609: L<Set user's profile image|https://api.mattermost.com/#tag/users%2Fpaths%2F~1users~1%7Buser_id%7D~1image%2Fpost>
610:
611: Set a user's profile image.
612:
613: my $response = $resource->set_profile_image('ID-HERE', '/path/to/file.jpg');
614:
615: =item C<update_active_status()>
616:
617: L<Update user active status|https://api.mattermost.com/#tag/users%2Fpaths%2F~1users~1%7Buser_id%7D~1active%2Fput>
618:
619: Set a user as active or inactive.
620:
621: $resource->update_active_status('ID-HERE', {
622: active => \1, # \1 for true, \0 for false
623: });
624:
625: =item C<update_password()>
626:
627: L<Update a user's password|https://api.mattermost.com/#tag/users%2Fpaths%2F~1users~1%7Buser_id%7D~1password%2Fput>
628:
629: my $response = $resource->update_password('ID-HERE', {
630: old_password => '...',
631: new_password => '...',
632: });
633:
634: =item C<update_authentication_method()>
635:
636: L<Update a user's authentication method|https://api.mattermost.com/#tag/users%2Fpaths%2F~1users~1%7Buser_id%7D~1auth%2Fput>
637:
638: my $response = $resource->update_authentication_method('USER-ID-HERE', {
639: # Optional parameters:
640: auth_data => '...',
641: auth_service => '...',
642: password => '...',
643: });
644:
645: =item C<get_status()>
646:
647: L<Get a user's status|https://api.mattermost.com/#tag/status%2Fpaths%2F~1users~1%7Buser_id%7D~1status%2Fget>
648:
649: my $response = $resource->get_status('USER-ID-HERE');
650:
651: =item C<set_status()>
652:
653: L<Update a user's status|https://api.mattermost.com/#tag/status%2Fpaths%2F~1users~1%7Buser_id%7D~1status%2Fput>
654:
655: my $response = $resource->set_status('USER-ID-HERE', 'STATUS-HERE');
656:
657: Available statuses are "online", "away", "offline" and "dnd".
658:
659: =item C<get_sessions()>
660:
661: L<Get a user's sessions|https://api.mattermost.com/#tag/users%2Fpaths%2F~1users~1%7Buser_id%7D~1sessions%2Fget>
662:
663: my $response = $resource->get_sessions('USER-ID-HERE');
664:
665: =item C<revoke_session()>
666:
667: L<Revoke a user sesssion|https://api.mattermost.com/#tag/users%2Fpaths%2F~1users~1%7Buser_id%7D~1sessions~1revoke%2Fpost>
668:
669: my $response = $resource->revoke_session('USER-ID-HERE', 'SESSION-ID-HERE');
670:
671: =item C<revoke_all_sessions()>
672:
673: L<Revoke all active sessions for a user|https://api.mattermost.com/#tag/users%2Fpaths%2F~1users~1%7Buser_id%7D~1sessions~1revoke~1all%2Fpost>
674:
675: my $response = $resource->revoke_all_sessions('USER-ID-HERE');
676:
677: =item C<get_preferences()>
678:
679: L<Get the user's preferences|https://api.mattermost.com/#tag/preferences%2Fpaths%2F~1users~1%7Buser_id%7D~1preferences%2Fget>
680:
681: my $response = $resource->get_preferences('USER-ID-HERE');
682:
683: =item C<set_preferences()>
684:
685: L<Save the user's preferences|https://api.mattermost.com/#tag/preferences%2Fpaths%2F~1users~1%7Buser_id%7D~1preferences%2Fput>
686:
687: my $response = $resource->set_preferences('USER-ID-HERE', [
688: { user_id => 'USER-ID-HERE', category => '...', name => '...', value => '...' },
689: { user_id => 'USER-ID-HERE', category => '...', name => '...', value => '...' },
690: { user_id => 'USER-ID-HERE', category => '...', name => '...', value => '...' },
691: ]);
692:
693: =item C<delete_preferences()>
694:
695: L<Delete user's preferences|https://api.mattermost.com/#tag/preferences%2Fpaths%2F~1users~1%7Buser_id%7D~1preferences~1delete%2Fpost>
696:
697: my $response = $resource->delete_preferences('USER-ID-HERE', [
698: { user_id => 'USER-ID-HERE', category => '...', name => '...', value => '...' },
699: { user_id => 'USER-ID-HERE', category => '...', name => '...', value => '...' },
700: { user_id => 'USER-ID-HERE', category => '...', name => '...', value => '...' },
701: ]);
702:
703: =item C<list_preferences_by_category()>
704:
705: L<List a user's preferences by category|https://api.mattermost.com/#tag/preferences%2Fpaths%2F~1users~1%7Buser_id%7D~1preferences~1%7Bcategory%7D%2Fget>
706:
707: my $response = $resource->list_preferences_by_category('USER-ID-HERE', 'CATEGORY-HERE');
708:
709: =item C<get_preference_by_category_and_name()>
710:
711: L<Get a specific user preference|https://api.mattermost.com/#tag/preferences%2Fpaths%2F~1users~1%7Buser_id%7D~1preferences~1%7Bcategory%7D~1name~1%7Bpreference_name%7D%2Fget>
712:
713: my $response = $resource->get_preference_by_category_and_name(
714: 'USER-ID-HERE',
715: 'CATEGORY-HERE',
716: 'NAME-HERE',
717: );
718:
719: =item C<get_flagged_posts()>
720:
721: L<Get a list of flagged posts|https://api.mattermost.com/#tag/posts%2Fpaths%2F~1users~1%7Buser_id%7D~1posts~1flagged%2Fget>
722:
723: Retrieve a list of posts flagged by the user with the given ID.
724:
725: my $response = $resource->get_flagged_posts('USER-ID-HERE', {
726: # Optional parameters
727: team_id => '...',
728: channel_id => '...',
729: page => 0,
730: per_page => 60,
731: });
732:
733: =item C<remove_reaction()>
734:
735: L<Remove a reaction from a user's post|https://api.mattermost.com/#tag/reactions%2Fpaths%2F~1users~1%7Buser_id%7D~1posts~1%7Bpost_id%7D~1reactions~1%7Bemoji_name%7D%2Fdelete>
736:
737: my $response = $resource->remove_reaction(
738: 'USER-ID-HERE',
739: 'POST-ID-HERE',
740: 'EMOJI-NAME-HERE',
741: );
742:
743: =item C<get_authorized_apps()>
744:
745: Alias for C<get_authorised_apps()>.
746:
747: =item C<get_authorised_apps()>
748:
749: L<Get authorized OAuth apps|https://api.mattermost.com/#tag/OAuth%2Fpaths%2F~1users~1%7Buser_id%7D~1oauth~1apps~1authorized%2Fget>
750:
751: my $response = $resource->get_authorised_apps('USER-ID-HERE', {
752: # Optional parameters:
753: page => 0,
754: per_page => 60,
755: });
756:
757: =back
758:
759: =head1 AUTHOR
760:
761: Mike Jones <mike@netsplit.org.uk>
762:
763: =head1 COPYRIGHT AND LICENSE
764:
765: This software is Copyright (c) 2023 by Mike Jones.
766:
767: This is free software, licensed under:
768:
769: The MIT (X11) License
770:
771: =cut
772: