File Coverage

blib/lib/W3C/SOAP/WSDL/Parser.pm
Criterion Covered Total %
statement 25 27 92.5
branch n/a
condition n/a
subroutine 9 9 100.0
pod n/a
total 34 36 94.4


line stmt bran cond sub pod time code
1             package W3C::SOAP::WSDL::Parser;
2              
3             # Created on: 2012-05-27 18:58:29
4             # Create by: Ivan Wills
5             # $Id$
6             # $Revision$, $HeadURL$, $Date$
7             # $Revision$, $Source$, $Date$
8              
9 5     5   338006 use Moose;
  5         1525699  
  5         35  
10 5     5   28723 use warnings;
  5         9  
  5         127  
11 5     5   2413 use version;
  5         6081  
  5         23  
12 5     5   274 use Carp;
  5         8  
  5         327  
13 5     5   19 use Data::Dumper qw/Dumper/;
  5         7  
  5         196  
14 5     5   1873 use English qw/ -no_match_vars /;
  5         6016  
  5         25  
15 5     5   1568 use Path::Class;
  5         10  
  5         250  
16 5     5   1684 use W3C::SOAP::Utils qw/ns2module/;
  5         12  
  5         29  
17 5     5   3964 use W3C::SOAP::XSD::Parser;
  0            
  0            
