How to Link / Get Config Item to a Ticket through Webservice (SOAP or REST) in OTRS - perl

I want to know how to get and link the ticket to Configuration item through SOAP or REST Webservice.
I have imported this Restfull Web service in admin console and successfully created and getting ticket information using this url
http://XXX.XXX.XXX.XXX/otrs/nph-genericinterface.pl/Webservice/GenericTicketConnectorREST/Ticket/1.
but the problem is the linked config item information is not coming when i get the ticket information.
i did lots of search on google found that ticket can be linked to Config Item through OTRS GUI and in AgentTicketzoom page it will show, i want this to be done through web service.
can any one help me in this problem or suggest some doc on how to create web service to get linked object information from ticket.
Updated#1
i added web controller to my existing Ticket connector successfully. the url is http://XXX.XXX.XXX.XXX/otrs/nph-genericinterface.pl/Webservice/GenericTicketConnectorRest/LinkObject with POST Call.but i am getting this error
{"faultcode":"Server","faultstring":"Got no ConfigObject!"}
i checked the initial parameters also
$VAR1 = { 'Password' => '1234567', 'RequestMethod' => 'POST','SourceKey' => '1', 'SourceObject' => 'Ticket', 'State' => 'valid', 'TargetKey' => '2', 'TargetObject' => 'ITSMConfigItem', 'Type' => 'ParentChild', 'UserID' => '1', 'UserLogin' => 'XXXXX.XXXX#XXXX.com'};
$VAR1 = { 'ErrorMessage' => 'Got no ConfigObject!', 'Success' => 0};

