def __init__(self, service): Resource.__init__(self) self.service = service self.resources = { '/capoeira/events/similar/artist': self.service.capoeiraSimilarByArtistQuery, '/capoeira/events/similar/track': self.service.capoeiraSimilarByTrackQuery } self.isLeaf = True # default_params is used in lieu of a defined Accept field in the header default_params = AcceptParameters(ContentType("text/html"), Language("en")) acceptable = [ AcceptParameters(ContentType("text/html"), Language("en")), AcceptParameters(ContentType("text/json"), Language("en")), AcceptParameters(ContentType("application/json"), Language("en")) ] self.contentNegotiator = ContentNegotiator(default_params, acceptable) # function mapping for rendering response self.renderFns = { 'text/html': formatHTMLResponse, 'text/json': formatJSONResponse, 'application/json': formatJSONResponse }
def do_conneg(self,node): """Specify the default parameters. These are the parameters which will be used in place of any HTTP Accept headers which are not present in the negotiation request. For example, if the Accept-Language header is not passed to the negotiator it will assume that the client request is for "en" """ # Configure conneg and work out default from node config acceptable=[] default_content_type=None for content_type in node.conneg: (code,dst,default) = node.conneg[content_type] self.log_message("conneg: config %s (%d,%s,%s)" % (content_type,code,dst,default)) acceptable.append(AcceptParameters(ContentType(content_type))) if (default): default_content_type=content_type # If there was no default, pick the last one we saw if (not default_content_type): default_content_type=content_type # Do we have an Accept header in the request? If not then default if ('Accept' not in self.headers): return(default_content_type) # else conneg... default_params = AcceptParameters(ContentType(default_content_type)) cn = ContentNegotiator(default_params, acceptable) accept = self.headers["Accept"] self.log_message("conneg: request Accept: %s" % (accept)) selected = cn.negotiate(accept) #self.log_message("conneg: match %s" % (str(selected))) if (selected is not None and str(selected.content_type) in node.conneg): self.log_message("conneg: selected %s" % (selected.content_type)) (code,dst,default) = node.conneg[str(selected.content_type)] else: self.log_message("conneg: defaulting to %s" % (default_content_type)) (code,dst,default) = node.conneg[default_content_type] self.code = code self.response_headers.append(['Location',self.full_uri(dst)]) self.log_message("conneg: %d redirect to %s" % (code,dst))
def build(self, resource, accept): adapters = HypermediaResource.adapters.all() acceptable = [AcceptParameters(ContentType(adapter.media_type)) for adapter in adapters] cn = ContentNegotiator(AcceptParameters(ContentType(self.default_type)), acceptable) negotiate = cn.negotiate(accept=accept) if negotiate: return HypermediaResponse(str(negotiate.content_type), resource) return HypermediaResponse(self.default_type, resource)
def test_en_only(self): """ en only """ accept_language = "en" server = [AcceptParameters(language=Language("en"))] cn = ContentNegotiator(acceptable=server) accept_parameters = cn.negotiate(accept_language=accept_language) assert str(accept_parameters.language) == "en"
def test_unsupported_by_server(self): """ en only, unsupported by server """ accept_language = "en" server = [AcceptParameters(language=Language("de"))] cn = ContentNegotiator(acceptable=server) accept_parameters = cn.negotiate(accept_language=accept_language) assert accept_parameters is None
def test_unsupported_by_server(self): """ text/plain only, unsupported by server """ client_accept = "text/plain" server = [AcceptParameters(ContentType("text/html"))] cn = ContentNegotiator(acceptable=server) accept_parameters = cn.negotiate(accept=client_accept) assert accept_parameters is None
def test_text_plain_only(self): """ text/plain only """ client_accept = "text/plain" server = [AcceptParameters(ContentType("text/plain"))] cn = ContentNegotiator(acceptable=server) accept_parameters = cn.negotiate(accept=client_accept) assert str(accept_parameters.content_type) == "text/plain"
def test_partially_supported_by_server(self): """ en, partially supported by server """ accept_language = "en" server = [AcceptParameters(language=Language("en-gb"))] cn = ContentNegotiator(acceptable=server) accept_parameters = cn.negotiate(accept_language=accept_language) assert str(accept_parameters.language) == "en-gb"
def _GET_container(self, path=None): """ GET a representation of the container in the appropriate (content negotiated) format as identified by the supplied id Args: - id: The ID of the container as supplied in the request URL Returns a representation of the container: SSS will return either the Atom Entry identical to the one supplied as a deposit receipt or the pure RDF/XML Statement depending on the Accept header """ ssslog.debug( "GET on Container (retrieve deposit receipt or statement); Incoming HTTP headers: " + str(request.environ)) # authenticate try: auth = self.authenticate() 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 # which would be weird from a client perspective if not ss.container_exists(path): return self.manage_error(SwordError(status=404, empty=True)) # get the content negotiation headers accept_header = request.environ.get("HTTP_ACCEPT") accept_packaging_header = request.environ.get( "HTTP_ACCEPT_PACKAGING") # do the negotiation default_accept_parameters, acceptable = config.get_container_formats( ) cn = ContentNegotiator(default_accept_parameters, acceptable) accept_parameters = cn.negotiate(accept=accept_header) ssslog.info("Container requested in format: " + str(accept_parameters)) # did we successfully negotiate a content type? if accept_parameters is None: raise SwordError(error_uri=Error.content, status=415, empty=True) # now actually get hold of the representation of the container and send it to the client cont = ss.get_container(path, accept_parameters) ssslog.info("Returning " + response.status + " from request on " + inspect.stack()[0][3]) if cont is not None: response.headers["Content-Type"] = str( accept_parameters.content_type.mimetype()) return cont except SwordError as e: return self.manage_error(e)
def test_supported_by_server_through_language_variants(self): """ en-gb, supported by server through language variants """ accept_language = "en-gb" server = [AcceptParameters(language=Language("en"))] cn = ContentNegotiator( acceptable=server, ignore_language_variants=True ) accept_parameters = cn.negotiate(accept_language=accept_language) assert str(accept_parameters.language) == "en"
def _GET_media_resource(self, path=None): """ GET the media resource content in the requested format (web request will include content negotiation via Accept header) Args: - id: the ID of the object in the store Returns the content in the requested format """ ssslog.debug("GET on MediaResource; Incoming HTTP headers: " + str(request.environ)) # NOTE: this method is not authenticated - we imagine sharing this URL with end-users who will just want # to retrieve the content. 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 # which would be weird from a client perspective if not ss.media_resource_exists(path): return self.manage_error(SwordError(status=404, empty=True)) # get the content negotiation headers accept_header = request.environ.get("HTTP_ACCEPT") accept_packaging_header = request.environ.get("HTTP_ACCEPT_PACKAGING") # do the negotiation default_accept_parameters, acceptable = config.get_media_resource_formats( ) cn = ContentNegotiator(default_accept_parameters, acceptable) accept_parameters = cn.negotiate( accept=accept_header, accept_packaging=accept_packaging_header) ssslog.info("Conneg format: " + str(accept_parameters)) try: # can get hold of the media resource media_resource = ss.get_media_resource(path, accept_parameters) except SwordError as e: return self.manage_error(e) # either send the client a redirect, or stream the content out if media_resource.redirect: redirect(media_resource.url, _code=302) # FOUND (not SEE OTHER) return else: response.content_type = media_resource.content_type if media_resource.packaging is not None: response.headers["Packaging"] = str(media_resource.packaging) f = open(media_resource.filepath, "r") response.status_int = 200 response.status = "200 OK" ssslog.info("Returning " + response.status + " from request on " + inspect.stack()[0][3]) return f.read()
def test_unsupported_by_server(self): """ en-gb, unsupported by server """ accept_language = "en-gb" server = [AcceptParameters(language=Language("en"))] cn = ContentNegotiator( acceptable=server, ignore_language_variants=False ) accept_parameters = cn.negotiate(accept_language=accept_language) assert accept_parameters is None
def test_without_q_values(self): """ en vs de without q values """ accept_language = "en, de" server = [ AcceptParameters(language=Language("en")), AcceptParameters(language=Language("de")), ] cn = ContentNegotiator(acceptable=server) accept_parameters = cn.negotiate(accept_language=accept_language) assert str(accept_parameters.language) == "en"
def test_with_q_values(self): """ application/atom+xml vs application/rdf+xml with q values """ client_accept = "application/atom+xml;q=0.6, application/rdf+xml;q=0.9" server = [ AcceptParameters(ContentType("application/rdf+xml")), AcceptParameters(ContentType("application/atom+xml")), ] cn = ContentNegotiator(acceptable=server) accept_parameters = cn.negotiate(accept=client_accept) assert str(accept_parameters.content_type) == "application/rdf+xml"
def test_with_q_values(self): """ fr vs no with q values """ accept_language = "fr;q=0.7, no;q=0.8" server = [ AcceptParameters(language=Language("fr")), AcceptParameters(language=Language("no")), ] cn = ContentNegotiator(acceptable=server) accept_parameters = cn.negotiate(accept_language=accept_language) assert str(accept_parameters.language) == "no"
def test_wildcard_by_itself(self): """ * by itself """ accept_language = "*" server = [ AcceptParameters(language=Language("no")), AcceptParameters(language=Language("de")), ] cn = ContentNegotiator(acceptable=server) accept_parameters = cn.negotiate(accept_language=accept_language) assert str(accept_parameters.language) == "no"
def test_image_wildcard_supported_by_serverwith_other_options_primary_option_supported(self): """ * with other options, primary option supported """ accept_language = "en, *" server = [ AcceptParameters(language=Language("en")), AcceptParameters(language=Language("de")), ] cn = ContentNegotiator(acceptable=server) accept_parameters = cn.negotiate(accept_language=accept_language) assert str(accept_parameters.language) == "en"
def test_mixed_q_values_most_preferred_available(self): """ en vs no vs de with mixed q values, most preferred available """ accept_language = "en;q=0.6, no;q=0.9, de" server = [ AcceptParameters(language=Language("no")), AcceptParameters(language=Language("de")), ] cn = ContentNegotiator(acceptable=server) accept_parameters = cn.negotiate(accept_language=accept_language) assert str(accept_parameters.language) == "de"
def test_content_type_and_language_specified(self): """ content type and language specified """ accept = "text/html" accept_lang = "en" server = [AcceptParameters(ContentType("text/html"), Language("en"))] cn = ContentNegotiator(acceptable=server) accept_parameters = cn.negotiate( accept=accept, accept_language=accept_lang ) assert str(accept_parameters.content_type) == "text/html" assert str(accept_parameters.language) == "en"
def test_any_supported_by_server(self): """ */* supported by server """ client_accept = "*/*" server = [ AcceptParameters(ContentType("text/plain")), AcceptParameters(ContentType("image/png")), AcceptParameters(ContentType("image/jpeg")), ] cn = ContentNegotiator(acceptable=server) accept_parameters = cn.negotiate(accept=client_accept) assert str(accept_parameters.content_type) == "text/plain"
def test_with_mixed_q_values(self): """ en vs de vs fr with mixed q values """ accept_language = "en;q=0.6, de;q=0.9, fr" server = [ AcceptParameters(language=Language("en")), AcceptParameters(language=Language("de")), AcceptParameters(language=Language("fr")), ] cn = ContentNegotiator(acceptable=server) accept_parameters = cn.negotiate(accept_language=accept_language) assert str(accept_parameters.language) == "fr"
def test_atom_with_type_feed_supported_by_server(self): """ application/atom+xml;type=feed supported by server """ client_accept = "application/atom+xml;type=feed" server = [ AcceptParameters(ContentType("application/atom+xml;type=feed")) ] cn = ContentNegotiator(acceptable=server) accept_parameters = cn.negotiate(accept=client_accept) assert ( str(accept_parameters.content_type) == "application/atom+xml;type=feed" )
def _GET_media_resource(self, path=None): """ GET the media resource content in the requested format (web request will include content negotiation via Accept header) Args: - id: the ID of the object in the store Returns the content in the requested format """ ssslog.debug("GET on MediaResource; Incoming HTTP headers: " + str(request.environ)) # NOTE: this method is not authenticated - we imagine sharing this URL with end-users who will just want # to retrieve the content. 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 # which would be weird from a client perspective if not ss.media_resource_exists(path): return self.manage_error(SwordError(status=404, empty=True)) # get the content negotiation headers accept_header = request.environ.get("HTTP_ACCEPT") accept_packaging_header = request.environ.get("HTTP_ACCEPT_PACKAGING") # do the negotiation default_accept_parameters, acceptable = config.get_media_resource_formats() cn = ContentNegotiator(default_accept_parameters, acceptable) accept_parameters = cn.negotiate(accept=accept_header, accept_packaging=accept_packaging_header) ssslog.info("Conneg format: " + str(accept_parameters)) try: # can get hold of the media resource media_resource = ss.get_media_resource(path, accept_parameters) except SwordError as e: return self.manage_error(e) # either send the client a redirect, or stream the content out if media_resource.redirect: redirect(media_resource.url, _code=302) # FOUND (not SEE OTHER) return else: response.content_type = media_resource.content_type if media_resource.packaging is not None: response.headers["Packaging"] = str(media_resource.packaging) f = open(media_resource.filepath, "r") response.status_int = 200 response.status = "200 OK" ssslog.info("Returning " + response.status + " from request on " + inspect.stack()[0][3]) return f.read()
def determine_content_type(self, request): acceptable = ( AcceptParameters(ContentType('application/json')), AcceptParameters(ContentType('application/hal+json')), AcceptParameters(ContentType('application/vnd.siren+json')), ) negotiator = ContentNegotiator(acceptable[0], acceptable) accept = request.META.get('HTTP_ACCEPT') negotiated_type = negotiator.negotiate(accept=request.META.get('HTTP_ACCEPT')) if negotiated_type: return negotiated_type.content_type return acceptable[0].content_type
def test_mixed_q_values_most_preferred_available(self): """ application/atom+xml vs application/rdf+xml vs text/html with mixed q values, most preferred available """ client_accept = ( "application/atom+xml;q=0.6, application/rdf+xml;q=0.9, text/html" ) server = [ AcceptParameters(ContentType("application/rdf+xml")), AcceptParameters(ContentType("text/html")), ] cn = ContentNegotiator(acceptable=server) accept_parameters = cn.negotiate(accept=client_accept) assert str(accept_parameters.content_type) == "text/html"
def GET(self, path): """ GET the media resource content in the requested format (web request will include content negotiation via Accept header) Args: - id: the ID of the object in the store Returns the content in the requested format """ ssslog.debug("GET on MediaResourceContent; Incoming HTTP headers: " + str(web.ctx.environ)) # 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) # 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 # which would be weird from a client perspective if not ss.media_resource_exists(path): return web.notfound() # get the content negotiation headers accept_header = web.ctx.environ.get("HTTP_ACCEPT") accept_packaging_header = web.ctx.environ.get("HTTP_ACCEPT_PACKAGING") # do the negotiation default_accept_parameters, acceptable = config.get_media_resource_formats() cn = ContentNegotiator(default_accept_parameters, acceptable) accept_parameters = cn.negotiate(accept=accept_header, accept_packaging=accept_packaging_header) ssslog.info("Conneg format: " + str(accept_parameters)) try: # can get hold of the media resource media_resource = ss.get_media_resource(path, accept_parameters) except SwordError as e: ssslog.debug("Raised error") return self.manage_error(e) # either send the client a redirect, or stream the content out if media_resource.redirect: return web.found(media_resource.url) else: web.header("Content-Type", media_resource.content_type) if media_resource.packaging is not None: web.header("Packaging", media_resource.packaging) f = open(media_resource.filepath, "r") web.ctx.status = "200 OK" return f.read()
def test_two_content_types_and_two_languages_specified(self): """ Two content types and 2 languages specified """ accept = "text/html, text/plain" accept_lang = "en, de" server = [ AcceptParameters(ContentType("text/html"), Language("de")), AcceptParameters(ContentType("text/plain"), Language("en")), ] cn = ContentNegotiator(acceptable=server) accept_parameters = cn.negotiate( accept=accept, accept_language=accept_lang ) assert str(accept_parameters.content_type) == "text/html" assert str(accept_parameters.language) == "de"
def getFormat(self, request): defaultOutput = AcceptParameters( ContentType(self.defaultOutputFormat, params='q=0')) acceptable = [defaultOutput] + [AcceptParameters(ContentType(x)) for x in self._outputFormatsPreference] cn = ContentNegotiator(defaultOutput, acceptable) if request.requestHeaders.hasHeader("Accept"): kwargs = {"accept": request.requestHeaders.getRawHeaders("Accept")[0]} else: kwargs = {} accp = cn.negotiate(**kwargs) return str(accp.content_type) if accp else None
def _GET_container(self, path=None): """ GET a representation of the container in the appropriate (content negotiated) format as identified by the supplied id Args: - id: The ID of the container as supplied in the request URL Returns a representation of the container: SSS will return either the Atom Entry identical to the one supplied as a deposit receipt or the pure RDF/XML Statement depending on the Accept header """ ssslog.debug("GET on Container (retrieve deposit receipt or statement); Incoming HTTP headers: " + str(request.environ)) # authenticate try: auth = self.authenticate() 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 # which would be weird from a client perspective if not ss.container_exists(path): return self.manage_error(SwordError(status=404, empty=True)) # get the content negotiation headers accept_header = request.environ.get("HTTP_ACCEPT") accept_packaging_header = request.environ.get("HTTP_ACCEPT_PACKAGING") # do the negotiation default_accept_parameters, acceptable = config.get_container_formats() cn = ContentNegotiator(default_accept_parameters, acceptable) accept_parameters = cn.negotiate(accept=accept_header) ssslog.info("Container requested in format: " + str(accept_parameters)) # did we successfully negotiate a content type? if accept_parameters is None: raise SwordError(error_uri=Error.content, status=415, empty=True) # now actually get hold of the representation of the container and send it to the client cont = ss.get_container(path, accept_parameters) ssslog.info("Returning " + response.status + " from request on " + inspect.stack()[0][3]) if cont is not None: response.headers["Content-Type"] = str(accept_parameters.content_type.mimetype()) return cont except SwordError as e: return self.manage_error(e)
def get(self, request): content_type_to_acceptable = lambda content_type: AcceptParameters( ContentType(content_type)) acceptable = map(content_type_to_acceptable, self.content_type_providers().keys()) preferred_content_type = self.preferred_content_type accept = request.headers.get('ACCEPT') negotiator = ContentNegotiator( content_type_to_acceptable(preferred_content_type), acceptable) negotiated_type = negotiator.negotiate(accept=accept) content_type = negotiated_type.content_type or preferred_content_type provider = self.content_type_providers()[str(content_type)] return provider()
class CapoeiraResource(Resource): def __init__(self, service): Resource.__init__(self) self.service = service self.resources = { '/capoeira/events/similar/artist': self.service.capoeiraSimilarByArtistQuery, '/capoeira/events/similar/track': self.service.capoeiraSimilarByTrackQuery } self.isLeaf = True # default_params is used in lieu of a defined Accept field in the header default_params = AcceptParameters(ContentType("text/html"), Language("en")) acceptable = [ AcceptParameters(ContentType("text/html"), Language("en")), AcceptParameters(ContentType("text/json"), Language("en")), AcceptParameters(ContentType("application/json"), Language("en")) ] self.contentNegotiator = ContentNegotiator(default_params, acceptable) # function mapping for rendering response self.renderFns = { 'text/html': formatHTMLResponse, 'text/json': formatJSONResponse, 'application/json': formatJSONResponse } def _delayedRender(self, request, deferred, renderFn): def d(_): request.write(renderFn(deferred.result)) request.finish() return d def render_GET(self, request): # try to find acceptable response format, else fail with 406 acceptable = self.contentNegotiator.negotiate( request.getHeader('Accept')) if not acceptable: request.setResponseCode(406) return "" # get acceptable content type and associated render function contentType = str(acceptable.content_type) renderFn = self.renderFns[contentType] request.setHeader("Content-Type", contentType) if request.path in self.resources: d = self.resources[request.path](request) d.addCallback(self._delayedRender(request, d, renderFn)) return NOT_DONE_YET else: # TODO: better handling of path not found return "{}"
def getFormat(self, request): defaultOutput = AcceptParameters( ContentType(self.defaultOutputFormat, params='q=0')) acceptable = [defaultOutput] + [ AcceptParameters(ContentType(x)) for x in self._outputFormatsPreference ] cn = ContentNegotiator(defaultOutput, acceptable) if request.requestHeaders.hasHeader("Accept"): kwargs = { "accept": request.requestHeaders.getRawHeaders("Accept")[0] } else: kwargs = {} accp = cn.negotiate(**kwargs) return str(accp.content_type) if accp else None
def test_two_content_types_and_one_language_specified_with_weights(self): """ Two content types and one language specified, with weights """ weights = { "content_type": 2.0, "language": 1.0, "charset": 1.0, "encoding": 1.0, } accept = "text/html, text/plain" accept_lang = "en" server = [ AcceptParameters(ContentType("text/html"), Language("de")), AcceptParameters(ContentType("text/plain"), Language("en")), ] cn = ContentNegotiator(acceptable=server, weights=weights) accept_parameters = cn.negotiate( accept=accept, accept_language=accept_lang ) assert str(accept_parameters.content_type) == "text/plain" assert str(accept_parameters.language) == "en"
def _init_content_negotiator(self): #TODO: use config instead default_content_type = "application/ld+json" default_accept_params = AcceptParameters( ContentType(default_content_type)) # rdf types rdf_types = set([ plugin.name for plugin in plugins(kind=Serializer) if "/" in plugin.name ]) #Blacklisted because mapped to TriX that requires a context-aware store blacklisted_types = ["application/xml"] #TODO: consider other types accepted_types = list( rdf_types.difference(blacklisted_types)) + ["application/json"] self._logger.debug("Accepted types: %s" % accepted_types) acceptable_params = [default_accept_params] + [ AcceptParameters(ContentType(ct)) for ct in accepted_types ] self._negotiator = ContentNegotiator(default_accept_params, acceptable_params)
def _init_content_negotiator(self): #TODO: use config instead default_content_type = "application/ld+json" default_accept_params = AcceptParameters(ContentType(default_content_type)) # rdf types rdf_types = set([plugin.name for plugin in plugins(kind=Serializer) if "/" in plugin.name]) #Blacklisted because mapped to TriX that requires a context-aware store blacklisted_types = ["application/xml"] #TODO: consider other types accepted_types = list(rdf_types.difference(blacklisted_types)) + ["application/json"] self._logger.debug("Accepted types: %s" % accepted_types) acceptable_params = [default_accept_params] + [AcceptParameters(ContentType(ct)) for ct in accepted_types] self._negotiator = ContentNegotiator(default_accept_params, acceptable_params)
def get_accept_parameters(self, accept, accept_language): # do the content negotiation cn = ContentNegotiator(default_accept_parameters=self.config.accepts_default, acceptable=self.config.accepts, weights=self.config.conneg_weights) ap = cn.negotiate(accept=accept, accept_language=accept_language) return ap
class HTTPController(object): """ HTTP. TODO: check declared methods (only GET and HEAD are implicit). """ DEFAULT_CONFIG = { 'allow_put_new_type_existing_resource': False, 'allow_put_remove_type_existing_resource': False, 'allow_put_new_resource': True } def __init__(self, manager, config={}): self._logger = getLogger(__name__) self._manager = manager # For operations except POST self._cruder = HashLessCRUDer(manager) self._config = self.DEFAULT_CONFIG.copy() self._config.update(config) self._negotiator = None self._init_content_negotiator() def _init_content_negotiator(self): #TODO: use config instead default_content_type = "application/ld+json" default_accept_params = AcceptParameters( ContentType(default_content_type)) # rdf types rdf_types = set([ plugin.name for plugin in plugins(kind=Serializer) if "/" in plugin.name ]) #Blacklisted because mapped to TriX that requires a context-aware store blacklisted_types = ["application/xml"] #TODO: consider other types accepted_types = list( rdf_types.difference(blacklisted_types)) + ["application/json"] self._logger.debug("Accepted types: %s" % accepted_types) acceptable_params = [default_accept_params] + [ AcceptParameters(ContentType(ct)) for ct in accepted_types ] self._negotiator = ContentNegotiator(default_accept_params, acceptable_params) def get(self, hashless_iri, accept_header="*/*", **kwargs): """ TODO: describe. No support declaration required. """ self._logger.debug("Accept header: %s" % accept_header) accepted_type = self._negotiator.negotiate(accept=accept_header) if accepted_type is None: raise OMNotAcceptableException() content_type = str(accepted_type.content_type) self._logger.debug("Selected content-type: %s" % content_type) try: return self._cruder.get(hashless_iri, content_type) except OMObjectNotFoundError: raise OMResourceNotFoundException() def head(self, hashless_iri, **kwargs): """ TODO: describe. No support declaration required. """ #TODO: consider a more efficient implementation try: self._cruder.get(hashless_iri, None) except OMObjectNotFoundError: raise OMResourceNotFoundException() def post(self, hashless_iri, content_type=None, payload=None, **kwargs): """ TODO: categorize the resource to decide what to do. Support declaration and implementation are required. """ if content_type is None: raise OMBadRequestException("Content type is required.") #if payload is None: # raise BadRequestException("No payload given.") # Must be its ID (we do not consider resources with hash IRIs) resource = self._manager.get(id=hashless_iri) if resource is None: raise OMResourceNotFoundException() operation = resource.get_operation(HTTP_POST) if operation is not None: graph = Graph() try: if content_type in JSON_TYPES: resource = self._manager.get(hashless_iri=hashless_iri) graph.parse(data=payload, format="json-ld", publicID=hashless_iri, context=resource.context) else: graph.parse(data=payload, format=content_type, publicID=hashless_iri) except PluginException: raise OMNotAcceptableException() #TODO: add arguments return operation(resource, graph=graph, content_type=content_type) #TODO: When error code is 405, alternatives must be given. raise OMMethodNotAllowedException() def put(self, hashless_iri, content_type=None, payload=None, **kwargs): """ TODO: describe. No support declaration required. """ if content_type is None: raise OMBadRequestException("Content type is required.") if payload is None: raise OMBadRequestException("No payload given.") return self._cruder.update(self, hashless_iri, payload, content_type, allow_new_type=False, allow_type_removal=False, allow_put_new_resource=True) def delete(self, hashless_iri, **kwargs): """ TODO: describe. No declaration required. """ self._cruder.delete(hashless_iri) def options(self, hashless_iri, **kwargs): """ TODO: implement it """ raise NotImplementedError("") def patch(self, hashless_iri, content_type=None, payload=None, **kwargs): """ TODO: implement it """ if content_type is None: raise OMBadRequestException("Content type is required.") if payload is None: raise OMBadRequestException("No payload given.") raise NotImplementedError("PATCH is not yet supported.")
class HTTPController(object): """ HTTP. TODO: check declared methods (only GET and HEAD are implicit). """ DEFAULT_CONFIG = {'allow_put_new_type_existing_resource': False, 'allow_put_remove_type_existing_resource': False, 'allow_put_new_resource': True } def __init__(self, manager, config={}): self._logger = getLogger(__name__) self._manager = manager # For operations except POST self._cruder = HashLessCRUDer(manager) self._config = self.DEFAULT_CONFIG.copy() self._config.update(config) self._negotiator = None self._init_content_negotiator() def _init_content_negotiator(self): #TODO: use config instead default_content_type = "application/ld+json" default_accept_params = AcceptParameters(ContentType(default_content_type)) # rdf types rdf_types = set([plugin.name for plugin in plugins(kind=Serializer) if "/" in plugin.name]) #Blacklisted because mapped to TriX that requires a context-aware store blacklisted_types = ["application/xml"] #TODO: consider other types accepted_types = list(rdf_types.difference(blacklisted_types)) + ["application/json"] self._logger.debug("Accepted types: %s" % accepted_types) acceptable_params = [default_accept_params] + [AcceptParameters(ContentType(ct)) for ct in accepted_types] self._negotiator = ContentNegotiator(default_accept_params, acceptable_params) def get(self, hashless_iri, accept_header="*/*", **kwargs): """ TODO: describe. No support declaration required. """ self._logger.debug("Accept header: %s" % accept_header) accepted_type = self._negotiator.negotiate(accept=accept_header) if accepted_type is None: raise OMNotAcceptableException() content_type = str(accepted_type.content_type) self._logger.debug("Selected content-type: %s" % content_type) try: return self._cruder.get(hashless_iri, content_type) except OMObjectNotFoundError: raise OMResourceNotFoundException() def head(self, hashless_iri, **kwargs): """ TODO: describe. No support declaration required. """ #TODO: consider a more efficient implementation try: self._cruder.get(hashless_iri, None) except OMObjectNotFoundError: raise OMResourceNotFoundException() def post(self, hashless_iri, content_type=None, payload=None, **kwargs): """ TODO: categorize the resource to decide what to do. Support declaration and implementation are required. """ if content_type is None: raise OMBadRequestException("Content type is required.") #if payload is None: # raise BadRequestException("No payload given.") # Must be its ID (we do not consider resources with hash IRIs) resource = self._manager.get(id=hashless_iri) if resource is None: raise OMResourceNotFoundException() operation = resource.get_operation(HTTP_POST) if operation is not None: graph = Graph() try: if content_type in JSON_TYPES: resource = self._manager.get(hashless_iri=hashless_iri) graph.parse(data=payload, format="json-ld", publicID=hashless_iri, context=resource.context) else: graph.parse(data=payload, format=content_type, publicID=hashless_iri) except PluginException: raise OMNotAcceptableException() #TODO: add arguments return operation(resource, graph=graph, content_type=content_type) #TODO: When error code is 405, alternatives must be given. raise OMMethodNotAllowedException() def put(self, hashless_iri, content_type=None, payload=None, **kwargs): """ TODO: describe. No support declaration required. """ if content_type is None: raise OMBadRequestException("Content type is required.") if payload is None: raise OMBadRequestException("No payload given.") return self._cruder.update(self, hashless_iri, payload, content_type, allow_new_type=False, allow_type_removal=False, allow_put_new_resource=True) def delete(self, hashless_iri, **kwargs): """ TODO: describe. No declaration required. """ self._cruder.delete(hashless_iri) def options(self, hashless_iri, **kwargs): """ TODO: implement it """ raise NotImplementedError("") def patch(self, hashless_iri, content_type=None, payload=None, **kwargs): """ TODO: implement it """ if content_type is None: raise OMBadRequestException("Content type is required.") if payload is None: raise OMBadRequestException("No payload given.") raise NotImplementedError("PATCH is not yet supported.")