18             use W3C::SOAP::WSDL::Document;
19             use W3C::SOAP::WSDL::Meta::Method;
20             use File::ShareDir qw/dist_dir/;
21              
22             Moose::Exporter->setup_import_methods(
23             as_is => ['load_wsdl'],
24             );
25              
26             extends 'W3C::SOAP::Parser';
27              
28             our $VERSION = version->new('0.11');
29              
30             has '+document' => (
31             isa => 'W3C::SOAP::WSDL::Document',
32             required => 1,
33             handles => {
34             module => 'module',
35             has_module => 'has_module',
36             ns_module_map => 'ns_module_map',
37             module_base => 'module_base',
38             has_module_base => 'has_module_base',
39             },
40             );
41             has location => (
42             is => 'rw',
43             isa => 'Str',
44             );
45             has xsd_parser => (
46             is => 'rw',
47             isa => 'W3C::SOAP::XSD::Parser',
48             builder => '_xsd_parser',
49             lazy => 1,
50             );
51              
52             sub write_modules {
53             my ($self) = @_;
54             confess "No lib directory setup" if !$self->has_lib;
55             confess "No module name setup" if !$self->has_module;
56             confess "No template object set" if !$self->has_template;
57              
58             my $wsdl = $self->document;
59             my $template = $self->template;
60             my $file = $self->lib . '/' . $self->module . '.pm';
61             $file =~ s{::}{/}g;
62             $file = file $file;
63             my $parent = $file->parent;
64             my @missing;
65             while ( !-d $parent ) {
66             push @missing, $parent;
67             $parent = $parent->parent;
68             }
69             mkdir $_ for reverse @missing;
70             my $xsd_parser = $self->get_xsd;
71             my @modules = $xsd_parser->write_modules;
72              
73             confess "No XSD modules found!\n" unless @modules;
74              
75             my $data = {
76             wsdl => $wsdl,
77             module => $self->module,
78             xsd => shift @modules,
79             modules => \@modules,
80             location => $self->location,
81             w3c_version => $VERSION,
82             };
83             $template->process('wsdl/pm.tt', $data, "$file");
84             confess "Error in creating $file (xsd.pm): ". $template->error."\n"
85             if $template->error;
86              
87             return ( $file, $xsd_parser->written_modules );
88             }
89              
90             sub _xsd_parser {
91             my ($self) = @_;
92              
93             my @args;
94             push @args, ( template => $self->template ) if $self->has_template;
95             push @args, ( lib => $self->lib ) if $self->has_lib ;
96             if ( $self->has_module_base ) {
97             my $base = $self->module_base;
98             $base =~ s/WSDL/XSD/;
99             $base .= '::XSD' if $base !~ /XSD/;
100             push @args, ( module_base => $base );
101             }
102              
103             my $parse = W3C::SOAP::XSD::Parser->new(
104             document => [],
105             ns_module_map => $self->ns_module_map,
106             @args,
107             );
108              
109             return $parse;
110             }
111              
112             sub get_xsd {
113             my ($self) = @_;
114             my $parse = $self->xsd_parser;
115              
116             for my $xsd (@{ $self->document->schemas }) {
117             $xsd->ns_module_map($self->ns_module_map);
118             $xsd->clear_xpc;
119              
120             push @{ $parse->document }, $xsd;
121              
122             $parse->document->[-1]->target_namespace($self->document->target_namespace)
123             if !$parse->document->[-1]->has_target_namespace;
124             }
125              
126             return $parse;
127             }
128              
129             my %cache;
130             sub load_wsdl {
131             my ($location) = @_;
132              
133             return $cache{$location} if $cache{$location};
134              
135             my $parser = __PACKAGE__->new(
136             location => $location,
137             ns_module_map => {},
138             module_base => 'Dynamic::WSDL',
139             );
140              
141             my $class = $parser->dynamic_classes;
142              
143             return $cache{$location} = $class->new;
144             }
145              
146             sub dynamic_classes {
147             my ($self) = @_;
148             my @classes = $self->get_xsd->dynamic_classes;
149              
150             $self->module_base('Dynamic::WSDL') if !$self->has_module_base;
151             my $class_name = $self->module_base . '::' . ns2module($self->document->target_namespace);
152              
153             my $wsdl = $self->document;
154             my %method;
155             for my $service (@{ $wsdl->services }) {
156             for my $port (@{ $service->ports }) {
157             for my $operation (@{ $port->binding->operations }) {
158             my $in_element = eval { $operation->port_type->inputs->[0]->message->element };
159             my $in_header_element = eval { $operation->port_type->inputs->[0]->header->element };
160             my $out_element = eval { $operation->port_type->outputs->[0]->message->element };
161             my $out_header_element = eval { $operation->port_type->outputs->[0]->header->element };
162             my @faults = eval {
163             map {{
164             class => $_->message->element->module,
165             name => $_->message->element->perl_name,
166             }}
167             @{ $operation->port_type->faults }
168             };
169              
170             $method{ $operation->perl_name } = W3C::SOAP::WSDL::Meta::Method->wrap(
171             body => sub { shift->_request($operation->perl_name => @_) },
172             package_name => $class_name,
173             name => $operation->perl_name,
174             wsdl_operation => $operation->name,
175             $in_element ? ( in_class => $in_element->module ) : (),
176             $in_element ? ( in_attribute => $in_element->perl_name ) : (),
177             $in_header_element ? ( in_header_class => $in_header_element->module ) : (),
178             $in_header_element ? ( in_header_attribute => $in_header_element->perl_name ) : (),
179             $out_element ? ( out_class => $out_element->module ) : (),
180             $out_element ? ( out_attribute => $out_element->perl_name ) : (),
181             $out_header_element ? ( out_header_class => $out_header_element->module ) : (),
182             $out_header_element ? ( out_header_attribute => $out_header_element->perl_name ) : (),
183             @faults ? ( faults => \@faults ) : (),
184             );
185              
186             if ( $ENV{W3C_SOAP_NAME_STYLE} eq 'both' && $operation->name ne $operation->perl_name ) {
187             my $name = $operation->perl_name;
188             $method{ $operation->name } = Moose::Meta::Method->wrap(
189             body => sub { shift->$name(@_) },
190             package_name => $class_name,
191             name => $operation->name,
192             );
193             }
194             }
195             }
196             }
197              
198             my $class = Moose::Meta::Class->create(
199             $class_name,
200             superclasses => [ 'W3C::SOAP::WSDL' ],
201             methods => \%method,
202             );
203              
204             $class->add_attribute(
205             '+location',
206             default => $wsdl->services->[0]->ports->[0]->address,
207             required => 1,
208             );
209              
210             return $class_name;
211             }
212              
213             1;
214              
215             __END__
216              
217             =head1 NAME
218              
219             W3C::SOAP::WSDL::Parser - Parses WSDL documents to generate Perl client
220             libraries to access the Web Service defined.
221              
222             =head1 VERSION
223              
224             This documentation refers to W3C::SOAP::WSDL::Parser version 0.11.
225              
226             =head1 SYNOPSIS
227              
228             use W3C::SOAP::WSDL::Parser qw/load_wsdl/;
229              
230             # quick/simple usage
231             # create a SOAP client
232             $url = 'http://example.com/soap.wsdl';
233             my $client = load_wsdl($url);
234             my $result = $client->some_action(...);
235              
236             # Create a new object
237             my $wsdl = W3C::SOAP::WSDL::Parser->new(
238             location => $url,
239             module => 'MyApp::WSDL',
240             lib => './lib',
241             template => Template->new(...),
242             ns_module_map => {
243             'http://example.com/xsd/namespace' => 'MyAPP::XSD::Example',
244             'some.other.namespace' => 'MyApp::XSD::SomeOther',
245             },
246             );
247              
248             # Write the generated WSDL module to disk
249             $wsdl->write_modules();
250             # may generate the files
251             # lib/MyApp/WSDL.pm
252             # lib/MyApp/XSD/Example.pm
253             # lib/MyApp/XSD/SomeOther.pm
254              
255             =head1 DESCRIPTION
256              
257             This module parses a WSDL file so that it can produce a client to talk to the
258             SOAP service.
259              
260             There are two ways of using this file:
261              
262             =over 4
263              
264             =item 1
265              
266             Dynamic : C<load_wsdl(...)> or C<<W3C::SOAP::WSDL->new()->dynamic_classes>>
267              
268             These return an in memory generated WSDL client which you can use to talk
269             to the specified web service.
270              
271             =item 2
272              
273             Static : C<<W3C::SOAP::WSDL->new()->write_modules()>> or use L<wsdl-parser>
274             command line script.
275              
276             This writes perl modules to disk so that you can C<use> the modules in your
277             later. This has the advantage that you don't have to recompile the WSDL
278             every time you run your code but it has the disadvantage that your client
279             may be out of date compared to the web service's WSDL.
280              
281             =back
282              
283             Both interfaces are identical once you have the client object. If you want
284             to change at a later point the code change should be adding or removing a
285             use statement and switching from a C<<Module->new>> to C<load_wsdl()>.
286              
287             =head1 SUBROUTINES/METHODS
288              
289             =head2 EXPORTED SUBROUTINES
290              
291             =over 4
292              
293             =item C<load_wsdl ($location)>
294              
295             Helper method that takes the supplied location and creates the dynamic WSDL
296             client object.
297              
298             =back
299              
300             =head2 CLASS METHODS
301              
302             =over 4
303              
304             =item C<new (%args)>
305              
306             Create the new object C<new> accepts the following arguments:
307              
308             =over 4
309              
310             =item location
311              
312             This is the location of the WSDL file, it may be a local file or a URL, it
313             is used to create the C<document> attribute if not supplied.
314              
315             =item document
316              
317             A L<W3C::SOAP::Document> object representing the WSDL file.
318              
319             =item module
320              
321             This is the name of the module to be generated, it is required when writing
322             the SOAP client to disk, the dynamic client generator creates a semi random
323             namespace.
324              
325             =item lib
326              
327             The library directory where modules should be stored. only required when
328             calling C<write_modules>
329              
330             =item template
331              
332             The Template Toolkit object used for the generation of on static modules
333             when using the L</write_modules> method.
334              
335             =item ns_module_map
336              
337             The mapping of XSD namespaces to perl Modules.
338              
339             =back
340              
341             =back
342              
343             =head2 OBJECT METHODS
344              
345             =over 4
346              
347             =item C<<$wsdl->write_modules ()>>
348              
349             Writes out a module that is a SOAP Client to interface with the contained
350             WSDL document, also writes any referenced XSDs.
351              
352             =item C<<$wsdl->dynamic_classes ()>>
353              
354             Creates a dynamic SOAP client object to talk to the WSDL this object was
355             created for
356              
357             =item C<<$wsdl->get_xsd ()>>
358              
359             Creates the L<W3C::SOAP::XSD::Parser> object that represents the XSDs that
360             are used by the specified WSDL file.
361              
362             =back
363              
364             =head1 DIAGNOSTICS
365              
366             =head1 CONFIGURATION AND ENVIRONMENT
367              
368             =head1 DEPENDENCIES
369              
370             =head1 INCOMPATIBILITIES
371              
372             =head1 BUGS AND LIMITATIONS
373              
374             There are no known bugs in this module.
375              
376             Please report problems to Ivan Wills (ivan.wills@gmail.com).
377              
378             Patches are welcome.
379              
380             =head1 AUTHOR
381              
382             Ivan Wills - (ivan.wills@gmail.com)
383              
384             =head1 LICENSE AND COPYRIGHT
385              
386             Copyright (c) 2012 Ivan Wills (14 Mullion Close, Hornsby Heights, NSW Australia 2077).
387             All rights reserved.
388              
389             This module is free software; you can redistribute it and/or modify it under
390             the same terms as Perl itself. See L<perlartistic>. This program is
391             distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
392             without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
393             PARTICULAR PURPOSE.
394              
395             =cut