Hullo all, Part of a project I've been working on for a while involves providing access to a whole bunch of data lookup webservices. Part of the specification for this project is that these services should be made available through SOAP and have WS-I compliant WSDL documents.
It turns out that since most of the database lookups were essentially returning sets of rows for a given column (or a single row for a given primary key) so that's a lot of boilerplate perl and WSDL to manage. In an attempt to keep myself sane during development/maintenance I resolved to generate all the boilerplate code (WSDL, controller, live tests, etc) from as little configuration as possible. Given that some of the services are simply primary key lookups, the minimal amount of information could be as little as the database connection and the desired endpoint ( http://my.server.com/api/soap/dataservices). To say that this is a little rough round the edges is an understatement, but I've now got it to a point where it works for me and figured it might save someone else some time too. I've copied and pasted the usage from this project below as a first pass to get comments. Caveats: - I've called this DBIC::API::SOAP because that seems to be where it fits into the grand scheme of things, however it doesn't current use any of DBIC::API at all (it probably should in the long term, but currently uses it's own abstract interface - DBIx::Class::WebService) - This uses C::C::SOAP::DocumentLiteralWrapped (sorry ruoso!) because DocLitWrapped happened to make it easier for me to generate and test WS-I compliant WSDLs required for work. The intention would be to move this across to a better schema when I've got time. - This currently generates a bunch of scaffolding rather than making much attempt to refactor stuff into runtime (all the clever stuff is done by C::C::SOAP). This is partly because I'm lazy and was running this on top of C::C::SOAP where the WSDL is required as a flat file. However, this is also partly because you wouldn't want to change your external WSDL at runtime anyway so I didn't think this was too much of a bad thing. Let me know your thoughts. Cheers, Ian USAGE The catalyst project "TestApp" in the test directory of this distribution provides a good usage as it was created from scratch using the Catalyst::Helper provided in this distribution. As always the Catalyst project starts with: % catalyst.pl TestApp At the absolute minimum, the user needs to have a database that contains tables with primary keys. If no other config is provided, the helper tool will automatically define one operation per table corresponding to a simple primary key lookup. First, we create a catalyst DBIC model that will help us interact with our database: % cd TestApp % cp /path/to/my/sqlite/database/testws.db . % script/testapp_create.pl model TestDB DBIC::Schema TestApp::Schema \ create=dynamic dbi:SQLite:testws.db exists "/path/to/TestApp/script/../lib/TestApp/Model" exists "/path/to/TestApp/script/../t" exists "/path/to/TestApp/script/../lib/TestApp" created "/path/to/TestApp/script/../lib/TestApp/Schema.pm" created "/path/to/TestApp/script/../lib/TestApp/Model/TestDB.pm" created "/path/to/TestApp/script/../t/model_TestDB.t" Now, we can feed this model name into the helper tool to generate SOAP-based webservice from them: % script/testapp_create.pl controller API::SOAP::DataServices \ DBIC::SOAP::DocumentLiteralWrapped TestDB # starts up the Catalyst project to resolve the model name into DBIC Schema created "root/static/wsdl" created "root/static/wsdl/testapp_controller_api_soap_dataservices.wsdl" created "lib/TestApp/Controller/API/SOAP/DataServices.pm" created "t/controller_API-SOAP-DataServices.t" The Path actions that this provides: /api/soap/dataservices/wsdl # returns WSDL as text/xml /api/soap/dataservices # SOAP endpoint /api/soap/dataservices/ArtistByArtistId # SOAP action handler /api/soap/dataservices/CdByCdId # SOAP action handler As a side note, you can also use your own configuration file rather than relying on the default operations (see DBIx::Class::WebService for more details) # my-dataservices.json { "name": "DataServices", "operations": [ { "name": "SearchArtistsByName", "data_source_name": "Artist", "input_columns": "name", "search_attributes": { "rows": "50" }, "documentation": "Returns a list of artists matching a simple text search" } ] } You should be able to run the test server and point your browser at: % script/testapp_server.pl http://localhost:3000/api/soap/dataservices/wsdl To view the automatically generated WSDL You can also run the generated tests as usual (output has been truncated for clarity). % prove -l t/01app...............................ok t/02pod...............................skipped: set TEST_POD to enable this t/03podcoverage.......................skipped: set TEST_POD to enable this t/controller_API-SOAP-DataServices....1/10 [debug] Debug messages enabled <snip> [debug] Searching CdByCdId (Cd): cd_id=1 [debug] Outgoing XML: <Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"><Body><x0:CdByCdIdResponse xmlns:x0="http://localhost:3000/api/soap/dataservices "><x0:CdByCdIdRecord><x0: artist_id>1</x0:artist_id><x0:cd_id>1</x0:cd_id><x0:title>Electric Ladyland</x0:title><x0:year>1999</x0:year></x0:CdByCdIdRecord></x0: CdByCdIdResponse></Body></Envelope> [info] Request took 0.027138s (36.849/s) .------------------------------------------------------------+-----------. | Action | Time | +------------------------------------------------------------+-----------+ | /api/soap/dataservices/rpc_endpoint | 0.014138s | | -> /api/soap/dataservices/CdByCdId | 0.012903s | | /api/soap/dataservices/end | 0.001441s | '------------------------------------------------------------+-----------' <snip> [debug] Searching ArtistByArtistId (Artist): artist_id=1 [debug] Outgoing XML: <Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"><Body><x0: ArtistByArtistIdResponse xmlns:x0="http://localhost:3000/api/soap/dataservices"><x0: ArtistByArtistIdRecord><x0:artist_id>1</x0:artist_id><x0:name>Jimi Hendrix</x0:name></x0:ArtistByArtistIdRecord></x0:ArtistByArtistIdResponse></ Body></Envelope> [info] Request took 0.022939s (43.594/s) .------------------------------------------------------------+-----------. | Action | Time | +------------------------------------------------------------+-----------+ | /api/soap/dataservices/rpc_endpoint | 0.012840s | | -> /api/soap/dataservices/ArtistByArtistId | 0.011673s | | /api/soap/dataservices/end | 0.001229s | '------------------------------------------------------------+-----------' t/controller_API-SOAP-DataServices....ok t/model_TestDB........................ok All tests successful. Files=5, Tests=13, 7 wallclock secs ( 0.07 usr 0.02 sys + 6.32 cusr 0.61 csys = 7.02 CPU) Result: PASS -- Ian Sillitoe CATH Team -- http://cathdb.info -- Ian Sillitoe CATH Team -- http://cathdb.info
_______________________________________________ List: [email protected] Listinfo: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst Searchable archive: http://www.mail-archive.com/[email protected]/ Dev site: http://dev.catalyst.perl.org/
