http://www.perlmonks.org?node_id=972730

three18ti has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks,
Long time listener, first time caller.

I'm working on a perl module (https://github.com/three18ti/kayako3-staffapi) to wrap the Kayako Help Desk API (http://wiki.kayako.com/display/DEV/Kayako+Staff+API) in an easy to use Perl Module.

Everything seems to work ok with the initialization and login in, however, I am having trouble when loading tickets.

Essentially, what I have done is created a method to dispatch the API call, then I have another method that parses the XML response, builds a Moose object, and sticks the Moose Object in one of the Parent Objects members.

Everything seems to work when the first ticket is loaded (or when the ticket list is loaded), however, every subsequent request never dispatches the API call, therefore returning the information from the first API call.

What follows is an excerpt from https://github.com/three18ti/kayako3-staffapi/blob/master/lib/Kayako3/StaffAPI.pm to illustrate how I am performing my calls. Any suggestions on this specific problem or the module in general are greatly appreciated.

=head2 get_ticket_list User function to get ticket list, requres department and status =cut =head2 ticket_list_respnse ticket list response object Object storage for Kayako3::StaffAPI::Response::TicketList Stores the XML response from $self->_do_ticket_list as a Moose Object =cut has 'ticket_list_response' => ( is => 'rw', isa => 'Kayako3::StaffAPI::Response::TicketList::Kayako_staffapi', lazy => 1, builder => '_do_ticket_list', handles => qr/^(?:_.*|ticket_list|get.*)/, ); =head2 _ticket_list_request_parameters Parameters set prior to calling $self->get_ticket_list to enable correct parameters are passed to function * I believe this is part of the problem =cut has '_ticket_list_request_parameters' => ( is => 'rw', isa => 'HashRef', lazy => 1, # trigger => \&_do_ticket_list, default => sub { {} }, ); =head2 get_ticket_list User function to get ticket list, requres department and status =cut sub get_ticket_list { my $self = shift; my $department = shift; my $status = shift; my ($department_id, $status_id) = ($department, $status); $self->_ticket_list_request_parameters( { sessionid => $self->_session_id, departmentid => $department_id, } ); $self->_ticket_list_request_parameters->{statusid} = $status_id if $status_id; } =head2 _do_ticket_list Performs the parsing of XML and initialization of "Ticket List Response" Object =cut sub _do_ticket_list { my $self = shift; # loads options from $self->_ticket_list_request_parameters my $response = $self->_get_ticket_list; my $unzipped_content = $self->_unzip($response->content); my $loader = XML::Toolkit::App->new( xmlns => { '' => 'Kayako3::StaffAPI::Response::TicketList' } )->loader; $loader->parse_string( $unzipped_content ); shift $loader->filter->objects; } =head2 _get_ticket_list Performs the _get_ticket_list API call =cut sub _get_ticket_list { my $self = shift; my $response = $self->ua->post( $self->_api_ticket_list, $self->_ticket_list_request_parameters, ); }

As you can see, what I am attempting to do is provide an interface for the user to pass parameters, then once those parameters are set, the _do_ticket_list function should be called, and return the results of _do_ticket_list results to the ticket_list_response member. Again, everything works the first time around, but every subsequent call does not trigger _do_ticket_list since the ticket_list_response member has already been built. Would un-setting ticket_list_response (or setting it equal to null... well, that would likely cause a Moose error as NULL wouldn't match the type definition.) do the trick, or is there something else that I'm overlooking?

Also, if you have any suggestions to the overall format of the module, please do let me know.

Thanks in advance.

Replies are listed 'Best First'.
Re: Moose Troubles
by moritz (Cardinal) on May 27, 2012 at 19:36 UTC
    As you can see, what I am attempting to do is provide an interface for the user to pass parameters, then once those parameters are set, the _do_ticket_list function should be called, and return the results of _do_ticket_list results to the ticket_list_response member. Again, everything works the first time around, but every subsequent call does not trigger _do_ticket_list since the ticket_list_response member has already been built

    If you want some action to be triggered every time ticket_list_response is accessed, you shouldn't make it an attribute, but a method right away.

      Hello,
      Thank you for the reply.

      I guess my confusion is, ticket_list_response is an object that delegates methods, changing ticket_list_response to a method then won't allow me to call object methods later.

      For instance, I want to initialize ticket_list_response attribute, then call count_tickets on the ticket_list_response object.

      #!/usr/bin/perl use Kayako3::Staff; # this initializes Kayako3::Staff object, # performs the login, # parses the login response and stores the user session ID, # then initializes the info_response object which provides # all the info for id lookups later on my $help_desk = Kayako3::Staff->new({ username => 'bob', password => 'my_pass', api_url => 'http://example.com/staffapi?', }); # since the API requires integer ids, # I perform a lookup in separate steps for clarity # usually, I'll perform this all in one step: # $help_desk->get_ticket_list( # $help_desk->get_department_id("Support"), # $help_desk->get_ticket_status_id("New") # ); my $department_id = $help_desk->get_department_id("Support"); my $ticket_status_id = $help_desk->get_ticket_status_id("New"); $hd->get_ticket_list($department_id, $ticket_status_id) # once the ticket is loaded: print "Ticket count in " . $help_desk->ticket_list_response->department . ", for tickets with status " . $help_desk->ticket_list_response->status . ": " . $help_desk->get_ticket_count . "\n"; # sample output: # Ticket count in Support, for tickets with status New: 10 # then later on, I want to do the same thing # for a different department: my $department_id = $help_desk->get_department_id("Sales"); my $ticket_status_id = $help_desk->get_ticket_status_id("In progress"); $hd->get_ticket_list($department_id, $ticket_status_id) #Then I want to print the ticket count again, # for the new department, # but performing $help_desk->get_ticket_count # returns the same results above print "Ticket count in " . $help_desk->ticket_list_response->department . ", for tickets with status " . $help_desk->ticket_list_response->status . ": " . $help_desk->get_ticket_count . "\n"; # sample output: # Ticket count in Support, for tickets with status New: 10 # desired ouutput: # Ticket count in Sales, for tickets with status In Progress: 15

      Usually, I wouldn't call $help_desk->_ticket_list_response->department directly, however, it is important to help illustrate my problem.

      I don't want to call ticket_list_response directly because it only provides a place holder to store the Kayako3::Staff:Response::TicketList object.

        When you want the ticket count for another department and/or another status shouldn't you start with a new object, rather than with an already existing object? Your _do_ticket_list method will only be called at the object's initialization, so changing the department id or the ticket status will go unnoticed and the ticket list is never refreshed.

        Reader beware: my Moose skills may be a bit rusty and the finer issues of builders and triggers may have escaped me.

        Update: Question: where does your $hd object come from? I never see you initialize it. Or is it perhaps a typo and did you mean $help_desk?

        CountZero

        A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

        My blog: Imperial Deltronics
        I guess my confusion is, ticket_list_response is an object that delegates methods, changing ticket_list_response to a method then won't allow me to call object methods later.

        Then store the object that is now stored in ticket_list_response somewhere else (for example in a private attribute), and make ticket_list_response a method that does whatever you want it to do whenever it is called.

Re: Moose Troubles
by Anonymous Monk on May 27, 2012 at 19:58 UTC
    If you're going to release this on CPAN, consider renaming it to WebService::Kayako::Staff. API in the module name is strongly discouraged: http://pause.perl.org/pause/query?ACTION=pause_namingmodules#Avoid_API_Interface_and_the_like

      Hello,
      Thanks for the advice and the link.

      My question is this, I am writing an API to an API, there are two APIs as named by my vendor 'StaffAPI', and 'RESTAPI', what you are suggesting is I rename to 'Kayako3::Staff', and 'Kayako3::REST' ?
      (Here's the real question) Would you say that dropping the API from the name is more important that naming cannon between my Module and the Vendor provided API? IOW, I called it StaffAPI because I thought that would reduce confusion as it is named the same by the vendor, is this an invalid reason?

      My second question is about the WebService name space, is some guidance on when I should and when I should not use the WebService name space?