1818from aiohttp .web import StreamResponse
1919from json import JSONDecodeError
2020
21- from h5json .hdf5dtype import validateTypeItem , getBaseTypeJson
2221from h5json .hdf5dtype import createDataType , getItemSize
23- from h5json .array_util import jsonToArray , getNumElements , bytesArrayToList
22+ from h5json .array_util import jsonToArray , getNumElements
2423from h5json .array_util import bytesToArray , arrayToBytes , decodeData , encodeData
25- from h5json .objid import isValidUuid , getRootObjId
24+ from h5json .objid import isValidUuid
2625
2726from .util .httpUtil import getAcceptType , jsonResponse , getHref , getBooleanParam
2827from .util .globparser import globmatch
3231from .util .attrUtil import validateAttributeName , getRequestCollectionName
3332from .util .dsetUtil import getShapeDims
3433
35- from .servicenode_lib import getDomainJson , getObjectJson , validateAction
36- from .servicenode_lib import getAttributes , putAttributes , deleteAttributes
34+ from .servicenode_lib import getDomainJson , getAttributeFromRequest , getAttributesFromRequest
35+ from .servicenode_lib import getAttributes , putAttributes , deleteAttributes , validateAction
3736from .domain_crawl import DomainCrawler
3837from . import hsds_logger as log
3938from . import config
@@ -296,244 +295,6 @@ async def GET_Attribute(request):
296295 return resp
297296
298297
299- async def _getTypeFromRequest (app , body , obj_id = None , bucket = None ):
300- """ return a type json from the request body """
301- if "type" not in body :
302- msg = "PUT attribute with no type in body"
303- log .warn (msg )
304- raise HTTPBadRequest (reason = msg )
305- datatype = body ["type" ]
306-
307- if isinstance (datatype , str ) and datatype .startswith ("t-" ):
308- # Committed type - fetch type json from DN
309- ctype_id = datatype
310- log .debug (f"got ctypeid: { ctype_id } " )
311- ctype_json = await getObjectJson (app , ctype_id , bucket = bucket )
312- log .debug (f"ctype { ctype_id } : { ctype_json } " )
313- root_id = getRootObjId (obj_id )
314- if ctype_json ["root" ] != root_id :
315- msg = "Referenced committed datatype must belong in same domain"
316- log .warn (msg )
317- raise HTTPBadRequest (reason = msg )
318- datatype = ctype_json ["type" ]
319- # add the ctype_id to the type
320- datatype ["id" ] = ctype_id
321- elif isinstance (datatype , str ):
322- try :
323- # convert predefined type string (e.g. "H5T_STD_I32LE") to
324- # corresponding json representation
325- datatype = getBaseTypeJson (datatype )
326- except TypeError :
327- msg = "PUT attribute with invalid predefined type"
328- log .warn (msg )
329- raise HTTPBadRequest (reason = msg )
330-
331- try :
332- validateTypeItem (datatype )
333- except KeyError as ke :
334- msg = f"KeyError creating type: { ke } "
335- log .warn (msg )
336- raise HTTPBadRequest (reason = msg )
337- except TypeError as te :
338- msg = f"TypeError creating type: { te } "
339- log .warn (msg )
340- raise HTTPBadRequest (reason = msg )
341- except ValueError as ve :
342- msg = f"ValueError creating type: { ve } "
343- log .warn (msg )
344- raise HTTPBadRequest (reason = msg )
345-
346- return datatype
347-
348-
349- def _getShapeFromRequest (body ):
350- """ get shape json from request body """
351- shape_json = {}
352- if "shape" in body :
353- shape_body = body ["shape" ]
354- shape_class = None
355- if isinstance (shape_body , dict ) and "class" in shape_body :
356- shape_class = shape_body ["class" ]
357- elif isinstance (shape_body , str ):
358- shape_class = shape_body
359- if shape_class :
360- if shape_class == "H5S_NULL" :
361- shape_json ["class" ] = "H5S_NULL"
362- if isinstance (shape_body , dict ) and "dims" in shape_body :
363- msg = "can't include dims with null shape"
364- log .warn (msg )
365- raise HTTPBadRequest (reason = msg )
366- if isinstance (shape_body , dict ) and "value" in body :
367- msg = "can't have H5S_NULL shape with value"
368- log .warn (msg )
369- raise HTTPBadRequest (reason = msg )
370- elif shape_class == "H5S_SCALAR" :
371- shape_json ["class" ] = "H5S_SCALAR"
372- dims = getShapeDims (shape_body )
373- if len (dims ) != 1 or dims [0 ] != 1 :
374- msg = "dimensions aren't valid for scalar attribute"
375- log .warn (msg )
376- raise HTTPBadRequest (reason = msg )
377- elif shape_class == "H5S_SIMPLE" :
378- shape_json ["class" ] = "H5S_SIMPLE"
379- dims = getShapeDims (shape_body )
380- shape_json ["dims" ] = dims
381- else :
382- msg = f"Unknown shape class: { shape_class } "
383- log .warn (msg )
384- raise HTTPBadRequest (reason = msg )
385- else :
386- # no class, interpet shape value as dimensions and
387- # use H5S_SIMPLE as class
388- if isinstance (shape_body , list ) and len (shape_body ) == 0 :
389- shape_json ["class" ] = "H5S_SCALAR"
390- else :
391- shape_json ["class" ] = "H5S_SIMPLE"
392- dims = getShapeDims (shape_body )
393- shape_json ["dims" ] = dims
394- else :
395- shape_json ["class" ] = "H5S_SCALAR"
396-
397- return shape_json
398-
399-
400- def _getValueFromRequest (body , data_type , data_shape ):
401- """ Get attribute value from request json """
402- dims = getShapeDims (data_shape )
403- if "value" in body :
404- if dims is None :
405- msg = "Bad Request: data can not be included with H5S_NULL space"
406- log .warn (msg )
407- raise HTTPBadRequest (reason = msg )
408- value = body ["value" ]
409- # validate that the value agrees with type/shape
410- arr_dtype = createDataType (data_type ) # np datatype
411- if len (dims ) == 0 :
412- np_dims = [1 , ]
413- else :
414- np_dims = dims
415-
416- if body .get ("encoding" ):
417- item_size = getItemSize (data_type )
418- if item_size == "H5T_VARIABLE" :
419- msg = "base64 encoding is not support for variable length attributes"
420- log .warn (msg )
421- raise HTTPBadRequest (reason = msg )
422- try :
423- data = decodeData (value )
424- except ValueError :
425- msg = "unable to decode data"
426- log .warn (msg )
427- raise HTTPBadRequest (reason = msg )
428-
429- expected_numbytes = arr_dtype .itemsize * np .prod (dims )
430- if len (data ) != expected_numbytes :
431- msg = f"expected: { expected_numbytes } but got: { len (data )} "
432- log .warn (msg )
433- raise HTTPBadRequest (reason = msg )
434-
435- # check to see if this works with our shape and type
436- try :
437- arr = bytesToArray (data , arr_dtype , np_dims )
438- except ValueError as e :
439- log .debug (f"data: { data } " )
440- log .debug (f"type: { arr_dtype } " )
441- log .debug (f"np_dims: { np_dims } " )
442- msg = f"Bad Request: encoded input data doesn't match shape and type: { e } "
443- log .warn (msg )
444- raise HTTPBadRequest (reason = msg )
445-
446- value_json = None
447- # now try converting to JSON
448- list_data = arr .tolist ()
449- try :
450- value_json = bytesArrayToList (list_data )
451- except ValueError as err :
452- msg = f"Cannot decode bytes to list: { err } , will store as encoded bytes"
453- log .warn (msg )
454- if value_json :
455- log .debug ("will store base64 input as json" )
456- if data_shape ["class" ] == "H5S_SCALAR" :
457- # just use the scalar value
458- value = value_json [0 ]
459- else :
460- value = value_json # return this
461- else :
462- value = data # return bytes to signal that this needs to be encoded
463- else :
464- # verify that the input data matches the array shape and type
465- try :
466- jsonToArray (np_dims , arr_dtype , value )
467- except ValueError as e :
468- msg = f"Bad Request: input data doesn't match selection: { e } "
469- log .warn (msg )
470- raise HTTPBadRequest (reason = msg )
471- else :
472- value = None
473-
474- return value
475-
476-
477- async def _getAttributeFromRequest (app , req_json , obj_id = None , bucket = None ):
478- """ return attribute from given request json """
479- attr_item = {}
480- attr_type = await _getTypeFromRequest (app , req_json , obj_id = obj_id , bucket = bucket )
481- attr_shape = _getShapeFromRequest (req_json )
482- attr_item = {"type" : attr_type , "shape" : attr_shape }
483- attr_value = _getValueFromRequest (req_json , attr_type , attr_shape )
484- if attr_value is not None :
485- if isinstance (attr_value , bytes ):
486- attr_value = encodeData (attr_value ) # store as base64
487- attr_item ["encoding" ] = "base64"
488- else :
489- # just store the JSON dict or primitive value
490- attr_item ["value" ] = attr_value
491- else :
492- attr_item ["value" ] = None
493-
494- return attr_item
495-
496-
497- async def _getAttributesFromRequest (request , req_json , obj_id = None , bucket = None ):
498- """ read the given JSON dictinary and return dict of attribute json """
499-
500- app = request .app
501- attr_items = {}
502- kwargs = {"obj_id" : obj_id }
503- if bucket :
504- kwargs ["bucket" ] = bucket
505- if "attributes" in req_json :
506- attributes = req_json ["attributes" ]
507- if not isinstance (attributes , dict ):
508- msg = f"expected list for attributes but got: { type (attributes )} "
509- log .warn (msg )
510- raise HTTPBadRequest (reason = msg )
511- # read each attr_item and canonicalize the shape, type, verify value
512- for attr_name in attributes :
513- attr_json = attributes [attr_name ]
514- attr_item = await _getAttributeFromRequest (app , attr_json , ** kwargs )
515- attr_items [attr_name ] = attr_item
516-
517- elif "type" in req_json :
518- # single attribute create - fake an item list
519- attr_item = await _getAttributeFromRequest (app , req_json , ** kwargs )
520- if "name" in req_json :
521- attr_name = req_json ["name" ]
522- else :
523- attr_name = request .match_info .get ("name" )
524- validateAttributeName (attr_name )
525- if not attr_name :
526- msg = "Missing attribute name"
527- log .warn (msg )
528- raise HTTPBadRequest (reason = msg )
529-
530- attr_items [attr_name ] = attr_item
531- else :
532- log .debug (f"_getAttributes from request - no attribute defined in { req_json } " )
533-
534- return attr_items
535-
536-
537298async def PUT_Attribute (request ):
538299 """HTTP method to create a new attribute"""
539300 log .request (request )
@@ -556,7 +317,7 @@ async def PUT_Attribute(request):
556317 log .debug (f"Attribute name: [{ attr_name } ]" )
557318 validateAttributeName (attr_name )
558319
559- log .info (f"PUT Attributes id: { req_obj_id } name: { attr_name } " )
320+ log .info (f"PUT Attribute id: { req_obj_id } name: { attr_name } " )
560321 username , pswd = getUserPasswordFromRequest (request )
561322 # write actions need auth
562323 await validateUserPassword (app , username , pswd )
@@ -588,7 +349,7 @@ async def PUT_Attribute(request):
588349
589350 # get attribute from request body
590351 kwargs = {"bucket" : bucket , "obj_id" : req_obj_id }
591- attr_body = await _getAttributeFromRequest (app , body , ** kwargs )
352+ attr_body = await getAttributeFromRequest (app , body , ** kwargs )
592353
593354 # write attribute to DN
594355 attr_json = {attr_name : attr_body }
@@ -625,7 +386,7 @@ async def PUT_Attributes(request):
625386 await validateUserPassword (app , username , pswd )
626387
627388 if not request .has_body :
628- msg = "PUT Attribute with no body"
389+ msg = "PUT Attributes with no body"
629390 log .warn (msg )
630391 raise HTTPBadRequest (reason = msg )
631392 try :
@@ -655,10 +416,10 @@ async def PUT_Attributes(request):
655416 if not req_obj_id :
656417 req_obj_id = domain_json ["root" ]
657418 kwargs = {"obj_id" : req_obj_id , "bucket" : bucket }
658- attr_items = await _getAttributesFromRequest ( request , body , ** kwargs )
419+ attr_items = await getAttributesFromRequest ( app , body , ** kwargs )
659420
660421 if attr_items :
661- log .debug (f"PUT Attribute { len (attr_items )} attibutes to add" )
422+ log .debug (f"PUT Attribute { len (attr_items )} attributes to add" )
662423 else :
663424 log .debug ("no attributes defined yet" )
664425
@@ -667,6 +428,7 @@ async def PUT_Attributes(request):
667428 obj_ids = {}
668429 if "obj_ids" in body :
669430 body_ids = body ["obj_ids" ]
431+
670432 if isinstance (body_ids , list ):
671433 # multi cast the attributes - each attribute in attr-items
672434 # will be written to each of the objects identified by obj_id
@@ -686,7 +448,7 @@ async def PUT_Attributes(request):
686448 msg += f"{ len (obj_ids )} objects"
687449 log .info (msg )
688450 elif isinstance (body_ids , dict ):
689- # each value is body_ids is a set of attriutes to write to the object
451+ # each value is body_ids is a set of attributes to write to the object
690452 # unlike the above case, different attributes can be written to
691453 # different objects
692454 if attr_items :
@@ -702,7 +464,7 @@ async def PUT_Attributes(request):
702464 id_json = body_ids [obj_id ]
703465
704466 kwargs = {"obj_id" : obj_id , "bucket" : bucket }
705- obj_items = await _getAttributesFromRequest ( request , id_json , ** kwargs )
467+ obj_items = await getAttributesFromRequest ( app , id_json , ** kwargs )
706468 if obj_items :
707469 obj_ids [obj_id ] = obj_items
708470
0 commit comments