From 8b6e53d232b2bf311341747bf2f3a55d5f563587 Mon Sep 17 00:00:00 2001 From: Matthew Lynch Date: Tue, 29 Aug 2023 16:48:21 -0500 Subject: [PATCH 1/5] use get application API when calling get_rstudio_app_info --- rsconnect/api.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/rsconnect/api.py b/rsconnect/api.py index beeaaedf..60370c92 100644 --- a/rsconnect/api.py +++ b/rsconnect/api.py @@ -52,6 +52,7 @@ def handle_bad_response(self, response): response.full_uri, response.json_data["error"], ) + raise Exception(error) raise RSConnectException(error) if response.status < 200 or response.status > 299: raise RSConnectException( @@ -249,7 +250,7 @@ def deploy(self, app_id, app_name, app_title, title_is_default, tarball, env_var try: self._server.handle_bad_response(app) except RSConnectException as e: - raise RSConnectException(f"{e} Try setting the --new flag to overwrite the previous deployment.") + raise RSConnectException(f"{e} Try setting the --new flag to overwrite the previous deployment.") from e app_guid = app["guid"] if env_vars: @@ -847,7 +848,7 @@ def validate_app_mode(self, *args, **kwargs): except RSConnectException as e: raise RSConnectException( f"{e} Try setting the --new flag to overwrite the previous deployment." - ) + ) from e elif isinstance(self.remote_server, PositServer): try: app = get_rstudio_app_info(self.remote_server, app_id) @@ -855,7 +856,7 @@ def validate_app_mode(self, *args, **kwargs): except RSConnectException as e: raise RSConnectException( f"{e} Try setting the --new flag to overwrite the previous deployment." - ) + ) from e else: raise RSConnectException("Unable to infer Connect client.") if existing_app_mode and existing_app_mode not in (None, AppModes.UNKNOWN, app_mode): @@ -1485,8 +1486,11 @@ def get_app_info(connect_server, app_id): def get_rstudio_app_info(server, app_id): with PositClient(server) as client: - response = client.get_content(app_id) - return response["source"] + if isinstance(server, ShinyappsServer): + return client.get_application(app_id) + else: + response = client.get_content(app_id) + return response["source"] def get_app_config(connect_server, app_id): From 9e21738b5020ec16974f1117819e5e8d3abb9c90 Mon Sep 17 00:00:00 2001 From: Matthew Lynch Date: Tue, 29 Aug 2023 16:51:21 -0500 Subject: [PATCH 2/5] remove extraneous exception --- rsconnect/api.py | 1 - 1 file changed, 1 deletion(-) diff --git a/rsconnect/api.py b/rsconnect/api.py index 60370c92..c004958d 100644 --- a/rsconnect/api.py +++ b/rsconnect/api.py @@ -52,7 +52,6 @@ def handle_bad_response(self, response): response.full_uri, response.json_data["error"], ) - raise Exception(error) raise RSConnectException(error) if response.status < 200 or response.status > 299: raise RSConnectException( From b887d51c53068b684432eefa82ac14918b0e7530 Mon Sep 17 00:00:00 2001 From: Matthew Lynch Date: Tue, 5 Sep 2023 11:28:55 -0500 Subject: [PATCH 3/5] add test for redeploying to shinyapps.io --- tests/test_main.py | 168 +++++++++++++++++- ...eate-application.json => application.json} | 0 2 files changed, 167 insertions(+), 1 deletion(-) rename tests/testdata/rstudio-responses/{create-application.json => application.json} (100%) diff --git a/tests/test_main.py b/tests/test_main.py index 9d159bd4..ff73604b 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -140,7 +140,7 @@ def post_application_callback(request, uri, response_headers): return [ 201, {"Content-Type": "application/json"}, - open("tests/testdata/rstudio-responses/create-application.json", "r").read(), + open("tests/testdata/rstudio-responses/application.json", "r").read(), ] httpretty.register_uri( @@ -278,6 +278,172 @@ def post_deploy_callback(request, uri, response_headers): if original_server_value: os.environ["CONNECT_SERVER"] = original_server_value + @httpretty.activate(verbose=True, allow_net_connect=False) + def test_deploy_manifest_shinyapps(self): + original_api_key_value = os.environ.pop("CONNECT_API_KEY", None) + original_server_value = os.environ.pop("CONNECT_SERVER", None) + + httpretty.register_uri( + httpretty.GET, + "https://api.shinyapps.io/v1/users/me", + body=open("tests/testdata/rstudio-responses/get-user.json", "r").read(), + status=200, + ) + httpretty.register_uri( + httpretty.GET, + "https://api.shinyapps.io/v1/applications" + "?filter=name:like:shinyapp&offset=0&count=100&use_advanced_filters=true", + body=open("tests/testdata/rstudio-responses/get-applications.json", "r").read(), + adding_headers={"Content-Type": "application/json"}, + status=200, + ) + httpretty.register_uri( + httpretty.GET, + "https://api.shinyapps.io/v1/accounts/", + body=open("tests/testdata/rstudio-responses/get-accounts.json", "r").read(), + adding_headers={"Content-Type": "application/json"}, + status=200, + ) + + httpretty.register_uri( + httpretty.GET, + "https://api.shinyapps.io/v1/applications/8442", + body=open("tests/testdata/rstudio-responses/application.json", "r").read(), + adding_headers={"Content-Type": "application/json"}, + status=200, + ) + + def post_application_property_callback(request, uri, response_headers): + parsed_request = _load_json(request.body) + try: + assert parsed_request == {"value": "private"} + except AssertionError as e: + return _error_to_response(e) + return [ + 201, + {}, + b"", + ] + + httpretty.register_uri( + httpretty.PUT, + "https://api.shinyapps.io/v1/applications/8442/properties/application.visibility", + body=post_application_property_callback, + status=200, + ) + + def post_bundle_callback(request, uri, response_headers): + parsed_request = _load_json(request.body) + del parsed_request["checksum"] + del parsed_request["content_length"] + try: + assert parsed_request == { + "application": 8442, + "content_type": "application/x-tar", + } + except AssertionError as e: + return _error_to_response(e) + return [ + 201, + {"Content-Type": "application/json"}, + open("tests/testdata/rstudio-responses/create-bundle.json", "r").read(), + ] + + httpretty.register_uri( + httpretty.POST, + "https://api.shinyapps.io/v1/bundles", + body=post_bundle_callback, + ) + + httpretty.register_uri( + httpretty.PUT, + "https://lucid-uploads-staging.s3.amazonaws.com/bundles/application-8442/" + "6c9ed0d91ee9426687d9ac231d47dc83.tar.gz" + "?AWSAccessKeyId=theAccessKeyId" + "&Signature=dGhlU2lnbmF0dXJlCg%3D%3D" + "&content-md5=D1blMI4qTiI3tgeUOYXwkg%3D%3D" + "&content-type=application%2Fx-tar" + "&x-amz-security-token=dGhlVG9rZW4K" + "&Expires=1656715153", + body="", + ) + + def post_bundle_status_callback(request, uri, response_headers): + parsed_request = _load_json(request.body) + try: + assert parsed_request == {"status": "ready"} + except AssertionError as e: + return _error_to_response(e) + return [303, {"Location": "https://api.shinyapps.io/v1/bundles/12640"}, ""] + + httpretty.register_uri( + httpretty.POST, + "https://api.shinyapps.io/v1/bundles/12640/status", + body=post_bundle_status_callback, + ) + + httpretty.register_uri( + httpretty.GET, + "https://api.shinyapps.io/v1/bundles/12640", + body=open("tests/testdata/rstudio-responses/get-accounts.json", "r").read(), + adding_headers={"Content-Type": "application/json"}, + status=200, + ) + + def post_deploy_callback(request, uri, response_headers): + parsed_request = _load_json(request.body) + try: + assert parsed_request == {"bundle": 12640, "rebuild": False} + except AssertionError as e: + return _error_to_response(e) + return [ + 303, + {"Location": "https://api.shinyapps.io/v1/tasks/333"}, + open("tests/testdata/rstudio-responses/post-deploy.json", "r").read(), + ] + + httpretty.register_uri( + httpretty.POST, + "https://api.shinyapps.io/v1/applications/8442/deploy", + body=post_deploy_callback, + ) + + httpretty.register_uri( + httpretty.GET, + "https://api.shinyapps.io/v1/tasks/333", + body=open("tests/testdata/rstudio-responses/get-task.json", "r").read(), + adding_headers={"Content-Type": "application/json"}, + status=200, + ) + + runner = CliRunner() + args = [ + "deploy", + "manifest", + get_manifest_path("shinyapp"), + "--account", + "some-account", + "--token", + "someToken", + "--secret", + "c29tZVNlY3JldAo=", + "--title", + "myApp", + "--visibility", + "private", + "--app-id", + "8442", + ] + try: + result = runner.invoke(cli, args) + assert result.exit_code == 0, result.output + finally: + if original_api_key_value: + os.environ["CONNECT_API_KEY"] = original_api_key_value + if original_server_value: + os.environ["CONNECT_SERVER"] = original_server_value + + @httpretty.activate(verbose=True, allow_net_connect=False) @pytest.mark.parametrize( "project_application_id,project_id", diff --git a/tests/testdata/rstudio-responses/create-application.json b/tests/testdata/rstudio-responses/application.json similarity index 100% rename from tests/testdata/rstudio-responses/create-application.json rename to tests/testdata/rstudio-responses/application.json From 00380987785e4a3e87ad8d07d8028a07e8f7a45b Mon Sep 17 00:00:00 2001 From: Matthew Lynch Date: Tue, 5 Sep 2023 11:32:10 -0500 Subject: [PATCH 4/5] rename test name --- tests/test_main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_main.py b/tests/test_main.py index ff73604b..8bad8096 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -279,7 +279,7 @@ def post_deploy_callback(request, uri, response_headers): os.environ["CONNECT_SERVER"] = original_server_value @httpretty.activate(verbose=True, allow_net_connect=False) - def test_deploy_manifest_shinyapps(self): + def test_redeploy_manifest_shinyapps(self): original_api_key_value = os.environ.pop("CONNECT_API_KEY", None) original_server_value = os.environ.pop("CONNECT_SERVER", None) From 36e931b645e848b9eb9fe5b1414a4985643a2b9b Mon Sep 17 00:00:00 2001 From: Matthew Lynch Date: Tue, 5 Sep 2023 11:40:46 -0500 Subject: [PATCH 5/5] add note of shinyapps.io --app-id fix to changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a0cb8990..5002603d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Fixed +- Error deploying to shinyapps.io when `--app-id` is provided [#464](https://github.com/rstudio/rsconnect-python/issues/464). + ### Added - Add `--disable-env-management`, `--disable-env-management-py` and `--disable-env-management-r` flags for all content types