Yes, the ticket can be linked to a configItem via a GUI and it can be done via a Webservice.
First of all you should write a new Generic Interface Connector operation, which will handle method LinkAdd from LinkObject Class ( APIdoc )
Then create and register new operations via a new XML file, like this:
FILE NAME: GenericInterfaceLinkObjectConnector.xml
<?xml version="1.0" encoding="utf-8"?>
<otrs_config version="1.0" init="Application">
<ConfigItem Name="GenericInterface::Operation::Module###LinkObject::LinkAdd" Required="0" Valid="1">
<Description Translatable="1">GenericInterface module registration for the operation layer.</Description>
<Group>GenericInterface</Group>
<SubGroup>GenericInterface::Operation::ModuleRegistration</SubGroup>
<Setting>
<Hash>
<Item Key="Name">LinkAdd</Item>
<Item Key="Controller">LinkObject</Item>
<Item Key="ConfigDialog">AdminGenericInterfaceOperationDefault</Item>
</Hash>
</Setting>
</ConfigItem>
</otrs_config>
After that you can publish a new provider WebService from OTRS GUI, where a newly created connector is used.
Make sure, that you pass all the needed parameters for the method!!!
$True = $LinkObject->LinkAdd(
SourceObject => 'Ticket',
SourceKey => '321',
TargetObject => 'FAQ',
TargetKey => '5',
Type => 'ParentChild',
State => 'Valid',
UserID => 1,
);
UPDATE:
Please read this Document to understand how Generic Interface is built and then please add a new Connector ( LinkObject )
To register the connector and its operation - place XML file in /Kernel/Config/Files/...
Then go to Sysconfig -> GenericInterface -> GenericInterface::Operation::ModuleRegistration and set a tick next to the GenericInterface::Operation::Module###LinkObject::LinkAdd and save changes
Afterwards add this Connector file to /Custom/Kernel/GenericInterface/Operation/LinkObject/LinkAdd.pm
# --
# Kernel/GenericInterface/Operation/LinkObject/LinkAdd.pm - GenericInterface LinkAdd operation backend
# Copyright (C) 2016 ArtyCo (Artjoms Petrovs), http://artjoms.lv/
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
# the enclosed file COPYING for license information (AGPL). If you
# did not receive this file, see http://www.gnu.org/licenses/agpl.txt.
# --
package Kernel::GenericInterface::Operation::LinkObject::LinkAdd;
use strict;
use warnings;
use Kernel::GenericInterface::Operation::Common;
use Kernel::System::LinkObject;
use Kernel::System::VariableCheck qw(IsStringWithData IsHashRefWithData);
=head1 NAME
Kernel::GenericInterface::Operation::LinkObject::LinkAdd - GenericInterface Link Create Operation backend
=head1 SYNOPSIS
=head1 PUBLIC INTERFACE
=over 4
=cut
=item new()
usually, you want to create an instance of this
by using Kernel::GenericInterface::Operation->new();
=cut
sub new {
my ( $Type, %Param ) = #_;
my $Self = {};
bless( $Self, $Type );
# check needed objects
for my $Needed (
qw(DebuggerObject ConfigObject MainObject LogObject TimeObject DBObject EncodeObject WebserviceID)
)
{
if ( !$Param{$Needed} ) {
return {
Success => 0,
ErrorMessage => "Got no $Needed!"
};
}
$Self->{$Needed} = $Param{$Needed};
}
# create additional objects
$Self->{CommonObject} = Kernel::GenericInterface::Operation::Common->new( %{$Self} );
$Self->{LinkObject}
= Kernel::System->LinkObject->new( %{$Self} );
return $Self;
}
=item Run()
Create a new link.
my $Result = $OperationObject->Run(
Data => {
SourceObject => 'Ticket',
SourceKey => '321',
TargetObject => 'Ticket',
TargetKey => '12345',
Type => 'ParentChild',
State => 'Valid',
UserID => 1,
},
);
$Result = {
Success => 1, # 0 or 1
ErrorMessage => '', # In case of an error
Data => {
Result => 1, # 0 or 1
},
};
=cut
sub Run {
my ( $Self, %Param ) = #_;
# check needed stuff
if ( !IsHashRefWithData( $Param{Data} ) ) {
return $Self->{CommonObject}->ReturnError(
ErrorCode => 'LinkAdd.MissingParameter',
ErrorMessage => "LinkAdd: The request is empty!",
);
}
my $LinkID = $Self->{LinkObject}->LinkAdd(
%Param,
);
if ( !$LinkID ) {
return $Self->{CommonObject}->ReturnError(
ErrorCode => 'LinkAdd.AuthFail',
ErrorMessage => "LinkAdd: Authorization failing!",
);
}
return {
Success => 1,
Data => {
Result => $LinkID,
},
};
}
1;
=back
=head1 TERMS AND CONDITIONS
This software is part of the OTRS project (L<http://otrs.org/>).
This software comes with ABSOLUTELY NO WARRANTY. For details, see
the enclosed file COPYING for license information (AGPL). If you
did not receive this file, see L<http://www.gnu.org/licenses/agpl.txt>.
=cut
And afterwards it should appear and can be used from the Admin -> WebServices -> Available Operations dropdown and of course can be used as a webservice.
A PHP usage example can be seen below:
#### Initialize new client session ####
$client = new SoapClient(
null,
array(
'location' => $url,
'uri' => "Core",
'trace' => 1,
'login' => $username,
'password' => $password,
'style' => SOAP_RPC,
'use' => SOAP_ENCODED
)
);
#### Create and send the SOAP Function Call ####
$success = $client->__soapCall("Dispatch",
array($username, $password,
"LinkObject", "LinkAdd",
"SourceObject", 'Ticket',
"SourceKey", $ticket_id1,
"TargetObject", 'Ticket',
"TargetKey", $ticket_id2,
"Type", 'ParentChild',
"State", 'Valid',
"UserID", '1'
));
In case of errors - enable debugging, review the System Log and check all the initial settings of OTRS
Good Luck!
UPDATE #2
To register a webservice - press the button Add new webservice, name it as you want it and set the following settings ( Select the LinkAdd Operation ) and save it
UPDATE #3
Here is an updated module file for OTRS 5
# --
# Kernel/GenericInterface/Operation/LinkObject/LinkAdd.pm - GenericInterface LinkAdd operation backend
# Copyright (C) 2016 ArtyCo (Artjoms Petrovs), http://artjoms.lv/
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
# the enclosed file COPYING for license information (AGPL). If you
# did not receive this file, see http://www.gnu.org/licenses/agpl.txt.
# --
package Kernel::GenericInterface::Operation::LinkObject::LinkAdd;
use strict;
use warnings;
use Kernel::GenericInterface::Operation::Common;
use Kernel::System::LinkObject;
use Kernel::System::VariableCheck qw(IsStringWithData IsHashRefWithData);
=head1 NAME
Kernel::GenericInterface::Operation::LinkObject::LinkAdd - GenericInterface Link Create Operation backend
=head1 SYNOPSIS
=head1 PUBLIC INTERFACE
=over 4
=cut
=item new()
usually, you want to create an instance of this
by using Kernel::GenericInterface::Operation->new();
=cut
sub new {
my ( $Type, %Param ) = #_;
my $Self = {};
bless( $Self, $Type );
# check needed objects
for my $Needed (
qw( DebuggerObject WebserviceID )
)
{
if ( !$Param{$Needed} ) {
return {
Success => 0,
ErrorMessage => "Got no $Needed!"
};
}
$Self->{$Needed} = $Param{$Needed};
}
# create additional objects
$Self->{CommonObject} = Kernel::GenericInterface::Operation::Common->new( %{$Self} );
$Self->{LinkObject}
= $Kernel::OM->Get('Kernel::System::LinkObject');
return $Self;
}
=item Run()
Create a new link.
my $Result = $OperationObject->Run(
Data => {
SourceObject => 'Ticket',
SourceKey => '321',
TargetObject => 'Ticket',
TargetKey => '12345',
Type => 'ParentChild',
State => 'Valid',
UserID => 1,
},
);
$Result = {
Success => 1, # 0 or 1
ErrorMessage => '', # In case of an error
Data => {
Result => 1, # 0 or 1
},
};
=cut
sub Run {
my ( $Self, %Param ) = #_;
# check needed stuff
if ( !IsHashRefWithData( $Param{Data} ) ) {
return $Self->{CommonObject}->ReturnError(
ErrorCode => 'LinkAdd.MissingParameter',
ErrorMessage => "LinkAdd: The request is empty!",
);
}
my $LinkID = $Self->{LinkObject}->LinkAdd(
%Param,
);
if ( !$LinkID ) {
return $Self->{CommonObject}->ReturnError(
ErrorCode => 'LinkAdd.AuthFail',
ErrorMessage => "LinkAdd: Authorization failing!",
);
}
return {
Success => 1,
Data => {
Result => $LinkID,
},
};
}
1;
=back
=head1 TERMS AND CONDITIONS
This software is part of the OTRS project (L<http://otrs.org/>).
This software comes with ABSOLUTELY NO WARRANTY. For details, see
the enclosed file COPYING for license information (AGPL). If you
did not receive this file, see L<http://www.gnu.org/licenses/agpl.txt>.
=cut

After lot of work, i've found the solution to made it by POST REST call.
My main trouble was about creating LinkObject with right %Param.
So, i've implement direct code to aquire right call LinkAdd.
Mainly, Artjoman provide the way. But i catch some errors on it, so here is another perl module in OTRS_HOME/Custom/Kernel/GenericInterface/Operation/LinkObject/LinkAdd.pm:
# --
# Kernel/GenericInterface/Operation/LinkObject/LinkAdd.pm - GenericInterface LinkAdd operation backend
# Copyright (C) 2016 ArtyCo (Artjoms Petrovs), http://artjoms.lv/
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
# the enclosed file COPYING for license information (AGPL). If you
# did not receive this file, see http://www.gnu.org/licenses/agpl.txt.
# --
package Kernel::GenericInterface::Operation::LinkObject::LinkAdd;
use strict;
use warnings;
use Kernel::System::ObjectManager;
use Kernel::System::VariableCheck qw(IsStringWithData IsHashRefWithData);
=head1 NAME
Kernel::GenericInterface::Operation::LinkObject::LinkAdd - GenericInterface Link Create Operation backend
=head1 SYNOPSIS
=head1 PUBLIC INTERFACE
=over 4
=cut
=item new()
usually, you want to create an instance of this
by using Kernel::GenericInterface::Operation->new();
=cut
sub new {
my ( $Type, %Param ) = #_;
my $Self = {};
bless( $Self, $Type );
# check needed objects
for my $Needed (
qw( DebuggerObject WebserviceID )
)
{
if ( !$Param{$Needed} ) {
return {
Success => 0,
ErrorMessage => "Got no $Needed!"
};
}
$Self->{$Needed} = $Param{$Needed};
}
# create additional objects
local $Kernel::OM = Kernel::System::ObjectManager->new( %{$Self} );
$Self->{LinkObject}
= $Kernel::OM->Get('Kernel::System::LinkObject');
return $Self;
}
=item Run()
Create a new link.
my $Result = $OperationObject->Run(
Data => {
SourceObject => 'Ticket',
SourceKey => '321',
TargetObject => 'Ticket',
TargetKey => '12345',
Type => 'ParentChild',
State => 'Valid',
UserID => 1,
},
);
$Result = {
Success => 1, # 0 or 1
ErrorMessage => '', # In case of an error
Data => {
Result => 1, # 0 or 1
},
};
=cut
sub Run {
my ( $Self, %Param ) = #_;
# check needed stuff
if ( !IsHashRefWithData( $Param{Data} ) ) {
return $Self->ReturnError(
ErrorCode => 'LinkAdd.MissingParameter',
ErrorMessage => "LinkAdd: The request is empty!",
);
}
my $LinkID = $Self->{LinkObject}->LinkAdd(
'SourceKey' => $Param{Data}{SourceKey},
'SourceObject' => $Param{Data}{SourceObject},
'State' => $Param{Data}{State},
'TargetKey' => $Param{Data}{TargetKey},
'TargetObject' => $Param{Data}{TargetObject},
'Type' => $Param{Data}{Type},
'UserID' => $Param{Data}{UserID},
);
if ( !$LinkID ) {
return $Self->ReturnError(
ErrorCode => 'LinkAdd.AuthFail',
ErrorMessage => "LinkAdd: Authorization failing!",
);
}
return {
Success => 1,
Data => {
Result => $LinkID,
},
};
}
sub ReturnError {
my ( $Self, %Param ) = #_;
$Self->{DebuggerObject}->Error(
Summary => $Param{ErrorCode},
Data => $Param{ErrorMessage},
);
# return structure
return {
Success => 1,
ErrorMessage => "$Param{ErrorCode}: $Param{ErrorMessage}",
Data => {
Error => {
ErrorCode => $Param{ErrorCode},
ErrorMessage => $Param{ErrorMessage},
},
},
};
}
1;
=back
=head1 TERMS AND CONDITIONS
This software is part of the OTRS project (L<http://otrs.org/>).
This software comes with ABSOLUTELY NO WARRANTY. For details, see
the enclosed file COPYING for license information (AGPL). If you
did not receive this file, see L<http://www.gnu.org/licenses/agpl.txt>.
=cut
So, making POST call to (http://otrs_host/otrs/nph-genericinterface.pl/Webservice/GenericTicketConnectorREST/LinkAdd?UserLogin=login&Password=password) with json
{"SourceObject":"Ticket","SourceKey":"7","TargetObject":"ITSMConfigItem","TargetKey":"1","Type":"DependsOn","State":"Valid","UserID":"1"}
will create a link between Ticket and ITSMConfigItem (not a computer, hardware and so on.)
I think, this simple and quite rude solution will help to understand how to add a full-api operations to your REST otrs with a better(but working) way.

Related

How can I send mail in a Perl script?

I have adapted a script from the Perl Cookbook. I am testing it to send mail to myself in gmail.
#!/usr/bin/perl
use strict;
use warnings;
use MIME::Lite;
my $msg;
$msg = MIME::Lite->new(From => 'zmumba#gmail.com',
To => 'zmumba#gmail.com',
Subject => 'My office photo',
Type => 'multipart/mixed');
$msg->attach(Type => 'image/png',
Path => '/home/zmumba/ZMD_Proj/Docs/Reporting',
Filename => 'office_lyout.png');
$msg->attach(Type => 'TEXT',
Data => 'I hope you can use this!');
$msg->send( );
When I run this script, I get the message "/home/zmumba/ZMD_Proj/Docs/Reporting" not readable.
From here How can I send mail through Gmail with Perl? , I now understand that I have to send mail through a mailserver to use MIME::Lite. So I replaced
$msg = MIME::Lite->new(From => 'zmumba#gmail.com
with
$msg = Email::Send::Gmail->new(From => 'zmumba#gmail.com
and I get the error "Can't locate object method "new" via package Email::Send::Gmail".
Then I tried
$msg = Net::IMAP::Simple::SSL->new(From => 'zmumba#gmail.com',
and I get "Odd number of elements in hash assignment at /home/zmumba/perl5/lib/perl5/Net/IMAP/Simple.pm line 25.
Can't call method "attach" on an undefined value at ...".
Any assistance on how to go about it?
Thanks in anticipation.
The Perl Cookbook is 20 years old and its recommendations will be out of date. Using MIME::Lite is discouraged.
MIME::Lite is not recommended by its current maintainer. There are a number of alternatives, like Email::MIME or MIME::Entity and Email::Sender, which you should probably use instead. MIME::Lite continues to accrue weird bug reports, and it is not receiving a large amount of refactoring due to the availability of better alternatives. Please consider using something else.
You should probably follow their recommendation and use Email::Sender.
"Can't locate object method "new" via package Email::Send::Gmail"
You need to load Email::Send::Gmail with use Email::Send::Gmail.
You may need to install the Email::Send::Gmail module. It's simplest to do this using either cpanminus or install a fresh Perl with perlbrew and then use cpanminus.
Then I tried
$msg = Net::IMAP::Simple::SSL->new(From => 'zmumba#gmail.com',
and I get "Odd number of elements in hash assignment at /home/zmumba/perl5/lib/perl5/Net/IMAP/Simple.pm line 25.
MIME::Lite, Email::Send::Gmail, and Net::IMAP::Simple::SSL are different libraries with different interfaces that take different arguments differently. Refer to their documentation for how to use them.
As mentioned before, both MIME::Lite and Email::Send is discouraged -
Email::Send is going away... well, not really going away, but it's
being officially marked "out of favor." It has API design problems
that make it hard to usefully extend and rather than try to deprecate
features and slowly ease in a new interface, we've released
Email::Sender which fixes these problems and others
I have created a script which uses Email::MIME, Email::Sender::Simple, Email::Sender::Transport::SMTP for sending mail. You can take a look at https://github.com/rai-gaurav/perl-toolkit/tree/master/Mail and use it as per your requirement.
Important lines from that code are -
sub create_mail {
my ( $self, $file_attachments, $mail_subject, $mail_body ) = #_;
my #mail_attachments;
if (#$file_attachments) {
foreach my $attachment (#$file_attachments) {
my $single_attachment = Email::MIME->create(
attributes => {
filename => basename($attachment),
content_type => "application/json",
disposition => 'attachment',
encoding => 'base64',
name => basename($attachment)
},
body => io->file($attachment)->all
);
push( #mail_attachments, $single_attachment );
}
}
# Multipart message : It contains attachment as well as html body
my #parts = (
#mail_attachments,
Email::MIME->create(
attributes => {
content_type => 'text/html',
encoding => 'quoted-printable',
charset => 'US-ASCII'
},
body_str => $mail_body,
),
);
my $mail_to_users = join ', ', #{ $self->{config}->{mail_to} };
my $cc_mail_to_users = join ', ', #{ $self->{config}->{mail_cc_to} };
my $email = Email::MIME->create(
header => [
From => $self->{config}->{mail_from},
To => $mail_to_users,
Cc => $cc_mail_to_users,
Subject => $mail_subject,
],
parts => [#parts],
);
return $email;
}
sub send_mail {
my ( $self, $email ) = #_;
my $transport = Email::Sender::Transport::SMTP->new(
{
host => $self->{config}->{smtp_server}
}
);
eval { sendmail( $email, { transport => $transport } ); };
if ($#) {
return 0, $#;
}
else {
return 1;
}
}
Gmail and other mail servers will not allow unauthorized relay. In most cases you will need authorized access.
Here is what I use, after also trying many modules. Maybe this solution seems a little bit overdone, but it's easy to change for HTML with plain text as alternative or other attachments. Parameters of the transport are the most common ones.
#!perl
use strict;
use warnings;
use utf8;
use Email::Sender::Simple qw(sendmail try_to_sendmail);
use Email::Sender::Transport::SMTPS;
use Email::Simple ();
use Email::Simple::Creator ();
use Email::MIME;
send_my_mail('john.doe#hisdomain.com','Test','Test, pls ignore');
sub send_my_mail {
my ($to_mail_address, $subject, $body_text) = #_;
my $smtpserver = 'smtp.mydomain.com';
my $smtpport = 587;
my $smtpuser = 'me#mydomain.com';
my $smtppassword = 'mysecret';
my $transport = Email::Sender::Transport::SMTPS->new({
host => $smtpserver,
ssl => 'starttls',
port => $smtpport,
sasl_username => $smtpuser,
sasl_password => $smtppassword,
#debug => 1,
});
my $text_part = Email::MIME->create(
attributes => {
'encoding' => 'quoted-printable',
'content_type' => 'text/plain',
'charset' => 'UTF-8',
},
'body_str' => $body_text,
);
my $alternative_part = Email::MIME->create(
attributes => {
'content_type' => 'multipart/alternative',
},
parts => [ $text_part, ],
);
my $email = Email::MIME->create(
header_str => [
To => $to_mail_address,
From => "Website <$smtpuser>",
Subject => $subject,
],
attributes => {
'content_type' => 'multipart/mixed',
},
parts => [ $alternative_part ],
);
my $status = try_to_sendmail($email, { transport => $transport });
return $status;
}
Take a look at Email::Send::Gmail module. It might solve your problem.

Getting "000004DC: LdapErr: DSID-0C090752." when performing bind in perl using Net::LDAP

Objective is to get the "dn" attribute of all the computers in my Active Directory server.
When the code executes I get: "000004DC: LdapErr: DSID-0C090752, comment: In order to perform this operation a successful bind must be completed on the connection."
Here is my code:
#!/usr/bin/perl
use strict;
use Net::LDAP;
use Data::Dumper;
my $ldap = Net::LDAP->new( 'my.domain.com' ) or die $#;
my $user = 'CN=username,OU=orgname,DC=my,DC=domain,DC=com';
my $pass = 'my_password';
$ldap->bind($user, password => $pass);
#$ldap->bind;
my $mesg = $ldap->search(
base => "DC=my,DC=domain,DC=com",
filter => "ObjectClass=Computers",
attrs => "dn"
);
I've tested the user / password to log into the domain directly with success.
Additional information if I add this to the end of the script: print Dumper($mesg);
$VAR1 = bless( {
'parent' => bless( {
'net_ldap_version' => 3,
'net_ldap_scheme' => 'ldap',
'net_ldap_debug' => 0,
'net_ldap_socket' => bless( \*Symbol::GEN0, 'IO::Socket::INET' ),
'net_ldap_host' => 'my.domain.com',
'net_ldap_uri' => 'my.domain.com',
'net_ldap_resp' => {},
'net_ldap_mesg' => {},
'net_ldap_async' => 0,
'net_ldap_port' => 389,
'net_ldap_refcnt' => 1
}, 'Net::LDAP' ),
'errorMessage' => '000004DC: LdapErr: DSID-0C090752, comment: In order to perform this operation a successful bind must be completed on the connection., data 0, v2580',
'ctrl_hash' => undef,
'resultCode' => 1,
'callback' => undef,
'mesgid' => 2,
'matchedDN' => '',
'controls' => undef,
'raw' => undef
}, 'Net::LDAP::Search' );
Any suggestions on how to get this script working is what I'm looking for.
Thanks!
With regard to the error message you posted I would say the bind attempt failed.
It might help you to improve bind result:
$mesg = $_ldap->bind("***", password => "***");
$mesg->is_error && die join ';' $mesg->code, $mesg->error
See Net::LDAP:
The return value from these methods is an object derived from the
Net::LDAP::Message class. The methods of this class allow you to
examine the status of the request.

Why isn't Moose Role exclude excluding particular role attributes?

I have a Moose::Role that has (among other things):
package My::Role;
use strict;
use warnings;
use Moose::Role;
use MooseX::ClassAttribute;
class_has table => (
is => 'ro'
isa => 'Str',
lazy => 1,
);
has id => (
is => 'ro',
isa => 'Int',
predicate => 'has_id',
writer => '_id',
required => 0,
);
has other => (
is => 'rw',
isa => 'Int',
);
...
1;
Then, in a module that consumes that Role,
package Some::Module;
with 'My::Role' => {
-excludes => [qw( id table )]
};
has module_id => (
is => 'ro',
isa => 'Int',
);
...
1;
Then, in a script I'm instantiating an instance of Some::Module:
my $some_module = Some::Module->new({ other => 3 });
and I'm able to call
$some_module->id; # I'd expect this to die but returns undef.
However, I'm unable to call
$some_module->table; # this dies as I'd expect
As I'd expect calling $some_module->table causes the script to cease. Calling
$some_module->id doesn't.
When I use Data::Dumper to dump out the attribute list of the $some_module meta
class it show that the id attribute is defined but the table attribute is not.
Does anyone know why the 'id' attribute defined in the Role would not be excluded
from the meta class but the 'table' class_attribute would? The problem being, as
described above, is that users of Some::Module can call id() when they should be
required to call module_id().
Furthermore, when dumping $some_module object, the 'id' doesn't show up in the dump.
Edit:
Here's a sample that illustrates the problem. I've defined a role
that implements an id then I'm consuming the role in the package My::Product.
I'm excluding the id when consuming it however. When I print the attribute
from the meta object it shows that it is in fact there. I was under the impression
that excluding the id from a role when consuming it wouldn't allow it to be called.
I'd expect that it would not only be NOT in the meta object but also to die on
an attempt to call it.
#!/usr/bin/perl
package My::Model;
use Moose::Role;
use MooseX::ClassAttribute;
class_has first_name => (
is => 'rw',
isa => 'Str',
);
class_has last_name => (
is => 'rw',
isa => 'Str',
);
has id => (
is => 'rw',
isa => 'Int',
predicate => 'has_id',
writer => '_id',
required => 0,
);
1;
package My::Product;
use Moose;
use Class::MOP::Class;
use Data::Dumper;
with 'My::Model' => { -excludes => [ qw( first_name id ) ], };
has count => (
is => 'rw',
isa => 'Int',
);
has product_id => (
is => 'ro',
isa => 'Int',
required => 0,
predicate => 'has_product_id'
);
sub create_classes {
my #list = ();
foreach my $subclass (qw( one two three )) {
Class::MOP::Class->create(
"My::Product::"
. $subclass => (
superclasses => ["My::Product"],
)
);
push #list, "My::Product::$subclass";
}
return \#list;
}
__PACKAGE__->meta()->make_immutable;
1;
package main;
use strict;
use warnings;
use Data::Dumper;
my $product = My::Product->new();
my $classes = $product->create_classes();
my #class_list;
foreach my $class ( #{ $classes } ) {
my $temp = $class->new( { count => time } );
$temp->first_name('Don');
$temp->last_name('MouseCop');
push #class_list, $temp;
}
warn "what is the id for the first obj => " . $class_list[0]->id ;
warn "what is the first_name for the first obj => " . $class_list[0]->first_name ;
warn "what is the last_name for the first obj => " . $class_list[0]->last_name ;
warn "\nAttribute list:\n";
foreach my $attr ( $class_list[2]->meta->get_all_attributes ) {
warn "name => " . $attr->name;
# warn Dumper( $attr );
}
Edit 2:
Upon dumping the $attr I am seeing that first_name and id are in the method_exclusions.
'role_applications' => [
bless( {
'class' => $VAR1->{'associated_class'},
'role' => $VAR1->{'associated_class'}{'roles'}[0],
'method_aliases' => {},
'method_exclusions' => [
'first_name',
'id'
]
}, 'Moose::Meta::Class::__ANON__::SERIAL::8' )
]
I have no idea how the innards of this works but I believe this is to do with the fact that the two methods you are excluding are attribute methods. The only relevant article I can find is here, where it says:
A roles attributes are similar to those of a class, except
that they are not actually applied. This means that methods that are
generated by an attributes accessor will not be generated in the role,
but only created once the role is applied to a class.
Therefore I'm guessing the problem is that when your classes are being constructed, the role is applied (and the methods are excluded), but after that the role's attributes are applied and the accessor methods (including id and first_name) are constructed.
To demonstrate, change the id attribute to _id, give it a different writer and create an id sub to access it:
# This replaces id
has _id => (
is => 'rw',
isa => 'Int',
writer => 'set_id',
required => 0,
);
sub id {
my $self = shift;
return $self->_id();
}
The script will now die with an exception:
Can't locate object method "id" via package "My::Product::one" at ./module.pm line 89.

How to access individual elements of perl Hashed object?

I'm a non-programmer attemting to retrieve useful info from our InfoBlox DHCP boxes. I've installed the Perl API and can make some use of it.
I've got an output from the Data::Dumper "thingie" that appears to have some of the info I want. I'd like to directly reference some of that data but I'm unsure how.
print Dumper(\$object)
Here is part of the Data::Dumper output;
$VAR1 = \bless( {
'network' => '10.183.1.0/24',
'override_lease_scavenge_time' => 'false',
'enable_ifmap_publishing' => 'false',
'low_water_mark_reset' => '10',
'use_lease_time' => 0,
'use_enable_option81' => 0,
'network_container' => '/',
'override_ddns_ttl' => 'false',
'rir' => 'NONE',
'network_view' => bless( {
<snip> --------------------------------------
'extattrs' => {
'Use' => bless( {
'value' => 'Voip'
}, 'Infoblox::Grid::Extattr' )
},
<snip> --------------------------------------
'members' => [
bless( {
'ipv4addr' => '10.85.9.242',
'name' => 'ig3-app3.my.net'
}, 'Infoblox::DHCP::Member' ),
bless( {
'ipv4addr' => '10.85.9.210',
'name' => 'ig3-app1.my.net'
}, 'Infoblox::DHCP::Member' ),
bless( {
'ipv4addr' => '10.85.9.226',
'name' => 'ig3-app2.my.net'
}, 'Infoblox::DHCP::Member' )
],
'override_ignore_client_identifier' => 'false',
'email_list' => undef,
'rir_registration_status' => '??
}, 'Infoblox::DHCP::Network' );
How do I view the elements? ie ...
print $object{members->name};
print $object{members->ipv4addr};
print $object{extattrs->Use->value};
I've found the API dox insufficiant for my skill level:) The data I'd like to pull remains just out of reach.
my #retrieved_objs = $session->search (
object => "Infoblox::DHCP::Network",
network => '.*\.*\.*\..*',
);
foreach $object ( #retrieved_objs ) {
my $network = $object->network;
my $comment = $object->comment;
my $extattrs = $object->extattrs;
my $options = $object->options;
print $network, " network ", $comment, " ", $extattrs, " ", $options, "\n";
}
-------- output ---
10.183.2.0/24 network HASH(0x6a2f038) ARRAY(0x1d20eb0)
10.192.1.0/24 network HASH(0x9df6540) ARRAY(0x9df5468)
10.192.2.0/24 network HASH(0xa088fc8) ARRAY(0xa089718)
You shouldn't try to access the internal values of an object directly. The module - in this case Infoblox::DHCP::Network will provide methods that allow you to read or manipulate the values properly.

Understanding name spaces in POE-Tk

I posted "How to undersand the POE-Tk use of destroy?" in an attempt to reduce the bug in my production code to a test case. But it seems that the solution to the test case is not working in the full program.
The program is 800+ lines long so I am hesitant to post it in full. I realize that the snippets I provide here may be too short to be of any use, but I hope to get some direction in either where to look for a solution or what additional information I can provide.
Here is the Session::Create section of my POE-Tk app.
POE::Session->create(
inline_states => {
_start => \&ui_start,
get_zone => \&get_zone,
ping => \&ping,
mk_disable => \&mk_disable,
mk_active => \&mk_active,
pop_up_add => \&pop_up_add,
add_button_press => sub {
my ($kernel, $session, $heap) = #_[KERNEL, SESSION, HEAP];
print "\nadd button pressed\n\n";
&validate;
},
ih_button_1_press => sub {
my ($kernel, $session, $heap) = #_[KERNEL, SESSION, HEAP];
print "\nih_button_1 pressed\n\n";
if( Tk::Exists($heap->{ih_mw}) ) {
print "\n\nih_mw exists in ih_button_1_press\n\n";
} else {
print "\n\nih_mw does not exist in ih_button_1_press\n\n";
}
1;
$heap->{ih_mw}->destroy if Tk::Exists($heap->{ih_mw});
&auth;
},
pop_up_del => \&pop_up_del,
auth => \&auth,
# validate => \&validate,
auth_routine => \&auth_routine,
raise_widget => \&raise_widget,
del_action => \&del_action,
over => sub { exit; }
}
);
add_button_press is called here;
sub pop_up_add {
...
my $add_but_2 = $add_frm_2->Button(
-text => "Add Record",
-command => $session->postback("add_button_press"),
-font => "{Arial} 12 {bold}") -> pack(
-anchor => 'c',
-pady => 6,
);
...
}
validate creates the Toplevel widget $heap->{ih_mw};
sub validate {
...
if( ! $valid ) {
print "\n! valid entered\n\n";
$heap->{label_text} .= "Add record anyway?";
my $lt_ref = \$heap->{label_text};
...
my $heap->{ih_mw} = $heap->{add_mw}->Toplevel( -title => "ih_mw");
...
if( Tk::Exists($heap->{ih_mw}) ) {
print "\n\nih_mw exists in validate\n\n";
} else {
print "\n\nih_mw does not exist in validate\n\n";
}
...
my $ih_but1 = $heap->{ih_mw}->Button( -text => "Add",
-font => 'vfont',
-command => $session->postback("ih_button_1_press"),
)->pack( -pady => 5 );
...
}
Pressing $ih_but1 results in this;
C:\scripts\alias\resource>alias_poe_V-3_0_par.pl
add button pressed
sub validate called
! valid entered
ih_mw exists in validate
ih_button_1 pressed
ih_mw does not exist in ih_button_1_press
So the $heap->{ih_mw} widget seems to be unkown to the ih_button_1_press anonymous subroutine even with the inclusion of "($kernel, $session, $heap) = #_[KERNEL, SESSION, HEAP];"
Where does $heap in &validate come from? You don't pass it as a parameter. Could $heap in &validate and $heap in &in_button_1_press not be the same thing? Have you tried printing the stringy form of $heap to see if the addresses are the same in the two functions?