|
From: SVN c. m. f. t. SWORD-A. p. <swo...@li...> - 2012-01-11 20:40:21
|
Revision: 439
http://sword-app.svn.sourceforge.net/sword-app/?rev=439&view=rev
Author: richard-jones
Date: 2012-01-11 20:40:14 +0000 (Wed, 11 Jan 2012)
Log Message:
-----------
enforce configurable interface between server implementation and web front end
Modified Paths:
--------------
sss/branches/sss-2/sss/config.py
sss/branches/sss-2/sss/core.py
sss/branches/sss-2/sss/repository.py
sss/branches/sss-2/sss/webpy.py
Modified: sss/branches/sss-2/sss/config.py
===================================================================
--- sss/branches/sss-2/sss/config.py 2012-01-11 20:02:27 UTC (rev 438)
+++ sss/branches/sss-2/sss/config.py 2012-01-11 20:40:14 UTC (rev 439)
@@ -116,8 +116,10 @@
],
"container_format_default" : {
"content_type" : "application/atom+xml;type=entry"
- }
+ },
+ "sword_server" : "sss.repository.SSS",
+ "authenticator" : "sss.repository.SSSAuthenticator"
}
"""
@@ -137,6 +139,12 @@
# it's a bit of a faff to include the code that was there before into
# the json string. How much does this matter?
+ def get_server_implementation(self):
+ return self._get_class(self.sword_server)
+
+ def get_authenticator_implementation(self):
+ return self._get_class(self.authenticator)
+
def get_container_formats(self):
default_params = self._get_accept_params(self.container_format_default)
Modified: sss/branches/sss-2/sss/core.py
===================================================================
--- sss/branches/sss-2/sss/core.py 2012-01-11 20:02:27 UTC (rev 438)
+++ sss/branches/sss-2/sss/core.py 2012-01-11 20:40:14 UTC (rev 439)
@@ -7,6 +7,139 @@
from sss_logging import logging
ssslog = logging.getLogger(__name__)
+class SwordServer(object):
+ """
+ The main SWORD Server class. This class deals with all the CRUD requests as provided by the web.py HTTP
+ handlers
+ """
+ def __init__(self, config, auth):
+ # get the configuration
+ self.configuration = config
+ self.auth_credentials = auth
+
+ def container_exists(self, path):
+ raise NotImplementedError()
+
+ def media_resource_exists(self, path):
+ raise NotImplementedError()
+
+ def service_document(self, path=None):
+ """
+ Construct the Service Document. This takes the set of collections that are in the store, and places them in
+ an Atom Service document as the individual entries
+ """
+ raise NotImplementedError()
+
+ def list_collection(self, path):
+ """
+ List the contents of a collection identified by the supplied id
+ """
+ raise NotImplementedError()
+
+ def deposit_new(self, path, deposit):
+ """
+ Take the supplied deposit and treat it as a new container with content to be created in the specified collection
+ Args:
+ -collection: the ID of the collection to be deposited into
+ -deposit: the DepositRequest object to be processed
+ Returns a DepositResponse object which will contain the Deposit Receipt or a SWORD Error
+ """
+ raise NotImplementedError()
+
+ def get_media_resource(self, path, accept_parameters):
+ """
+ Get a representation of the media resource for the given id as represented by the specified content type
+ -id: The ID of the object in the store
+ -content_type A ContentType object describing the type of the object to be retrieved
+ """
+ raise NotImplementedError()
+
+ def replace(self, path, deposit):
+ """
+ Replace all the content represented by the supplied id with the supplied deposit
+ Args:
+ - oid: the object ID in the store
+ - deposit: a DepositRequest object
+ Return a DepositResponse containing the Deposit Receipt or a SWORD Error
+ """
+ raise NotImplementedError()
+
+ def delete_content(self, path, delete):
+ """
+ Delete all of the content from the object identified by the supplied id. the parameters of the delete
+ request must also be supplied
+ - oid: The ID of the object to delete the contents of
+ - delete: The DeleteRequest object
+ Return a DeleteResponse containing the Deposit Receipt or the SWORD Error
+ """
+ raise NotImplementedError()
+
+ def add_content(self, path, deposit):
+ """
+ Take the supplied deposit and treat it as a new container with content to be created in the specified collection
+ Args:
+ -collection: the ID of the collection to be deposited into
+ -deposit: the DepositRequest object to be processed
+ Returns a DepositResponse object which will contain the Deposit Receipt or a SWORD Error
+ """
+ raise NotImplementedError()
+
+ def get_container(self, path, accept_parameters):
+ """
+ Get a representation of the container in the requested content type
+ Args:
+ -oid: The ID of the object in the store
+ -content_type A ContentType object describing the required format
+ Returns a representation of the container in the appropriate format
+ """
+ raise NotImplementedError()
+
+ def deposit_existing(self, path, deposit):
+ """
+ Deposit the incoming content into an existing object as identified by the supplied identifier
+ Args:
+ -oid: The ID of the object we are depositing into
+ -deposit: The DepositRequest object
+ Returns a DepositResponse containing the Deposit Receipt or a SWORD Error
+ """
+ raise NotImplementedError()
+
+ def delete_container(self, path, delete):
+ """
+ Delete the entire object in the store
+ Args:
+ -oid: The ID of the object in the store
+ -delete: The DeleteRequest object
+ Return a DeleteResponse object with may contain a SWORD Error document or nothing at all
+ """
+ raise NotImplementedError()
+
+ def get_statement(self, path):
+ raise NotImplementedError()
+
+ # NOT PART OF STANDARD, BUT USEFUL
+ # These are used by the webpy interface to provide easy access to certain
+ # resources. Not implementing them is fine. If they are not implemented
+ # then you just have to make sure that your file paths don't rely on the
+ # Part http handler
+
+ def get_part(self, path):
+ """
+ Get a file handle to the part identified by the supplied path
+ - path: The URI part which is the path to the file
+ """
+ raise NotImplementedError()
+
+ def get_edit_uri(self, path):
+ raise NotImplementedError()
+
+class Authenticator(object):
+ def __init__(self, config):
+ self.config = config
+
+ def basic_authenticate(self, username, password, obo):
+ raise NotImplementedError()
+
class EntryDocument(object):
def __init__(self, atom_id=None, alternate_uri=None, content_uri=None, edit_uri=None, se_uri=None, em_uris=[],
Modified: sss/branches/sss-2/sss/repository.py
===================================================================
--- sss/branches/sss-2/sss/repository.py 2012-01-11 20:02:27 UTC (rev 438)
+++ sss/branches/sss-2/sss/repository.py 2012-01-11 20:40:14 UTC (rev 439)
@@ -1,5 +1,5 @@
import os, hashlib, uuid, urllib
-from core import Statement, DepositResponse, MediaResourceResponse, DeleteResponse, SWORDSpec, Auth, AuthException, SwordError, ServiceDocument, SDCollection, EntryDocument
+from core import Statement, DepositResponse, MediaResourceResponse, DeleteResponse, SWORDSpec, Auth, AuthException, SwordError, ServiceDocument, SDCollection, EntryDocument, Authenticator, SwordServer
from spec import Namespaces, Errors
from lxml import etree
from datetime import datetime
@@ -10,9 +10,9 @@
from sss_logging import logging
ssslog = logging.getLogger(__name__)
-class SSSAuthenticator(object):
+class SSSAuthenticator(Authenticator):
def __init__(self, config):
- self.config = config
+ Authenticator.__init__(self, config)
def basic_authenticate(self, username, password, obo):
# we may have turned authentication off for development purposes
@@ -124,18 +124,14 @@
collection, id, fn = path.split("/", 2)
return collection, id, fn
-class SWORDServer(object):
+class SSS(SwordServer):
"""
The main SWORD Server class. This class deals with all the CRUD requests as provided by the web.py HTTP
handlers
"""
def __init__(self, config, auth):
+ SwordServer.__init__(self, config, auth)
- # get the configuration
- self.configuration = config
-
- self.auth_credentials = auth
-
# create a DAO for us to use
self.dao = DAO(self.configuration)
Modified: sss/branches/sss-2/sss/webpy.py
===================================================================
--- sss/branches/sss-2/sss/webpy.py 2012-01-11 20:02:27 UTC (rev 438)
+++ sss/branches/sss-2/sss/webpy.py 2012-01-11 20:40:14 UTC (rev 439)
@@ -8,13 +8,12 @@
from sss_logging import logging
ssslog = logging.getLogger(__name__)
-# create the global configuration
+# create the global configuration and import the implementation classes
from config import Configuration
config = Configuration()
+Authenticator = config.get_authenticator_implementation()
+SwordServer = config.get_server_implementation()
-# FIXME: we need to separate this dependence, probably in configuration
-from repository import SWORDServer, SSSAuthenticator
-
# Whether to run using SSL. This uses a default self-signed certificate. Change the paths to
# use an alternative set of keys
ssl = False
@@ -97,7 +96,7 @@
ssslog.info("Authentication details: " + str(username) + ":" + str(password) + "; On Behalf Of: " + str(obo))
- authenticator = SSSAuthenticator(config)
+ authenticator = Authenticator(config)
try:
auth = authenticator.basic_authenticate(username, password, obo)
except AuthException as e:
@@ -281,7 +280,7 @@
return self.manage_error(e)
# if we get here authentication was successful and we carry on (we don't care who authenticated)
- ss = SWORDServer(config, auth)
+ ss = SwordServer(config, auth)
sd = ss.service_document(sub_path)
web.header("Content-Type", "text/xml")
return sd
@@ -306,7 +305,7 @@
return self.manage_error(e)
# if we get here authentication was successful and we carry on (we don't care who authenticated)
- ss = SWORDServer(config, auth)
+ ss = SwordServer(config, auth)
cl = sss.list_collection(collection)
web.header("Content-Type", "text/xml")
return cl
@@ -332,7 +331,7 @@
# go ahead and process the deposit. Anything other than a success
# will be raised as a sword error
- ss = SWORDServer(config, auth)
+ ss = SwordServer(config, auth)
result = ss.deposit_new(collection, deposit)
# created
@@ -368,7 +367,7 @@
# NOTE: this method is not authenticated - we imagine sharing this URL with end-users who will just want
# to retrieve the content. It's only for the purposes of example, anyway
- ss = SWORDServer(config, None)
+ ss = SwordServer(config, None)
# first thing we need to do is check that there is an object to return, because otherwise we may throw a
# 406 Not Acceptable without looking first to see if there is even any media to content negotiate for
@@ -438,7 +437,7 @@
deposit = self.get_deposit(web, auth)
# now replace the content of the container
- ss = SWORDServer(config, auth)
+ ss = SwordServer(config, auth)
result = ss.replace(path, deposit)
# replaced
@@ -475,7 +474,7 @@
delete = self.get_delete(web, auth)
# carry out the delete
- ss = SWORDServer(config, auth)
+ ss = SwordServer(config, auth)
result = ss.delete_content(path, delete)
# just return, no need to give any more feedback
@@ -509,7 +508,7 @@
deposit = self.get_deposit(web, auth)
# if we get here authentication was successful and we carry on
- ss = SWORDServer(config, auth)
+ ss = SwordServer(config, auth)
result = ss.add_content(path, deposit)
web.header("Content-Type", "application/atom+xml;type=entry")
@@ -543,7 +542,7 @@
try:
auth = self.http_basic_authenticate(web)
- ss = SWORDServer(config, auth)
+ ss = SwordServer(config, auth)
# first thing we need to do is check that there is an object to return, because otherwise we may throw a
# 415 Unsupported Media Type without looking first to see if there is even any media to content negotiate for
@@ -594,7 +593,7 @@
# get the deposit object
deposit = self.get_deposit(web, auth)
- ss = SWORDServer(config, auth)
+ ss = SwordServer(config, auth)
result = ss.replace(path, deposit)
web.header("Location", result.location)
@@ -636,7 +635,7 @@
deposit = self.get_deposit(web, auth)
- ss = SWORDServer(config, auth)
+ ss = SwordServer(config, auth)
result = ss.deposit_existing(path, deposit)
# NOTE: spec says 201 Created for multipart and 200 Ok for metadata only
@@ -679,7 +678,7 @@
delete = self.get_delete(web, auth)
# do the delete
- ss = SWORDServer(config, auth)
+ ss = SwordServer(config, auth)
result = ss.delete_container(path, delete)
# no need to return any content
@@ -697,7 +696,7 @@
# authenticate
auth = self.http_basic_authenticate(web)
- ss = SWORDServer(config, auth)
+ ss = SwordServer(config, auth)
# first thing we need to do is check that there is an object to return, because otherwise we may throw a
# 415 Unsupported Media Type without looking first to see if there is even any media to content negotiate for
@@ -722,7 +721,7 @@
class Aggregation(SwordHttpHandler):
def GET(self, path):
# in this case we just redirect back to the Edit-URI with a 303 See Other
- ss = SWORDServer(config, None)
+ ss = SwordServer(config, None)
edit_uri = ss.get_edit_uri()
web.ctx.status = "303 See Other"
web.header("Content-Location", edit_uri)
@@ -749,7 +748,7 @@
Class to provide access to the component parts of the object on the server
"""
def GET(self, path):
- ss = SWORDServer(config, None)
+ ss = SwordServer(config, None)
# if we did, we can get hold of the media resource
fh = ss.get_part(path)
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|