-
-
Notifications
You must be signed in to change notification settings - Fork 54
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Multiops #295
Conversation
new_attribute = False # set this if we have any new attributes | ||
for attr_name in items: | ||
attribute = items[attr_name] | ||
if attr_name in attributes: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
attributes
should have a name that makes it clear that it's a list of attributes that already exist/are already in storage. Maybe retrieved_attributes
? This would also be good for attr_dict
in POST_Attributes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The attributes are the current set of attributes, so I think I'll keep it the way it is. Realize there's potential for confusion in other methods - will review.
self._objs = None | ||
|
||
|
||
async def get_attributes(self, obj_id, attr_names): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this be merged with _get_attributes
from attr_sn
? The domain crawler self
seems like it contains the app
and parameter fields needed for _get_attributes
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea. Created a getAttributes function in servicenode_lib.py that both attr_sn and DomainCrawl call.
Here is a list of cases where either HSDS or the client can attempt to use an overlong URL due to attributes or links having long names: Client request is safe, but can cause HSDS to use an overlong URL
Both client request and resultant HSDS request can use an overlong URL
These API endpoints expect a potentially overlong
The HDF5 Library doesn't formally specify an attribute name limit, and only attribute names up to 255 bytes are tested. For this reason, I think we can safely consider attribute names that cause a URL to exceed aiohttp's default limit (8190 bytes) to be unsupported. In that case, we wouldn't need to worry about changing The changes that would need to be made to avoid all other potentially overlong URLs:
The latter two changes don't seem like perfect ideas - moving all the GET functionality under POST/PUT could be be unintuitive or duplicate behavior. Some other possible solutions to handle the long URL cases in
|
hsds/servicenode_lib.py
Outdated
): | ||
""" get the requested set of attributes from the given object """ | ||
if attr_names is None: | ||
# TBD - fetch all attributes is this is None? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should fetch all attributes to match the commented out test in attr_test.py
tests/integ/attr_test.py
Outdated
@@ -1703,7 +1704,14 @@ def testPostAttributeMultiple(self): | |||
self.assertEqual(len(root_attrs), 2) | |||
dset_attrs = attributes[dset_id] | |||
self.assertEqual(len(dset_attrs), 1) # one of the ones we asked for didn't exist | |||
|
|||
|
|||
# test with no providing all attribute names - should return all attributes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be enabled or removed
tests/integ/attr_test.py
Outdated
for grp_id in grp_ids: | ||
for i in range(grp_count): | ||
grp_id = grp_ids[i] | ||
print(f"grp_id: {grp_id}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove print statement
tests/integ/attr_test.py
Outdated
domain=self.base_domain, username="test_user2" | ||
) | ||
headers = helper.getRequestHeaders(domain=self.base_domain, username="test_user2") | ||
attr_payload = {"type": "H5T_STD_I32LE", "value": 42} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test fails due to attr_payload
being undefined if user2_name
isn't found in the config - this should either have a default second username, or the requests should only be made if a second username is found.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
hsds/attr_dn.py
Outdated
else: | ||
encoding = None | ||
|
||
if "seperator" in params: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seperator
-> separator
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
hsds/attr_sn.py
Outdated
else: | ||
encoding = None | ||
|
||
if "seperator" in params: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seperator
-> separator
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
hsds/servicenode_lib.py
Outdated
|
||
|
||
async def deleteAttributes(app, obj_id, attr_names=None, seperator="/", bucket=None): | ||
""" get the requested set of attributes from the given object """ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Description should be delete the requested set of attributes from the given object
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
attr_names_param = base64.b64encode(attr_name.encode("utf8")).decode("ascii") | ||
# specify a seperator since our attribute name has the default slash | ||
params = {"attr_names": attr_names_param, "encoding": "base64", "seperator": "!"} | ||
rsp = self.session.delete(req, params=params, headers=headers) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should include a test that deletes multiple attributes at once through DELETE_Attributes
. I wrote the following while testing the API:
def testDeleteAttributesMultiple(self):
print("testDeleteAttributesMultiple", self.base_domain)
headers = helper.getRequestHeaders(domain=self.base_domain)
req = self.endpoint + "/"
# Get root uuid
rsp = self.session.get(req, headers=headers)
self.assertEqual(rsp.status_code, 200)
rspJson = json.loads(rsp.text)
root_uuid = rspJson["root"]
helper.validateId(root_uuid)
# create a subgroup
req = self.endpoint + "/groups"
rsp = self.session.post(req, headers=headers)
self.assertEqual(rsp.status_code, 201)
rspJson = json.loads(rsp.text)
grp_id = rspJson["id"]
self.assertTrue(helper.validateId(grp_id))
# link as "grp1"
grp_name = "grp1"
req = self.endpoint + "/groups/" + root_uuid + "/links/" + grp_name
payload = {"id": grp_id}
rsp = self.session.put(req, data=json.dumps(payload), headers=headers)
self.assertEqual(rsp.status_code, 201) # created
# Create attributes
for i in range(10):
attr_name = "attr" + str(i * 100)
value = [i]
data = {"type": "H5T_IEEE_F32LE", "shape": 1, "value": value}
req = self.endpoint + "/groups/" + grp_id + "/attributes/" + attr_name
rsp = self.session.put(req, data=json.dumps(data), headers=headers)
self.assertEqual(rsp.status_code, 201) # Created
# Delete all by parameter
attr_names = ["attr" + str(i * 100) for i in range(10)]
separator = '/'
req = self.endpoint + "/groups/" + grp_id + "/attributes?attr_names=" + separator.join(attr_names)
rsp = self.session.delete(req, headers=headers)
self.assertEqual(rsp.status_code, 200)
# Attempt to read deleted attributes
for i in range(10):
req = self.endpoint + "/groups/" + grp_id + "/attributes/" + "attr" + str(i)
rsp = self.session.get(req, headers=headers)
self.assertEqual(rsp.status_code, 404)
# Create another batch of attributes
for i in range(10):
attr_name = "attr" + str(i * 100)
value = [i]
data = {"type": "H5T_IEEE_F32LE", "shape": 1, "value": value}
req = self.endpoint + "/groups/" + grp_id + "/attributes/" + attr_name
rsp = self.session.put(req, data=json.dumps(data), headers=headers)
self.assertEqual(rsp.status_code, 201) # Created
# Delete with custom separator
attr_names = ["attr" + str(i * 100) for i in range(10)]
separator = ':'
req = self.endpoint + "/groups/" + grp_id + "/attributes?attr_names=" + separator.join(attr_names) + "&seperator=" + separator
rsp = self.session.delete(req, headers=headers)
self.assertEqual(rsp.status_code, 200)
# Attempt to read
for i in range(10):
req = self.endpoint + "/groups/" + grp_id + "/attributes/" + "attr" + str(i)
rsp = self.session.get(req, headers=headers)
self.assertEqual(rsp.status_code, 404)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea. Added test
At this point, there are four different ways to "get the value of an attribute" - |
Add ability to get multiple attributes via PUT attributes or write multiple attributes with POST attributes.