ldp.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. import logging
  2. from collections import defaultdict
  3. from flask import Blueprint, request
  4. from lakesuperior.exceptions import InvalidResourceError, \
  5. ResourceExistsError, ResourceNotExistsError, \
  6. InvalidResourceError, ServerManagedTermError
  7. from lakesuperior.model.ldp_rs import Ldpc, LdpRs
  8. from lakesuperior.model.ldp_nr import LdpNr
  9. from lakesuperior.store_layouts.rdf.base_rdf_layout import BaseRdfLayout
  10. from lakesuperior.util.translator import Translator
  11. logger = logging.getLogger(__name__)
  12. # Blueprint for LDP REST API. This is what is usually found under `/rest/` in
  13. # standard fcrepo4. Here, it is under `/ldp` but initially `/rest` can be kept
  14. # for backward compatibility.
  15. ldp = Blueprint('ldp', __name__)
  16. accept_patch = (
  17. 'application/sparql-update',
  18. )
  19. accept_post = (
  20. 'application/ld+json',
  21. 'application/n-triples',
  22. 'application/rdf+xml',
  23. 'application/x-turtle',
  24. 'application/xhtml+xml',
  25. 'application/xml',
  26. 'text/html',
  27. 'text/n3',
  28. 'text/plain',
  29. 'text/rdf+n3',
  30. 'text/turtle',
  31. )
  32. #allow = (
  33. # 'COPY',
  34. # 'DELETE',
  35. # 'GET',
  36. # 'HEAD',
  37. # 'MOVE',
  38. # 'OPTIONS',
  39. # 'PATCH',
  40. # 'POST',
  41. # 'PUT',
  42. #)
  43. std_headers = {
  44. 'Accept-Patch' : ','.join(accept_patch),
  45. 'Accept-Post' : ','.join(accept_post),
  46. #'Allow' : ','.join(allow),
  47. }
  48. ## REST SERVICES ##
  49. @ldp.route('/<path:uuid>', methods=['GET'])
  50. @ldp.route('/', defaults={'uuid': None}, methods=['GET'],
  51. strict_slashes=False)
  52. def get_resource(uuid):
  53. '''
  54. Retrieve RDF or binary content.
  55. '''
  56. out_headers = std_headers
  57. pref_return = defaultdict(dict)
  58. if 'prefer' in request.headers:
  59. prefer = Translator.parse_rfc7240(request.headers['prefer'])
  60. logger.debug('Parsed Prefer header: {}'.format(prefer))
  61. if 'return' in prefer:
  62. pref_return = prefer['return']
  63. # @TODO Add conditions for LDP-NR
  64. rsrc = Ldpc(uuid)
  65. try:
  66. out = rsrc.get(pref_return=pref_return)
  67. except ResourceNotExistsError:
  68. return 'Resource #{} not found.'.format(rsrc.uuid), 404
  69. else:
  70. out_headers = rsrc.head()
  71. return (out.graph.serialize(format='turtle'), out_headers)
  72. @ldp.route('/<path:parent>', methods=['POST'])
  73. @ldp.route('/', defaults={'parent': None}, methods=['POST'],
  74. strict_slashes=False)
  75. def post_resource(parent):
  76. '''
  77. Add a new resource in a new URI.
  78. '''
  79. headers = std_headers
  80. try:
  81. slug = request.headers['Slug']
  82. except KeyError:
  83. slug = None
  84. try:
  85. rsrc = Ldpc.inst_for_post(parent, slug)
  86. except ResourceNotExistsError as e:
  87. return str(e), 404
  88. except InvalidResourceError as e:
  89. return str(e), 409
  90. try:
  91. rsrc.post(request.get_data().decode('utf-8'))
  92. except ServerManagedTermError as e:
  93. return str(e), 412
  94. headers.update({
  95. 'Location' : rsrc.uri,
  96. })
  97. return rsrc.uri, headers, 201
  98. @ldp.route('/<path:uuid>', methods=['PUT'])
  99. def put_resource(uuid):
  100. '''
  101. Add a new resource at a specified URI.
  102. '''
  103. logger.info('Request headers: {}'.format(request.headers))
  104. rsp_headers = std_headers
  105. rsrc = Ldpc(uuid)
  106. # Parse headers.
  107. pref_handling = None
  108. if 'prefer' in request.headers:
  109. prefer = Translator.parse_rfc7240(request.headers['prefer'])
  110. logger.debug('Parsed Prefer header: {}'.format(prefer))
  111. if 'handling' in prefer:
  112. pref_handling = prefer['handling']['value']
  113. try:
  114. ret = rsrc.put(
  115. request.get_data().decode('utf-8'),
  116. handling=pref_handling
  117. )
  118. except InvalidResourceError as e:
  119. return str(e), 409
  120. except ResourceExistsError as e:
  121. return str(e), 409
  122. except ServerManagedTermError as e:
  123. return str(e), 412
  124. else:
  125. res_code = 201 if ret == BaseRdfLayout.RES_CREATED else 204
  126. return '', res_code, rsp_headers
  127. @ldp.route('/<path:uuid>', methods=['PATCH'])
  128. def patch_resource(uuid):
  129. '''
  130. Update an existing resource with a SPARQL-UPDATE payload.
  131. '''
  132. headers = std_headers
  133. rsrc = Ldpc(uuid)
  134. try:
  135. rsrc.patch(request.get_data().decode('utf-8'))
  136. except ResourceNotExistsError:
  137. return 'Resource #{} not found.'.format(rsrc.uuid), 404
  138. except ServerManagedTermError as e:
  139. return str(e), 412
  140. return '', 204, headers
  141. @ldp.route('/<path:uuid>', methods=['DELETE'])
  142. def delete_resource(uuid):
  143. '''
  144. Delete a resource.
  145. '''
  146. headers = std_headers
  147. rsrc = Ldpc(uuid)
  148. try:
  149. rsrc.delete()
  150. except ResourceNotExistsError:
  151. return 'Resource #{} not found.'.format(rsrc.uuid), 404
  152. return '', 204, headers