From d2e5b62dce727fe3eaa7cdfb28267e18907b45b0 Mon Sep 17 00:00:00 2001 From: Sumanth V Rao Date: Tue, 27 Oct 2020 06:51:22 +0530 Subject: [PATCH] realm/playground: Add API endpoint for deleting a playground entry. Similar to the previous commit, we have added a `do_*` function which does the deletion from the DB. The next commit handles sending the events when both adding and deleting a playground entry. Added the openAPI format data to zulip.yaml for DELETE /realm/playgrounds/{playground_id}. Also added python and curl examples to remove-playground.md. Tests added. --- templates/zerver/api/remove-playground.md | 28 +++++++++++++++++++ .../zerver/help/include/rest-endpoints.md | 1 + zerver/lib/actions.py | 4 +++ zerver/openapi/curl_param_value_generators.py | 14 ++++++++++ zerver/openapi/python_examples.py | 12 ++++++++ zerver/openapi/zulip.yaml | 25 +++++++++++++++++ zerver/tests/test_realm_playgrounds.py | 23 +++++++++++++++ zerver/views/realm_playgrounds.py | 22 +++++++++++++-- zproject/urls.py | 3 +- 9 files changed, 129 insertions(+), 3 deletions(-) create mode 100644 templates/zerver/api/remove-playground.md diff --git a/templates/zerver/api/remove-playground.md b/templates/zerver/api/remove-playground.md new file mode 100644 index 0000000000..03c3389a18 --- /dev/null +++ b/templates/zerver/api/remove-playground.md @@ -0,0 +1,28 @@ +# Remove a playground + +{generate_api_description(/realm/playgrounds/{playground_id}:delete)} + +## Usage examples + +{start_tabs} +{tab|python} + +{generate_code_example(python)|/realm/playgrounds/{playground_id}:delete|example} + +{tab|curl} + +{generate_code_example(curl)|/realm/playgrounds/{playground_id}:delete|example} + +{end_tabs} + +## Parameters + +{generate_api_arguments_table|zulip.yaml|/realm/playgrounds/{playground_id}:delete} + +## Response + +#### Example response + +A typical successful JSON response may look like: + +{generate_code_example|/realm/playgrounds/{playground_id}:delete|fixture(200)} diff --git a/templates/zerver/help/include/rest-endpoints.md b/templates/zerver/help/include/rest-endpoints.md index e358127cf9..cd3af218e9 100644 --- a/templates/zerver/help/include/rest-endpoints.md +++ b/templates/zerver/help/include/rest-endpoints.md @@ -60,6 +60,7 @@ * [Add a linkifier](/api/add-linkifier) * [Remove a linkifier](/api/remove-linkifier) * [Add a playground](/api/add-playground) +* [Remove a playground](/api/remove-playground) * [Get all custom emoji](/api/get-custom-emoji) * [Upload custom emoji](/api/upload-custom-emoji) * [Get all custom profile fields](/api/get-custom-profile-fields) diff --git a/zerver/lib/actions.py b/zerver/lib/actions.py index 91b1bed29b..4d629246b2 100644 --- a/zerver/lib/actions.py +++ b/zerver/lib/actions.py @@ -6602,6 +6602,10 @@ def do_add_realm_playground(realm: Realm, **kwargs: Any) -> int: return realm_playground.id +def do_remove_realm_playground(realm_playground: RealmPlayground) -> None: + realm_playground.delete() + + def get_occupied_streams(realm: Realm) -> QuerySet: # TODO: Make a generic stub for QuerySet """ Get streams with subscribers """ diff --git a/zerver/openapi/curl_param_value_generators.py b/zerver/openapi/curl_param_value_generators.py index daa4dc34d1..e21385c74c 100644 --- a/zerver/openapi/curl_param_value_generators.py +++ b/zerver/openapi/curl_param_value_generators.py @@ -14,6 +14,7 @@ from django.utils.timezone import now as timezone_now from zerver.lib.actions import ( do_add_linkifier, do_add_reaction, + do_add_realm_playground, do_create_user, update_user_presence, ) @@ -277,6 +278,19 @@ def add_realm_playground() -> Dict[str, object]: } +@openapi_param_value_generator(["/realm/playgrounds/{playground_id}:delete"]) +def remove_realm_playground() -> Dict[str, object]: + playground_info = dict( + name="Python playground", + pygments_language="Python", + url_prefix="https://python.example.com", + ) + playground_id = do_add_realm_playground(get_realm("zulip"), **playground_info) + return { + "playground_id": playground_id, + } + + @openapi_param_value_generator(["/users/{user_id}:delete"]) def deactivate_user() -> Dict[str, object]: user_profile = do_create_user( diff --git a/zerver/openapi/python_examples.py b/zerver/openapi/python_examples.py index 43069c0b0a..6970fe028f 100644 --- a/zerver/openapi/python_examples.py +++ b/zerver/openapi/python_examples.py @@ -396,6 +396,17 @@ def add_realm_playground(client: Client) -> None: validate_against_openapi_schema(result, "/realm/playgrounds", "post", "200") +@openapi_test_function("/realm/playgrounds/{playground_id}:delete") +def remove_realm_playground(client: Client) -> None: + + # {code_example|start} + # Remove the playground with ID 1 + result = client.call_endpoint(url="/realm/playgrounds/1", method="DELETE") + # {code_example|end} + + validate_against_openapi_schema(result, "/realm/playgrounds/{playground_id}", "delete", "200") + + @openapi_test_function("/users/me:get") def get_profile(client: Client) -> None: @@ -1436,6 +1447,7 @@ def test_server_organizations(client: Client) -> None: add_realm_playground(client) get_server_settings(client) remove_realm_filter(client) + remove_realm_playground(client) get_realm_emoji(client) upload_custom_emoji(client) get_realm_profile_fields(client) diff --git a/zerver/openapi/zulip.yaml b/zerver/openapi/zulip.yaml index 1857ddcea5..26e928bd22 100644 --- a/zerver/openapi/zulip.yaml +++ b/zerver/openapi/zulip.yaml @@ -6621,6 +6621,31 @@ paths: description: | The numeric ID assigned to this playground. example: {"id": 1, "result": "success", "msg": ""} + /realm/playgrounds/{playground_id}: + delete: + operationId: remove_realm_playground + tags: ["server_and_organizations"] + description: | + Remove realm playground options to run code snippets in + custom playgrounds + + `DELETE {{ api_url }}/v1/realm/playgrounds/{playground_id}` + parameters: + - name: playground_id + in: path + description: | + The ID of the playground that you want to remove. + schema: + type: integer + example: 1 + required: true + responses: + "200": + description: Success. + content: + application/json: + schema: + $ref: "#/components/schemas/JsonSuccess" /register: post: operationId: register_queue diff --git a/zerver/tests/test_realm_playgrounds.py b/zerver/tests/test_realm_playgrounds.py index 7ec57a5e4c..ac2b4244a2 100644 --- a/zerver/tests/test_realm_playgrounds.py +++ b/zerver/tests/test_realm_playgrounds.py @@ -2,6 +2,7 @@ from typing import Dict import orjson +from zerver.lib.actions import do_add_realm_playground from zerver.lib.test_classes import ZulipTestCase from zerver.models import RealmPlayground, get_realm @@ -93,3 +94,25 @@ class RealmPlaygroundTests(ZulipTestCase): resp = self.api_post(hamlet, "/json/realm/playgrounds") self.assert_json_error(resp, "Must be an organization administrator") + + resp = self.api_delete(hamlet, "/json/realm/playgrounds/1") + self.assert_json_error(resp, "Must be an organization administrator") + + def test_delete_realm_playground(self) -> None: + iago = self.example_user("iago") + realm = get_realm("zulip") + + playground_info = dict( + name="Python playground", + pygments_language="Python", + url_prefix="https://python.example.com", + ) + playground_id = do_add_realm_playground(realm, **playground_info) + self.assertTrue(RealmPlayground.objects.filter(name="Python playground").exists()) + + result = self.api_delete(iago, f"/json/realm/playgrounds/{playground_id + 1}") + self.assert_json_error(result, "Invalid playground") + + result = self.api_delete(iago, f"/json/realm/playgrounds/{playground_id}") + self.assert_json_success(result) + self.assertFalse(RealmPlayground.objects.filter(name="Python").exists()) diff --git a/zerver/views/realm_playgrounds.py b/zerver/views/realm_playgrounds.py index 62cef4e0de..de3694474e 100644 --- a/zerver/views/realm_playgrounds.py +++ b/zerver/views/realm_playgrounds.py @@ -5,11 +5,11 @@ from django.http import HttpRequest, HttpResponse from django.utils.translation import ugettext as _ from zerver.decorator import require_realm_admin -from zerver.lib.actions import do_add_realm_playground +from zerver.lib.actions import do_add_realm_playground, do_remove_realm_playground from zerver.lib.request import REQ, JsonableError, has_request_variables from zerver.lib.response import json_error, json_success from zerver.lib.validator import check_capped_string, check_url -from zerver.models import RealmPlayground, UserProfile +from zerver.models import Realm, RealmPlayground, UserProfile def check_pygments_language(var_name: str, val: object) -> str: @@ -25,6 +25,14 @@ def check_pygments_language(var_name: str, val: object) -> str: return s +def access_playground_by_id(realm: Realm, playground_id: int) -> RealmPlayground: + try: + realm_playground = RealmPlayground.objects.get(id=playground_id, realm=realm) + except RealmPlayground.DoesNotExist: + raise JsonableError(_("Invalid playground")) + return realm_playground + + @require_realm_admin @has_request_variables def add_realm_playground( @@ -44,3 +52,13 @@ def add_realm_playground( except ValidationError as e: return json_error(e.messages[0], data={"errors": dict(e)}) return json_success({"id": playground_id}) + + +@require_realm_admin +@has_request_variables +def delete_realm_playground( + request: HttpRequest, user_profile: UserProfile, playground_id: int +) -> HttpResponse: + realm_playground = access_playground_by_id(user_profile.realm, playground_id) + do_remove_realm_playground(realm_playground) + return json_success() diff --git a/zproject/urls.py b/zproject/urls.py index 36d866e392..e70706749f 100644 --- a/zproject/urls.py +++ b/zproject/urls.py @@ -121,7 +121,7 @@ from zerver.views.realm_export import delete_realm_export, export_realm, get_rea from zerver.views.realm_icon import delete_icon_backend, get_icon_backend, upload_icon from zerver.views.realm_linkifiers import create_linkifier, delete_linkifier, list_linkifiers from zerver.views.realm_logo import delete_logo_backend, get_logo_backend, upload_logo -from zerver.views.realm_playgrounds import add_realm_playground +from zerver.views.realm_playgrounds import add_realm_playground, delete_realm_playground from zerver.views.registration import ( accounts_home, accounts_home_from_multiuse_invite, @@ -271,6 +271,7 @@ v1_api_and_json_patterns = [ rest_path("realm/filters/", DELETE=delete_linkifier), # realm/playgrounds -> zerver.views.realm_playgrounds rest_path("realm/playgrounds", POST=add_realm_playground), + rest_path("realm/playgrounds/", DELETE=delete_realm_playground), # realm/profile_fields -> zerver.views.custom_profile_fields rest_path( "realm/profile_fields",