mirror of
https://github.com/zulip/zulip.git
synced 2025-11-16 03:41:58 +00:00
realm_playgrounds: Add url_template field.
As an intermediate step before we fully support url_template for realm playgrounds, we populate url_template in the backend ensuring that all the new entries will be validated. With a later backfilling migration, we prepare the database such that all the records will have a valid URL template. Signed-off-by: Zixuan James Li <p359101898@gmail.com>
This commit is contained in:
committed by
Tim Abbott
parent
131729a06c
commit
641f60305d
@@ -31,7 +31,11 @@ def do_add_realm_playground(
|
|||||||
url_prefix: str,
|
url_prefix: str,
|
||||||
) -> int:
|
) -> int:
|
||||||
realm_playground = RealmPlayground(
|
realm_playground = RealmPlayground(
|
||||||
realm=realm, name=name, pygments_language=pygments_language, url_prefix=url_prefix
|
realm=realm,
|
||||||
|
name=name,
|
||||||
|
pygments_language=pygments_language,
|
||||||
|
url_prefix=url_prefix,
|
||||||
|
url_template=url_prefix + "{code}",
|
||||||
)
|
)
|
||||||
# We expect full_clean to always pass since a thorough input validation
|
# We expect full_clean to always pass since a thorough input validation
|
||||||
# is performed in the view (using check_url, check_pygments_language, etc)
|
# is performed in the view (using check_url, check_pygments_language, etc)
|
||||||
@@ -68,6 +72,7 @@ def do_remove_realm_playground(
|
|||||||
"name": realm_playground.name,
|
"name": realm_playground.name,
|
||||||
"pygments_language": realm_playground.pygments_language,
|
"pygments_language": realm_playground.pygments_language,
|
||||||
"url_prefix": realm_playground.url_prefix,
|
"url_prefix": realm_playground.url_prefix,
|
||||||
|
"url_template": realm_playground.url_template,
|
||||||
}
|
}
|
||||||
|
|
||||||
realm_playground.delete()
|
realm_playground.delete()
|
||||||
|
|||||||
19
zerver/migrations/0462_realmplayground_url_template.py
Normal file
19
zerver/migrations/0462_realmplayground_url_template.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Generated by Django 4.2.1 on 2023-05-27 00:06
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
from zerver.models import url_template_validator
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("zerver", "0461_alter_realm_default_code_block_language"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="realmplayground",
|
||||||
|
name="url_template",
|
||||||
|
field=models.TextField(null=True, validators=[url_template_validator]),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -1366,6 +1366,7 @@ class RealmPlayground(models.Model):
|
|||||||
|
|
||||||
realm = models.ForeignKey(Realm, on_delete=CASCADE)
|
realm = models.ForeignKey(Realm, on_delete=CASCADE)
|
||||||
url_prefix = models.TextField(validators=[URLValidator()])
|
url_prefix = models.TextField(validators=[URLValidator()])
|
||||||
|
url_template = models.TextField(validators=[url_template_validator], null=True)
|
||||||
|
|
||||||
# User-visible display name used when configuring playgrounds in the settings page and
|
# User-visible display name used when configuring playgrounds in the settings page and
|
||||||
# when displaying them in the playground links popover.
|
# when displaying them in the playground links popover.
|
||||||
@@ -1390,6 +1391,38 @@ class RealmPlayground(models.Model):
|
|||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"{self.realm.string_id}: {self.pygments_language} {self.name}"
|
return f"{self.realm.string_id}: {self.pygments_language} {self.name}"
|
||||||
|
|
||||||
|
def clean(self) -> None:
|
||||||
|
"""Validate whether the URL template is valid for the playground,
|
||||||
|
ensuring that "code" is the sole variable present in it.
|
||||||
|
|
||||||
|
Django's `full_clean` calls `clean_fields` followed by `clean` method
|
||||||
|
and stores all ValidationErrors from all stages to return as JSON.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Prior to the completion of this migration, we make url_template nullable,
|
||||||
|
# while ensuring that no code path will create a RealmPlayground without populating it.
|
||||||
|
assert self.url_template is not None
|
||||||
|
|
||||||
|
# Do not continue the check if the url template is invalid to begin with.
|
||||||
|
# The ValidationError for invalid template will only be raised by the validator
|
||||||
|
# set on the url_template field instead of here to avoid duplicates.
|
||||||
|
if not uri_template.validate(self.url_template):
|
||||||
|
return
|
||||||
|
|
||||||
|
# Extract variables used in the URL template.
|
||||||
|
template_variables = set(uri_template.URITemplate(self.url_template).variable_names)
|
||||||
|
|
||||||
|
if (
|
||||||
|
"code" not in template_variables
|
||||||
|
): # nocoverage: prior to the completion of the migration, it is impossible to generate a URL template without the "code" variable
|
||||||
|
raise ValidationError(_('Missing the required variable "code" in the URL template'))
|
||||||
|
|
||||||
|
# The URL template should only contain a single variable, which is "code".
|
||||||
|
if len(template_variables) != 1:
|
||||||
|
raise ValidationError(
|
||||||
|
_('"code" should be the only variable present in the URL template'),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_realm_playgrounds(realm: Realm) -> List[RealmPlaygroundDict]:
|
def get_realm_playgrounds(realm: Realm) -> List[RealmPlaygroundDict]:
|
||||||
playgrounds: List[RealmPlaygroundDict] = []
|
playgrounds: List[RealmPlaygroundDict] = []
|
||||||
|
|||||||
@@ -900,6 +900,7 @@ class TestRealmAuditLog(ZulipTestCase):
|
|||||||
"name": "Python playground",
|
"name": "Python playground",
|
||||||
"pygments_language": "Python",
|
"pygments_language": "Python",
|
||||||
"url_prefix": "https://python.example.com",
|
"url_prefix": "https://python.example.com",
|
||||||
|
"url_template": "https://python.example.com{code}",
|
||||||
}
|
}
|
||||||
expected_extra_data = {
|
expected_extra_data = {
|
||||||
"realm_playgrounds": initial_playgrounds,
|
"realm_playgrounds": initial_playgrounds,
|
||||||
|
|||||||
@@ -65,6 +65,24 @@ class RealmPlaygroundTests(ZulipTestCase):
|
|||||||
resp = self.api_post(iago, "/api/v1/realm/playgrounds", payload)
|
resp = self.api_post(iago, "/api/v1/realm/playgrounds", payload)
|
||||||
self.assert_json_error(resp, "Invalid characters in pygments language")
|
self.assert_json_error(resp, "Invalid characters in pygments language")
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
"name": "Template with an unexpected variable",
|
||||||
|
"pygments_language": "Python",
|
||||||
|
"url_prefix": "https://template.com?test={test}",
|
||||||
|
}
|
||||||
|
resp = self.api_post(iago, "/api/v1/realm/playgrounds", payload)
|
||||||
|
self.assert_json_error(
|
||||||
|
resp, '"code" should be the only variable present in the URL template'
|
||||||
|
)
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
"name": "Invalid URL template",
|
||||||
|
"pygments_language": "Python",
|
||||||
|
"url_prefix": "https://template.com?test={test",
|
||||||
|
}
|
||||||
|
resp = self.api_post(iago, "/api/v1/realm/playgrounds", payload)
|
||||||
|
self.assert_json_error(resp, "Invalid URL template.")
|
||||||
|
|
||||||
def test_create_already_existing_playground(self) -> None:
|
def test_create_already_existing_playground(self) -> None:
|
||||||
iago = self.example_user("iago")
|
iago = self.example_user("iago")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user