Skip to content
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

Mbeliaev/update props #298

Merged
merged 2 commits into from
Oct 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 90 additions & 12 deletions artifactory.py
Original file line number Diff line number Diff line change
Expand Up @@ -730,6 +730,38 @@ def rest_del(url, params=None, session=None, verify=True, cert=None, timeout=Non
raise_for_status(response)
return response

@staticmethod
def rest_patch(
url,
json_data=None,
params=None,
session=None,
verify=True,
cert=None,
timeout=None,
):
"""
Perform a PATCH request to url with requests.session
:param url: url
:param json_data: (dict) JSON data to attach to patch request
:param params: request parameters
:param session:
:param verify:
:param cert:
:param timeout:
:return: request response object
"""
url = quote_url(url)
response = session.patch(
url=url,
json=json_data,
params=params,
verify=verify,
cert=cert,
timeout=timeout,
)
return response

@staticmethod
def rest_put_stream(
url,
Expand Down Expand Up @@ -1270,6 +1302,41 @@ def del_properties(self, pathobj, props, recursive):
timeout=pathobj.timeout,
)

def update_properties(self, pathobj, properties, recursive=False):
"""
Update item properties

Args:
pathobj: (ArtifactoryPath) object
properties: (dict) properties
recursive: (bool) apply recursively or not. For folders

Returns: None
"""
url = "/".join(
[
pathobj.drive.rstrip("/"),
"api/metadata",
str(pathobj.relative_to(pathobj.drive)).strip("/"),
]
)

# construct data according to Artifactory format
json_data = {"props": properties}

params = {"recursive": int(recursive)}

response = self.rest_patch(
url,
json_data=json_data,
params=params,
session=pathobj.session,
verify=pathobj.verify,
cert=pathobj.cert,
timeout=pathobj.timeout,
)
raise_for_status(response)

def scandir(self, pathobj):
return _ScandirIter((pathobj.joinpath(x) for x in self.listdir(pathobj)))

Expand Down Expand Up @@ -1983,12 +2050,15 @@ def properties(self):
@properties.setter
def properties(self, properties):
properties_to_remove = set(self.properties) - set(properties)
if properties_to_remove:
self.del_properties(properties_to_remove, recursive=False)
self.set_properties(properties, recursive=False)
for prop in properties_to_remove:
properties[prop] = None
self.update_properties(properties=properties, recursive=False)

@properties.deleter
def properties(self):
"""
Delete properties
"""
self.del_properties(self.properties, recursive=False)

def set_properties(self, properties, recursive=True):
Expand All @@ -2004,15 +2074,10 @@ def set_properties(self, properties, recursive=True):
if not properties:
return

# If URL > 13KB, nginx default raise error '414 Request-URI Too Large'
MAX_SIZE = 50
if len(properties) > MAX_SIZE:
for chunk in chunks(properties, MAX_SIZE):
self._accessor.set_properties(self, chunk, recursive)
else:
self._accessor.set_properties(self, properties, recursive)
# Uses update properties since it can consume JSON as input and removes URL limit
self.update_properties(properties, recursive=recursive)

def del_properties(self, properties, recursive=None):
def del_properties(self, properties, recursive=False):
"""
Delete properties listed in properties

Expand All @@ -2021,7 +2086,20 @@ def del_properties(self, properties, recursive=None):
recursive - on folders property attachment is recursive by default. It is
possible to force recursive behavior.
"""
return self._accessor.del_properties(self, properties, recursive)
properties_to_remove = dict.fromkeys(properties, None)
# Uses update properties since it can consume JSON as input and removes URL limit
self.update_properties(properties_to_remove, recursive=recursive)

def update_properties(self, properties, recursive=False):
"""
Update properties, set/update/remove item or folder properties
Args:
properties: (dict) data to be set
recursive: (bool) recursive on folder

Returns: None
"""
return self._accessor.update_properties(self, properties, recursive)

def aql(self, *args):
"""
Expand Down
2 changes: 1 addition & 1 deletion requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ flake8
pre-commit
tox
PyJWT
responses
responses>=0.14.0
48 changes: 24 additions & 24 deletions tests/unit/test_artifactory_path.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

import dateutil
import responses
from responses.matchers import json_params_matcher
from responses.matchers import query_param_matcher

import artifactory
from artifactory import ArtifactoryPath
Expand Down Expand Up @@ -667,17 +669,22 @@ def test_set_properties(self):
}

path = self._mock_properties_response()
path.properties = properties

# Must delete only removed property
# rest delete is a second call, use index 1
self.assertEqual(responses.calls[1].request.params["properties"], "removethis")
resp_props = properties.copy()
resp_props["removethis"] = None
self.assertNotEqual(
properties, resp_props
) # ensure not update original properties

# Must put all property
self.assertEqual(
responses.calls[2].request.params["properties"],
"addthis=addthis;test=test_property;time=2018-01-16 12:17:44.135143",
responses.add(
responses.PATCH,
url="http://artifactory.local/artifactory/api/metadata/ext-release-local/org/company/tool/1.0/tool-1.0.tar.gz",
match=[
json_params_matcher({"props": resp_props}),
query_param_matcher({"recursive": "0"}),
],
)
path.properties = properties

@responses.activate
def test_set_properties_without_remove(self):
Expand All @@ -695,12 +702,17 @@ def test_set_properties_without_remove(self):
}

path = self._mock_properties_response()
path.properties = properties
self.assertEqual(
responses.calls[1].request.params["properties"],
"addthis=addthis;removethis=removethis_property;test=test_property;time=2018-01-16 12:17:44.135143",
responses.add(
responses.PATCH,
url="http://artifactory.local/artifactory/api/metadata/ext-release-local/org/company/tool/1.0/tool-1.0.tar.gz",
match=[
json_params_matcher({"props": properties}),
query_param_matcher({"recursive": "0"}),
],
)

path.properties = properties

@staticmethod
def _mock_properties_response():
"""
Expand Down Expand Up @@ -730,18 +742,6 @@ def _mock_properties_response():
"uri": constructed_url,
},
)
responses.add(
responses.DELETE,
constructed_url,
status=204,
body="",
)
responses.add(
responses.PUT,
constructed_url,
status=204,
body="",
)
return path

@responses.activate
Expand Down