Skip to content

Commit

Permalink
fix(service): correctly set project_name slug on project create (#1691)
Browse files Browse the repository at this point in the history
  • Loading branch information
jsam committed Nov 5, 2020
1 parent ec0d0a3 commit 234e1b3
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 14 deletions.
4 changes: 2 additions & 2 deletions conftest.py
Expand Up @@ -1036,7 +1036,7 @@ def svc_client_with_templates(svc_client, mock_redis, authentication_headers, te
def svc_client_templates_creation(svc_client_with_templates):
"""Setup and teardown steps for templates tests."""
from renku.core.utils.requests import retry
from renku.core.utils.scm import strip_and_lower
from renku.core.utils.scm import normalize_to_ascii

svc_client, authentication_headers, template = svc_client_with_templates
parameters = []
Expand All @@ -1055,7 +1055,7 @@ def svc_client_templates_creation(svc_client_with_templates):
# clenup by invoking the GitLab delete API
# TODO: consider using the project delete endpoint once implemented
def remove_project():
project_slug = "{0}/{1}".format(payload["project_namespace"], strip_and_lower(payload["project_name"]))
project_slug = "{0}/{1}".format(payload["project_namespace"], normalize_to_ascii(payload["project_name"]))

project_slug_encoded = urllib.parse.quote(project_slug, safe="")
project_delete_url = "{0}/api/v4/projects/{1}".format(payload["project_repository"], project_slug_encoded)
Expand Down
7 changes: 5 additions & 2 deletions renku/core/models/git.py
Expand Up @@ -105,13 +105,16 @@ def __attrs_post_init__(self):

@classmethod
def parse(cls, href):
"""Derive basic informations."""
"""Derive URI components."""
if not href.isascii():
raise UnicodeError(f"`{href}` is not a valid Git remote")

for regex in _REPOSITORY_URLS:
matches = re.search(regex, href)
if matches:
return cls(href=href, regex=regex, **matches.groupdict())
else:
raise errors.ConfigurationError("`{href}` is not a valid Git remote.".format(href=href))
raise errors.ConfigurationError(f"`{href}` is not a valid Git remote")

@property
def image(self):
Expand Down
14 changes: 12 additions & 2 deletions renku/core/utils/scm.py
Expand Up @@ -19,9 +19,19 @@
import re


def strip_and_lower(input):
def normalize_to_ascii(input_string, sep="-"):
"""Adjust chars to make the input compatible as scm source."""
return re.sub(r"\s", r"-", input.strip()).lower()
return (
sep.join(
[
component
for component in re.sub(r"[^a-zA-Z0-9_.-]+", " ", input_string).split(" ")
if component and component.isascii()
]
)
.lower()
.strip(sep)
)


def git_unicode_unescape(s, encoding="utf-8"):
Expand Down
4 changes: 4 additions & 0 deletions renku/service/serializers/cache.py
Expand Up @@ -124,6 +124,8 @@ def validate_git_url(self, value):
"""Validates git url."""
try:
GitURL.parse(value)
except UnicodeError as e:
raise ValidationError("`git_url` contains unsupported characters") from e
except ConfigurationError as e:
raise ValidationError("Invalid `git_url`") from e

Expand All @@ -144,6 +146,8 @@ def set_owner_name(self, data, **kwargs):
"""Set owner and name fields."""
try:
git_url = GitURL.parse(data["git_url"])
except UnicodeError as e:
raise ValidationError("`git_url` contains unsupported characters") from e
except ConfigurationError as e:
raise ValidationError("Invalid `git_url`") from e

Expand Down
14 changes: 10 additions & 4 deletions renku/service/serializers/templates.py
Expand Up @@ -23,7 +23,7 @@

from renku.core.errors import ConfigurationError
from renku.core.models.git import GitURL
from renku.core.utils.scm import strip_and_lower
from renku.core.utils.scm import normalize_to_ascii
from renku.service.config import TEMPLATE_CLONE_DEPTH_DEFAULT
from renku.service.serializers.cache import ProjectCloneContext
from renku.service.serializers.rpc import JsonRPCResponse
Expand All @@ -40,7 +40,6 @@ class ManifestTemplatesRequest(ProjectCloneContext):
def set_git_url(self, data, **kwargs):
"""Set git_url field."""
data["git_url"] = data["url"]

return data


Expand Down Expand Up @@ -68,8 +67,15 @@ class ProjectTemplateRequest(ManifestTemplatesRequest):
@pre_load()
def create_new_project_url(self, data, **kwargs):
"""Set owner and name fields."""
project_name_stripped = strip_and_lower(data["project_name"])
new_project_url = f"{data['project_repository']}/{data['project_namespace']}/{project_name_stripped}"
try:
project_name_stripped = normalize_to_ascii(data["project_name"])
new_project_url = f"{data['project_repository']}/{data['project_namespace']}/{project_name_stripped}"
_ = GitURL.parse(new_project_url)
except UnicodeError as e:
raise ValidationError("`git_url` contains unsupported characters") from e
except ConfigurationError as e:
raise ValidationError("Invalid `git_url`") from e

project_slug = f"{data['project_namespace']}/{project_name_stripped}"
data["new_project_url"] = new_project_url
data["project_name_stripped"] = project_name_stripped
Expand Down
61 changes: 59 additions & 2 deletions tests/service/controllers/test_templates_create_project.py
Expand Up @@ -16,7 +16,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
"""Renku service templates create project controller tests."""
from renku.core.utils.scm import strip_and_lower
import pytest
from marshmallow import ValidationError

from renku.core.utils.scm import normalize_to_ascii


def test_template_create_project_ctrl(ctrl_init, svc_client_templates_creation, mocker):
Expand Down Expand Up @@ -85,6 +88,60 @@ def test_template_create_project_ctrl(ctrl_init, svc_client_templates_creation,
assert payload["project_namespace"] == received_metadata["__namespace__"]
assert payload["project_repository"] == received_metadata["__repository__"]

project_name = strip_and_lower(payload["project_name"])
project_name = normalize_to_ascii(payload["project_name"])
assert project_name == received_metadata["__sanitized_project_name__"]
assert f"{payload['project_namespace']}/{project_name}" == received_metadata["__project_slug__"]


@pytest.mark.parametrize(
"project_name,expected_name",
[
("Test renku-core /é", "test-renku-core"),
("Test renku-core é", "test-renku-core"),
("Test é renku-core ", "test-renku-core"),
("é Test é renku-core ", "test-renku-core"),
("Test/renku-core", "test-renku-core"),
("Test 😁", "test"),
("invalid wörd", "invalid-w-rd"),
("invalid wörd and another invalid wórd", "invalid-w-rd-and-another-invalid-w-rd"),
("João", "jo-o"),
("'My Input String", "my-input-string"),
("My Input String", "my-input-string"),
(" a new project ", "a-new-project"),
("test!_pro-ject~", "test-_pro-ject"),
("test!!!!_pro-ject~", "test-_pro-ject"),
("Test:-)", "test"),
("-Test:-)-", "test"),
],
)
def test_project_name_handler(project_name, expected_name, ctrl_init, svc_client_templates_creation, mocker):
"""Test template create project controller correct set of project name."""
from renku.service.controllers.templates_create_project import TemplatesCreateProjectCtrl

cache, user_data = ctrl_init
svc_client, headers, payload, rm_remote = svc_client_templates_creation
payload["project_name"] = project_name

ctrl = TemplatesCreateProjectCtrl(cache, user_data, payload)
mocker.patch.object(ctrl, "new_project_push", return_value=None)
response = ctrl.to_response()

# Check response.
assert {"result"} == response.json.keys()
assert {"url", "namespace", "name"} == response.json["result"].keys()
assert expected_name == response.json["result"]["name"]


@pytest.mark.parametrize("project_name", ["здрасти"])
def test_except_project_name_handler(project_name, ctrl_init, svc_client_templates_creation, mocker):
"""Test template create project controller exception raised."""
from renku.service.controllers.templates_create_project import TemplatesCreateProjectCtrl

cache, user_data = ctrl_init
svc_client, headers, payload, rm_remote = svc_client_templates_creation
payload["project_name"] = project_name

with pytest.raises(ValidationError) as exc_info:
TemplatesCreateProjectCtrl(cache, user_data, payload)

assert "Invalid `git_url`" in str(exc_info.value)
4 changes: 2 additions & 2 deletions tests/service/views/test_templates_views.py
Expand Up @@ -25,7 +25,7 @@
from flaky import flaky

from renku.core.commands.init import fetch_template_from_git, read_template_manifest
from renku.core.utils.scm import strip_and_lower
from renku.core.utils.scm import normalize_to_ascii
from renku.service.config import RENKU_EXCEPTION_ERROR_CODE


Expand Down Expand Up @@ -109,7 +109,7 @@ def test_create_project_from_template(svc_client_templates_creation):

assert response
assert {"result"} == set(response.json.keys())
stripped_name = strip_and_lower(payload["project_name"])
stripped_name = normalize_to_ascii(payload["project_name"])
assert stripped_name == response.json["result"]["name"]
expected_url = "{0}/{1}/{2}".format(payload["project_repository"], payload["project_namespace"], stripped_name,)
assert expected_url == response.json["result"]["url"]
Expand Down

0 comments on commit 234e1b3

Please sign in to comment.