[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
SF.net SVN: ledger-smb:[4744] trunk/rest-handler.pl
- Subject: SF.net SVN: ledger-smb:[4744] trunk/rest-handler.pl
- From: ..hidden..
- Date: Tue, 22 May 2012 03:55:13 +0000
Revision: 4744
http://ledger-smb.svn.sourceforge.net/ledger-smb/?rev=4744&view=rev
Author: einhverfr
Date: 2012-05-22 03:55:12 +0000 (Tue, 22 May 2012)
Log Message:
-----------
Initial implementation of REST handler
Added Paths:
-----------
trunk/rest-handler.pl
Added: trunk/rest-handler.pl
===================================================================
--- trunk/rest-handler.pl (rev 0)
+++ trunk/rest-handler.pl 2012-05-22 03:55:12 UTC (rev 4744)
@@ -0,0 +1,293 @@
+=head1 NAME
+
+LedgerSMB::Handlers::REST_Handler - REST handler for new code sections
+
+=head1 SYNPOSIS
+
+This is invoked via http, for example, to get a customer:
+
+ GET www.myhost.com/ledgersmb/rest/1.4/my_company/customer/242.xml
+
+To attach a new note, you might post an xml document to:
+
+ POST www.myhost.com/ledgersmb/rest/1.4/my_company/customer/242/note.xml
+
+Similarly to retrieve all notes for this customer:
+
+ GET www.myhost.com/ledgersmb/rest/1.4/my_company/customer/242/note.xml
+
+=head1 DESCRIPTION
+
+=head2 URL Syntax
+
+Everything before the /rest/ part of the URL is ignored. After the /rest/
+the url is given fixed semantic meaning based on the following pattern:
+[company_name]/[object class][/id][/subresource][.format]
+
+=head2 Authentication
+
+Authentication information is provided using HTTP authentication headers.
+Currently only HTTP Basic is supported though Kerberos could be supported with
+a little effort. Please use it over SSL.
+
+=head2 Delegated Format Parsing API
+
+Every supported format handler supports two methods: from_input and to_output.
+The former transforms the format into a hashref, and the latter transforms a
+hashref into a file of this format. It could do so using Template Toolkit or
+some other manner and so arbitrary formats are thus supported.
+
+The format can be determined either by the CONTENT_TYPE header or the
+extension. Currently the system just checks the portion following the slash
+and uses that. Future versions may perform a lookup against the mime_types
+table to look up handlers. Sub-types can be indicated using an underscore, so
+text/xml_mydialect would dispatch to the XML_mydialect handler if available and
+if not to the XML handler. This would be eqivaent to an extension of
+.xml_mydialect.
+
+The reason for the subtyping options is that this allows for one to create, for
+example, an EDI handler and specify subtypes either as subclasses or as
+additional options within the same class.
+
+=head2 Delegated Object Handling API
+
+Every supported object class MUST support the following methods (even if they
+do nothing but throw an error):
+
+=over
+
+=item GET
+
+Retrieves one or more records.
+
+=item PUT
+
+Insert or updates a record.
+
+=item POST
+
+If an ID is provided this should check to see if the record exists, and if so
+throw an error. If it does not, or if it does not exist, then create it.
+
+=item DELETE (optional)
+
+Deletes a resource if this is supported.
+
+=back
+
+If any of the above are not supported, and a request comes in that would hit
+that method, a 403 Forbidden error is returned.
+
+In addition each subresource should get its own method, optionally suffixed
+with _GET, _PUT, _POST, or _DELETE. These are deleaged first to the suffixed
+version, and if that i snot found to the subresource name directly. If that is
+not found, we throw a 404 error.
+
+Each method is pashed a hashref which contains:
+
+=over
+
+=item dbh
+
+Database connection handle to the company db
+
+=item class_name
+
+Top level class.
+
+=item id
+
+ID field for that object
+
+=item subresource
+
+Subresource field name
+
+=item args
+
+Hashref of query string args
+
+=item payload
+
+This is a hashref returned from the format handler.
+
+=back
+
+=head2 Error Handling
+
+Other scripts in this workflow should throw errors using die, and include both
+the http error number and a brief description. Examples might be:
+
+ die '401 Unauthorized';
+
+or
+
+ die "500 Error from function: $DBH->errstr";
+
+=head1 NOTES
+
+This is currently only supported ober CGI. Once the old code is eliminated, we
+plan to port the entire application over to a more flexible framework.
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (C) 2012 LedgerSMB Core Team. This file is licensed under the GNU
+General Public License version 2, or at your option any later version. Please
+see the included License.txt for details.
+
+=cut
+
+package LedgerSMB::REST_Handler;
+
+use CGI::Simple;
+use Try::Tiny;
+use strict;
+use warnings;
+
+return process_request();
+
+# Note: Indenting try/catch only two characters here because it wraps all
+# substantive logic in the function. -CT
+sub process_request{
+
+ try {
+ my $request = get_request_properties();
+
+ my $format = lc($request->{format});
+ if (! eval "require LedgerSMB::REST_Format::" . $format) {
+ eval "require LedgerSMB::REST_Format::" . $format;
+ }
+ my $fmtpackage = "LedgerSMB::REST_Format::" . $format;
+
+ if ($request->{payload}){
+ if ($fmtpackage->can('from_input')){
+ $request->{payload} = $fmtpackage->can('from_input')->($request);
+ } else {
+ die '404 Unsupported Format';
+ }
+ }
+
+ my $classpkg = "LedgerSMB::REST_Class::" . $request->{class};
+ eval "require $classpkg" || return error_handler('404 Resource Not Found');
+
+ my $restobj;
+ if ($classpkg->can('new')){
+ $restobj = $classpkg->can('new')->($request);
+ } else {
+ die '500 Bad Class Definition';
+ }
+
+ if ($request->{subresource}){
+
+ my $suffixed_method = "$request->{subresource}_"
+ . lc($request->{method});
+ if ($restobj->can($suffixed_method)){
+ $restobj->can($suffixed_method)->($request);
+ } else {
+ $restobj->can($request->{subresource})
+ || die '404 Resource Not Found';
+ }
+
+ } else {
+
+ if ($classpkg->can($request->{method})){
+ $classpkg->can($request->{method})->($request);
+ } else {
+ die '401 Method Not Allowed';
+ }
+
+ }
+
+ my $content;
+ my $ctype;
+ if ($request->{payload}){
+ if ($fmtpackage->can('to_output')){
+ $content = $fmtpackage->can('to_output')->($request);
+ } else {
+ return error_handler('404 Unsupported Format');
+ }
+ }
+ if ($fmtpackage->can('mime_type')){
+ $ctype = $fmtpackage->can('mime_type')->();
+ }
+
+ return output({state => '200 Success',
+ content => $content,
+ content_type => $ctype});
+ } catch {
+ return error_handler($_);
+ }
+}
+
+sub error_handler {
+ my ($error) = @_;
+ my $content = $error;
+ $content =~ s/^\d\d\d\s//;
+ $error =~ s/(.*$).*/$1/m;
+ if ($error =~ /^\d\d\d\s/){
+ $error = "500 $error";
+ }
+ output({state => $error, content => $content});
+}
+
+# Isolating request-> hashref logic so that it is easier to port to other
+# environments --CT
+
+sub get_request_properties {
+ my $cgi = CGI::Simple->new();
+ use LedgerSMB::Auth;
+
+ my $creds = LedgerSMB::Auth::get_credentials();
+ my $request = {};
+ my $url = $cgi->self_url();
+
+
+ $request->{args} = $cgi->Vars();
+ $request->{method} = $ENV{REQUEST_METHOD};
+ $request->{payload} = $cgi->param( "$request->{method}DATA" );
+
+ $url =~ s|/rest/(.*)|$1|;
+ $url =~ s|(\.[^/]$)||;
+ $request->{format} = $1;
+
+ my @components = split /\//, $url;
+
+ $request->{dbh} = DBI->connect(
+ "dbi:Pg:dbname=$components[0]",
+ "$creds->{login}", "$creds->{password}",
+ { AutoCommit => 0 }
+ );
+
+ if (!$request->{dbh}) {
+ die '403 Authentication Failed';
+ }
+
+ if (!$request->{format}){
+ my $fmt = $ENV{CONTENT_TYPE};
+ $fmt =~ /([^\/]*$)/;
+ $request->{format} = $1;
+ }
+
+ $request->{class_name} = $components[1];
+ $request->{id} = $components[2];
+ $request->{subresource} = $components[3];
+
+ return $request;
+}
+
+# Isolating output routine
+sub output {
+ my ($args) = $_;
+ my $ctype;
+ my $cgi = CGI::Simple->new();
+
+ if ($args->{content_type}){
+ $ctype = $args->{content_type};
+ } else {
+ $ctype = 'text/text';
+ }
+ $cgi->header($ctype, $args->{state});
+ $cgi->put($args->{content});
+}
+
+1;
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.