File Coverage

blib/lib/WebService/Autotask.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package WebService::Autotask;
2 1     1   48616 use strict;
  1         2  
  1         27  
3 1     1   4 use warnings;
  1         1  
  1         19  
4              
5 1     1   553 use SOAP::Lite;
  0            
  0            
6             use XML::LibXML;
7             use Scalar::Util qw(blessed);
8             use MIME::Base64;
9             use Encode;
10              
11             use vars qw($VERSION);
12             $VERSION = '1.1';
13              
14             my @VALID_OPS = qw(
15             Equals NotEqual GreaterThan LessThan GreaterThanOrEquals LessThanOrEquals
16             BeginsWith EndsWith Contains IsNull IsNotNull IsThisDay Like NotLike
17             SoundsLike
18             );
19              
20             my @VALID_CONDITIONS = qw(AND OR);
21              
22             =head1 NAME
23              
24             WebService::Autotask - Interface to the Autotask webservices API.
25              
26             =head1 SYNOPSIS
27              
28             my $at = WebService::Autotask->new({
29             username => 'user@autotask.account.com',
30             password => 'some_password'
31             });
32              
33             my $list = $at->query({
34             entity => 'Account',
35             query => [
36             {
37             name => 'AccountName',
38             expressions => [{op => 'BeginsWith', value => 'b'}]
39             },
40             ]
41             });
42              
43             $list->[0]->{AccountName} = 'New Account Name';
44              
45             $at->update(@$list);
46              
47             $list = $at->create(
48             bless({
49             AccountName => "Testing Account Name",
50             Phone => "800-555-1234",
51             AccountType => 1,
52             OwnerResourceID => 123456,
53             }, 'Account')
54             );
55              
56             =head1 DESCRIPTION
57              
58             "WebService::Autotask" is a module that provides an interface to the Autotask webservices
59             API. Using this method and your Autotask login credentials you can access and
60             manage your Autotask items using this interface. You should read the Autotask
61             API documentation prior to using this module.
62              
63             Note: all input is assumed to be UTF-8.
64              
65             =head1 CONSTRUCTOR
66              
67             =head2 new
68              
69             Create a new WebService::Autotask SOAP interface object. This takes a hash
70             references with the following arguments:
71              
72             =over 4
73              
74             =item B
75              
76             The username to use when logging into the Autotask system
77              
78             =item B
79              
80             The password to use when logging into the Autotask system.
81              
82             =item B
83              
84             If you know which proxy server you are to use then you may supply it here. By
85             default one of the proxies is used and then the correct proxy is determined
86             after logging in. If the default proxy is not correct the correct proxy will
87             then be logged into automatically. This option should not be required.
88              
89             =back
90              
91             =cut
92              
93             sub new {
94             my ($package, $args, $proxies) = @_;
95              
96             # Set a default proxy to use.
97             $args->{proxy} = 'https://webservices.autotask.net/atservices/1.5/atws.asmx' if (!exists($args->{proxy}));
98             die "No username provided in call to new()" if (!$args->{username});
99             die "No password provided in call to new()" if (!$args->{password});
100              
101             # Use the URI module to get the domain name.
102             my $site = $args->{proxy};
103             $site =~ s/^https?:\/\///;
104             $site =~ s/\/.*$//;
105              
106             # Create an empty hashref for the proxies arg if we don't already have one
107             # defined.
108             $proxies = {};
109              
110             my $soap = SOAP::Lite
111             ->uri('http://autotask.net/ATWS/v1_5/')
112             ->on_action(sub { return join('', @_)})
113             ->proxy($args->{proxy},
114             credentials => [
115             "$site:443", $site, $args->{username} => $args->{password}
116             ]);
117             my $self = {
118             at_soap => $soap,
119             valid_entities => {},
120             picklist_values => {},
121             error => ''
122             };
123              
124             # Check that we are using the right proxy.
125             my $res = $soap->getZoneInfo(SOAP::Data->value($args->{username})->name('UserName'))->result;
126             if ($res->{Error} || $res->{ErrorCode}) {
127             die "Could not find correct Autotask Proxy for user: " . $args->{username} . "\n";
128             }
129             # See if our proxy matches the one provided.
130             if ($args->{proxy} ne $res->{URL}) {
131             if (exists($proxies->{$res->{URL}})) {
132             die "Infinite recursion detected. We have already tried the proxy " . $res->{URL} . " but have been directed to it again";
133             }
134             $proxies->{$args->{proxy}} = 1;
135             $args->{proxy} = $res->{URL};
136             return $package->new($args, $proxies);
137             }
138              
139             # Get a list of all the entity types available.
140             $res = $soap->GetEntityInfo->result;
141             if (!exists($res->{EntityInfo}) || ref($res->{EntityInfo}) ne 'ARRAY') {
142             die "Unable to get a list of valid Entities from the Autotask server";
143             }
144             foreach my $ent (@{$res->{EntityInfo}}) {
145             $self->{valid_entities}->{$ent->{Name}} = $ent;
146             }
147              
148             return bless($self, $package);
149             }
150              
151             =head1 METHODS
152              
153             =head2 query(%args)
154              
155             Generic query method to query the Autotask system for entity data. This takes
156             a hash ref as its argument. If an error occurs while trying to parse the given
157             arguments or creating the associated QueryXML query this method will die with
158             an appropriate error. Returns either the single matching entry as a hash
159             reference, or an array of hash references when more than one result is returned.
160             The following keys are allowed:
161              
162             =over 4
163              
164             =item B
165              
166             The name of the entity you want to query for.
167              
168             =item B
169              
170             An array reference of fields and conditions that are used to construct the
171             query XML. See below for the definition of a field and a condition.
172              
173             =over 4
174              
175             =item B
176              
177             A field is a hash reference that contains the following entries
178              
179             =over 4
180              
181             =item B
182              
183             The name of the field to be querying.
184              
185             =item B
186              
187             Boolean value to indicate if this field is a user defined field or not. Only
188             one UDF field is allowed per query, by default if ommited this is set to
189             false.
190              
191             =item B
192              
193             An array of hash references for the expressions to apply to this field. The
194             keys for this hash refernce are:
195              
196             =over 4
197              
198             =item B
199              
200             The operator to use. One of: Equals, NotEqual, GreaterThan, LessThan,
201             GreaterThanOrEquals, LessThanOrEquals, BeginsWith, EndsWith, Contains, IsNull,
202             IsNotNull, IsThisDay, Like, NotLike or SoundsLike. If not in this list an
203             error will be issued.
204              
205             =item B
206              
207             The appropriate value to go with the given operator.
208              
209             =back
210              
211             =back
212              
213             =item B
214              
215             A condition block that allows you define a more complex query. Each condition
216             element is a hash reference with the following fields:
217              
218             =over 4
219              
220             =item B
221              
222             The condition operator to be used. If no operator value is given AND is assumed.
223             Valid operators are: AND and OR.
224              
225             =item B
226              
227             Each condition contains a list of field and/or expression elements. These have
228             already been defined above.
229              
230             =back
231              
232             =back
233              
234             An example of a valid query woudl be:
235              
236             query => [
237             {
238             name => 'AccountName',
239             expressions => [{op => 'Equals', value => 'New Account'}]
240             },
241             {
242             operator => 'OR',
243             elements => [
244             {
245             name => 'FirstName',
246             expressions => [
247             {op => 'BeginsWith', value => 'A'},
248             {op => 'EndsWith', value => 'S'}
249             ]
250             },
251             {
252             name => 'LastName',
253             expressions => [
254             {op => 'BeginsWith', value => 'A'},
255             {op => 'EndsWith', value => 'S'}
256             ]
257             }
258             ]
259             }
260             ]
261              
262             This will find all accounts with the AccountName of New Account that also
263             have either a FirstName or a LastName that begins with an A and ends with an
264             S.
265              
266             =back
267              
268             =cut
269              
270             sub query {
271             my ($self, $args) = @_;
272              
273             # Validate that we have the right arguments.
274             $self->_validate_entity_argument($args->{entity}, 'query');
275             die "Missing query argument in call to query"
276             if (!exists($args->{query}) || !$args->{query});
277              
278             # Get the entity information if we don't already have it.
279             $self->_load_entity_field_info($args->{entity});
280              
281             # We need to generate the QueryXML from the Query argument.
282             my $query = $self->_create_query_xml($args->{entity}, $args->{query});
283            
284             my $soap = $self->{at_soap};
285             my $res = $soap->query(SOAP::Data->value($query)->name('sXML'))->result;
286              
287             if ($res->{Errors} || $res->{ReturnCode} ne '1') {
288             # There were errors. Grab the errors and set $@ to their textual values.
289             $self->_set_error($res->{Errors}->{ATWSError});
290             return undef;
291             }
292              
293             return _get_entity_results($res);
294             }
295              
296             sub _create_query_xml {
297             my ($self, $entity, $query) = @_;
298              
299             my $doc = XML::LibXML::Document->new();
300             my $xml = $doc->createElement('queryxml');
301             $doc->setDocumentElement($xml);
302              
303             my $elem = $doc->createElement('entity');
304             $elem->appendChild($doc->createTextNode($entity));
305             $xml->appendChild($elem);
306             my $q_elem = $doc->createElement('query');
307              
308             # Figure out the query values.
309             foreach my $item (@$query) {
310             # Is this a field or a condition?
311             if (exists($item->{name})) {
312             # We have a field.
313             $q_elem->appendChild($self->_parse_field($entity, $doc, $item));
314             }
315             elsif (exists($item->{elements})) {
316             # We have a condition.
317             $q_elem->appendChild($self->_parse_condition($entity, $doc, $item));
318             }
319             else {
320             # We have an invalid element.
321             die "Found an invalid element in query element";
322             }
323             }
324              
325             $xml->appendChild($q_elem);
326             return $xml->toString();
327             }
328              
329             sub _parse_condition {
330             my ($self, $entity, $doc, $condition) = @_;
331              
332             my $c_elem = $doc->createElement('condition');
333             if ($condition->{operator}) {
334             die $condition->{operator} . " is not a valid operator for a condition"
335             if (!grep {$_ eq $condition->{operator}} @VALID_CONDITIONS);
336             $c_elem->setAttribute('operator', $condition->{operator});
337             }
338              
339             # Now add each element found in the condition.
340             foreach my $item (@{$condition->{elements}}) {
341             # Is this a field or a condition?
342             if (exists($item->{name})) {
343             # We have a field.
344             $c_elem->appendChild($self->_parse_field($entity, $doc, $item));
345             }
346             elsif (exists($item->{elements})) {
347             # We have a condition.
348             $c_elem->appendChild($self->_parse_condition($entity, $doc, $item));
349             }
350             else {
351             # We have an invalid element.
352             die "Found an invalid element in query element";
353             }
354             }
355              
356             return $c_elem;
357             }
358              
359             sub _parse_field {
360             my ($self, $entity, $doc, $field) = @_;
361              
362             # Check to see that this entity actually has a field with this name.
363             die "Invalid query field " . $field->{name} . " for entity $entity"
364             if (!$self->{valid_entities}->{$entity}->{fields}->{$field->{name}}->{IsQueryable});
365             my $f_elem = $doc->createElement('field');
366             if ($self->{valid_entities}->{$entity}->{fields}->{$field->{name}}->{IsUDF}) {
367             $f_elem->setAttribute('udf', 'true');
368             }
369             $f_elem->appendChild($doc->createTextNode($field->{name}));
370              
371             # Go through the expressions.
372             foreach my $item (@{$field->{expressions}}) {
373             die "Invalid op " . $item->{op} . " in expression"
374             if (!grep {$_ eq $item->{op}} @VALID_OPS);
375             my $exp = $doc->createElement('expression');
376             $exp->setAttribute('op', $item->{op});
377             $exp->appendChild($doc->createTextNode($item->{value}));
378             $f_elem->appendChild($exp);
379             }
380              
381             return $f_elem;
382             }
383              
384             sub _load_entity_field_info {
385             my ($self, $entity) = @_;
386              
387             # If we have already loaded information for thsi entity, don't do it a
388             # second time.
389             if ($self->{valid_entities}->{$entity}->{fields}) {
390             return;
391             }
392              
393             my $soap = $self->{at_soap};
394              
395             # Now load the fields.
396             my $res = $soap->GetFieldInfo(SOAP::Data->name('psObjectType')->value($entity))->result;
397             foreach my $field (@{$res->{Field}}) {
398             $self->{valid_entities}->{$entity}->{fields}->{$field->{Name}} = $field;
399             }
400              
401             # Now load the user derfined fields.
402             $res = $soap->getUDFInfo(SOAP::Data->name('psTable')->value($entity))->result;
403             if ($res && ref($res) eq 'HASH' && exists($res->{Field}) && ref($res->{Field}) eq 'ARRAY') {
404             foreach my $field (@{$res->{Field}}) {
405             $self->{valid_entities}->{$entity}->{fields}->{$field->{Name}} = $field;
406             $self->{valid_entities}->{$entity}->{fields}->{$field->{Name}}->{IsUDF} = 'true';
407             }
408             }
409              
410             return;
411             }
412              
413             sub _get_entity_results {
414             my ($result) = @_;
415            
416             # Make sure we have results to return.
417             if (!exists($result->{EntityResults}) || ref($result->{EntityResults}) ne 'HASH' || !exists($result->{EntityResults}->{Entity})) {
418             return ();
419             }
420              
421             my $ents = $result->{EntityResults}->{Entity};
422              
423             # Return the actual array instead of an array ref if we got one.
424             if (ref($ents) eq 'ARRAY') {
425             return (@$ents);
426             }
427              
428             # Return the item to be assigned as an array.
429             return($ents);
430             }
431              
432             =head2 update(@entities)
433              
434             Update the given entities. Entites will be verified prior to submitted to
435             verify that they can be updated, any fields that are not updatable will
436             be ignored. Each object reference needs to be blessed with the entity type
437             that it is (Account, Contact, etc). Returns the list of entites that were
438             updated successfully. If an error occurs $@ will be set and undef is returned.
439             See the section on Entity format for more details on how to format entities to
440             be accepted by this method.
441              
442             =cut
443              
444             sub update {
445             my ($self, @entities) = @_;
446              
447             die "Missing entity argument in call to query" if (!@entities);
448              
449             # Validate that we have the right arguments.
450             my @list;
451             foreach my $ent (@entities) {
452             $self->_validate_entity_argument($ent, 'update');
453              
454             # Get the entity information if we don't already have it.
455             $self->_load_entity_field_info(blessed($ent));
456              
457             # Verify all fields provided are valid.
458             $self->_validate_fields($ent);
459              
460             push(@list, _entity_as_soap_data($ent));
461             }
462              
463             my $soap = $self->{at_soap};
464             my $res = $soap->update(SOAP::Data->name('Entities')->value(\SOAP::Data->name('array' => @list)))->result;
465              
466             if ($res->{Errors} || $res->{ReturnCode} ne '1') {
467             # There were errors. Grab the errors and set $@ to their textual values.
468             $self->_set_error($res->{Errors}->{ATWSError});
469             return undef;
470             }
471              
472             return _get_entity_results($res);
473             }
474              
475             =head2 create(@entities)
476              
477             Create the given entities. Entites will be verified prior to submitted to
478             verify that they can be created, any fields that are not creatable will
479             be ignored on creation. Each object reference needs to be blessed with the
480             entity type it is (Account, Contact, etc). Returns the list of entites that
481             were created successfully. If an error occurs $@ will be set and undef is
482             returned. See the section on Entity format for more details on how to format
483             entities to be accepted by this method.
484              
485             =cut
486              
487             sub create {
488             my ($self, @entities) = @_;
489              
490             die "Missing entity argument in call to query" if (!@entities);
491              
492             # Validate that we have the right arguments.
493             my @list;
494             foreach my $ent (@entities) {
495             $self->_validate_entity_argument($ent, 'create');
496              
497             # Get the entity information if we don't already have it.
498             $self->_load_entity_field_info(blessed($ent));
499              
500             # Verify all fields provided are valid.
501             $self->_validate_fields($ent);
502              
503             push(@list, _entity_as_soap_data($ent));
504             }
505              
506             my $soap = $self->{at_soap};
507             my $res = $soap->create(SOAP::Data->name('Entities')->value(\SOAP::Data->name('array' => @list)))->result;
508              
509             if ($res->{Errors} || $res->{ReturnCode} ne '1') {
510             # There were errors. Grab the errors and set $@ to their textual values.
511             $self->_set_error($res->{Errors}->{ATWSError});
512             return undef;
513             }
514              
515             return _get_entity_results($res);
516             }
517              
518             =head2 get_picklist_options($entity, $field)
519              
520             Return a hash that contains the ID values and options for a picklist field
521             item. If the field is not a picklist field then an empty hash will be
522             retruned. The hash is formated with the labels as keys and the values as the
523             values.
524              
525             =cut
526              
527             sub get_picklist_options {
528             my ($self, $entity, $field) = @_;
529              
530             # See if we have this item cached.
531             if (!exists($self->{picklist_values}->{$entity})) {
532             $self->{picklist_values}->{$entity} = {};
533             }
534             if (!exists($self->{picklist_values}->{$entity}->{$field})) {
535             # first get the entity information.
536             $self->_load_entity_field_info($entity);
537              
538             # Next find the field inside this entity.
539             my $data = $self->{valid_entities}->{$entity}->{fields}->{$field};
540              
541             if (!exists($data->{PicklistValues}) || ref($data->{PicklistValues}) ne 'HASH' ||
542             !exists($data->{PicklistValues}->{PickListValue}) || ref($data->{PicklistValues}->{PickListValue}) ne 'ARRAY') {
543             return ();
544             }
545              
546             my %pick_list;
547             foreach my $value (@{$data->{PicklistValues}->{PickListValue}}) {
548             $pick_list{$value->{Label}} = $value->{Value};
549             }
550              
551             $self->{picklist_values}->{$entity}->{$field} = \%pick_list;
552             }
553              
554             return %{$self->{picklist_values}->{$entity}->{$field}};
555             }
556              
557             =head2 create_attachment($attach)
558              
559             Create a new attachment. Takes a hashref containing Data (the raw data of
560             the attachment) and Info, which contains the AttachmentInfo. Returns the
561             new attachment ID on success.
562              
563             =cut
564              
565             sub create_attachment {
566             my ($self, $attach) = @_;
567              
568             # Get the entity information if we don't already have it.
569             my $atb = "Attachment";
570             my $ati = $atb . "Info";
571             $self->_load_entity_field_info($ati);
572              
573             # Collect the Info fields
574             my $e_info = $self->{valid_entities}->{$ati};
575             my @inf;
576             foreach my $f_name (keys %{$$attach{Info}}) {
577             die "Field $f_name is not a valid field for $ati"
578             if (!$e_info->{fields}->{$f_name});
579             push @inf, SOAP::Data->name($f_name => $$attach{Info}{$f_name});
580             }
581              
582             my $data = decode("utf8", $$attach{Data});
583             my $res = $self->{at_soap}->CreateAttachment(
584             SOAP::Data->name("attachment" => \SOAP::Data->value(
585             SOAP::Data->name(Info => \SOAP::Data->value(@inf))->attr({'xsi:type' => $ati}),
586             SOAP::Data->name('Data')->value($data)->type('base64Binary'),
587             ))->attr({'xsi:type' => $atb}));
588             return $res->valueof('//CreateAttachmentResponse/CreateAttachmentResult');
589             }
590              
591             =head2 get_attachment($id)
592              
593             Fetch an attachment; the only argument is the attachment ID. Returns a
594             hashref of attachment Data and Info, or undef if the specified attachment
595             doesn't exist or if there is an error.
596              
597             =cut
598              
599             sub get_attachment {
600             my ($self, $id) = @_;
601              
602             # The result is either a hashref with Data and Info or undef
603             my $res = $self->{at_soap}->GetAttachment(SOAP::Data->name('attachmentId')->value($id))->result;
604             if ($res && %$res && $$res{Data}) {
605             # Go ahead and decode it
606             $$res{Data} = decode_base64($$res{Data});
607             }
608             return $res;
609             }
610              
611             =head2 delete_attachment($id)
612              
613             Delete an attachment; the only argument is the attachment ID. Returns true on
614             success or sets the error string on failure.
615              
616             =cut
617              
618             sub delete_attachment {
619             my ($self, $id) = @_;
620              
621             my $res = $self->{at_soap}->DeleteAttachment(SOAP::Data->name('attachmentId')->value($id))->result;
622             if ($res) {
623             $self->_set_error($res);
624             return 0;
625             }
626             return 1;
627             }
628              
629             =head1 ENTITY FORMAT
630              
631             The follow section details how to format a variable that contains entity
632             informaiton. Entites are required for creating and updating items in the
633             Autotask database.
634              
635             An entity is a blessed hash reference. It is bless with the name of the type
636             of entity that it is (Account, Contract, Contact, etc). They keys of the hash
637             are the field names found in the Autotask entity object. The values are the
638             corresponding values to be used.
639              
640             A special key is used for all user defined fields (UserDefinedFields). This
641             entry contains a hash reference containing one key UserDefinedField. This is
642             in turn an array reference containing each user defined field. The user
643             defined field entry looks simliar to this:
644              
645             {
646             UserDefinedField => [
647             {
648             Name => "UserDefinedField1",
649             Value => "Value for Field"
650             },
651             {
652             Name => "SecondUDF",
653             Value => "Value for SecondUDF"
654             }
655             ]
656             }
657              
658             When used together the entire structure looks something simliar to this:
659              
660             bless({
661             FieldName1 => "Value for FieldName1",
662             Field2 => "Value for Field2",
663             UserDefinedFields => {
664             UserDefinedField => [
665             {
666             Name => "UserDefinedField1",
667             Value => "Value for Field"
668             },
669             {
670             Name => "SecondUDF",
671             Value => "Value for SecondUDF"
672             }
673             ]
674             }
675             }, 'EntityName')
676              
677             Obviously the above is just an example. You will need to look at the actual
678             fields that are allowed for each Autotask entity. The user defined fields also
679             will depend on how your instance of Autotask has been configured.
680              
681             =cut
682              
683             sub _entity_as_soap_data {
684             my ($entity) = @_;
685              
686             my @fields = ();
687            
688             foreach my $f_name (sort(keys(%$entity))) {
689             my $field;
690             if ($f_name eq 'UserDefinedFields') {
691             $field = _udf_as_soap_data($entity->{$f_name});
692             } else {
693             # Assume non-ASCII is UTF-8
694             my $data = decode("utf8", $entity->{$f_name});
695             $field = SOAP::Data->name($f_name => $data);
696             # SOAP::Lite will treat as binary if UTF-8
697             $field->type("string") if ($data ne $entity->{$f_name});
698             }
699             push @fields, $field;
700             }
701            
702             return SOAP::Data->name(Entity => \SOAP::Data->value(@fields))->attr({'xsi:type' => ref($entity)});
703             }
704              
705             sub _udf_as_soap_data {
706             my ($udfs) = @_;
707              
708             my @fields = ();
709              
710             foreach my $field (@{$udfs->{UserDefinedField}}) {
711             # Assume non-ASCII is UTF-8
712             my $data = decode("utf8", $field);
713             my $val = SOAP::Data->value($data);
714             # SOAP::Lite will treat as binary if UTF-8
715             $data->type("string") if ($data ne $field);
716             push(@fields, SOAP::Data->name(UserDefinedField => $val));
717             }
718              
719             return SOAP::Data->name(UserDefinedFields => \SOAP::Data->value(@fields));
720             }
721              
722              
723             sub _set_error {
724             my ($self, $errs) = @_;
725              
726             $self->{error} = '';
727              
728             if (ref($errs) eq 'HASH') {
729             $errs = [ $errs ];
730             }
731              
732             if (ref($errs) eq 'ARRAY') {
733             foreach my $err (@$errs) {
734             $self->{error} .= "ATWSError: " . $err->{Message} . "\n";
735             }
736             }
737              
738             if (!$self->{error}) {
739             $self->{error}= "An unspecified error occured. This usually is due to bad SOAP formating based on the data passed into this method";
740             }
741              
742             $self->{error} =~ s/\n$//;
743             }
744              
745             sub _validate_entity_argument {
746             my ($self, $entity, $type) = @_;
747              
748             my $flag;
749             if ($type eq 'query') {
750             $flag = 'CanQuery';
751             }
752             elsif ($type eq 'create') {
753             $flag = 'CanCreate';
754             }
755             elsif ($type eq 'update') {
756             $flag = 'CanUpdate';
757             }
758              
759             my $e_type = blessed($entity);
760             if (!$e_type) {
761             if (ref($entity) eq '') {
762             # Our entity is actually a type string.
763             $e_type = $entity;
764             }
765             else {
766             die "Entity has not been blessed";
767             }
768             }
769              
770             # Are we allowed to query this entity?
771             if (!$e_type) {
772             die "Missing entity argument in call to $type"
773             }
774             elsif ( !grep {$_ eq $e_type} keys(%{$self->{valid_entities}}) ) {
775             die "$e_type is not a valid entity. Valid entities are: " .
776             join(', ', keys(%{$self->{valid_entities}}))
777             }
778             elsif ($self->{valid_entities}->{$e_type}->{$flag} eq 'false') {
779             die "Not allowed to $type $e_type"
780             }
781              
782             return 1;
783             }
784              
785             sub _validate_fields {
786             my ($self, $ent) = @_;
787              
788             my $type = blessed($ent);
789             my $e_info = $self->{valid_entities}->{$type};
790              
791             foreach my $f_name (keys(%$ent)) {
792             if ($f_name eq 'UserDefinedFields') {
793             # Special case field. Look at the actual user defined fields.
794             foreach my $udf (@{$ent->{$f_name}->{UserDefinedField}}) {
795             die "Field " . $udf->{Name} . " is not a valid $type entity user defined field"
796             if (!$e_info->{fields}->{$udf->{Name}});
797             }
798             }
799             else {
800             die "Field $f_name is not a valid field for $type entity"
801             if (!$e_info->{fields}->{$f_name});
802             }
803             }
804              
805             return 1;
806             }
807              
808             =head1 DEPENDENCIES
809              
810             L, L
811              
812             =head1 AUTHOR
813              
814             Derek Wueppelmann (derek@roaringpenguin.com)
815              
816             Attachment, UTF-8 support added by Chris Adams (cmadams@hiwaay.net)
817              
818             =head1 LICENSE AND COPYRIGHT
819              
820             Copyright (c) 2010 Roaring Penguin Software, Inc.
821              
822             Attachment, UTF-8 support Copyright (c) 2013 HiWAAY Information Services, Inc.
823              
824             This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
825              
826             Autotask (tm) is a trademark of Autotask.
827              
828             =cut
829              
830             1;