From 44dfaa8a3d4b3430320ece268dc9923212b276c2 Mon Sep 17 00:00:00 2001 From: Josh Kofsky Date: Tue, 8 Nov 2022 20:51:16 -0500 Subject: [PATCH 01/16] @W-11993356 Metecho: handle scratch org DNS propagation delays --- metecho/api/sf_run_flow.py | 70 ++++++++++++++++++++++++++------------ 1 file changed, 48 insertions(+), 22 deletions(-) diff --git a/metecho/api/sf_run_flow.py b/metecho/api/sf_run_flow.py index 2884e9f40..269f69e5f 100644 --- a/metecho/api/sf_run_flow.py +++ b/metecho/api/sf_run_flow.py @@ -6,7 +6,7 @@ import subprocess import time from datetime import datetime - +import urllib3 from cumulusci.core.config import OrgConfig, TaskConfig from cumulusci.core.runtime import BaseCumulusCI from cumulusci.oauth.client import OAuth2Client, OAuth2ClientConfig @@ -79,10 +79,14 @@ def delete_org_on_error(scratch_org=None, originating_user_id=None): f"Are you certain that the Org still exists? If you need support, your job ID is {job_id}." # noqa: B950 ) else: - error_msg = _(f"Are you certain that the Org still exists? {err.args[0]}") + error_msg = _( + f"Are you certain that the Org still exists? {err.args[0]}" + ) error = ScratchOrgError(error_msg) - scratch_org.remove_scratch_org(error, originating_user_id=originating_user_id) + scratch_org.remove_scratch_org( + error, originating_user_id=originating_user_id + ) raise error @@ -181,7 +185,9 @@ def get_org_result( # Schema for ScratchOrgInfo object: # https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_objects_scratchorginfo.htm # noqa: B950 - scratch_org_definition = {k.lower(): v for k, v in scratch_org_definition.items()} + scratch_org_definition = { + k.lower(): v for k, v in scratch_org_definition.items() + } features = scratch_org_definition.get("features", []) # Map between fields in the scratch org definition and fields on the ScratchOrgInfo object. # Start with fields that have special handling outside the .json. @@ -192,7 +198,9 @@ def get_org_result( "description": f"{repo_owner}/{repo_name} {repo_branch}", # Override whatever is in scratch_org_config.days: "durationdays": DURATION_DAYS, - "features": ";".join(features) if isinstance(features, list) else features, + "features": ";".join(features) + if isinstance(features, list) + else features, "namespace": ( cci.project_config.project__package__namespace if scratch_org_config.namespaced @@ -273,19 +281,29 @@ def get_access_token(*, org_result, scratch_org_config): the scratch org is created. This must be completed once in order for future access tokens to be obtained using the JWT token flow. """ - oauth_config = OAuth2ClientConfig( - client_id=SF_CLIENT_ID, - client_secret=SF_CLIENT_SECRET, - redirect_uri=SF_CALLBACK_URL, - auth_uri=f"{scratch_org_config.instance_url}/services/oauth2/authorize", - token_uri=f"{scratch_org_config.instance_url}/services/oauth2/token", - scope="web full refresh_token", - ) - oauth = OAuth2Client(oauth_config) - auth_result = oauth.auth_code_grant(org_result["AuthCode"]).json() - scratch_org_config.config["access_token"] = scratch_org_config._scratch_info[ - "access_token" - ] = auth_result["access_token"] + attempts = 0 + while attempts < settings.MAXIMUM_JOB_LENGTH: + try: + oauth_config = OAuth2ClientConfig( + client_id=SF_CLIENT_ID, + client_secret=SF_CLIENT_SECRET, + redirect_uri=SF_CALLBACK_URL, + auth_uri=f"{scratch_org_config.instance_url}/services/oauth2/authorize", + token_uri=f"{scratch_org_config.instance_url}/services/oauth2/token", + scope="web full refresh_token", + ) + oauth = OAuth2Client(oauth_config) + auth_result = oauth.auth_code_grant(org_result["AuthCode"]).json() + scratch_org_config.config[ + "access_token" + ] = scratch_org_config._scratch_info["access_token"] = auth_result[ + "access_token" + ] + return + except urllib3.exceptions.NewConnectionError: + attempts += 10 + if attempts >= settings.MAXIMUM_JOB_LENGTH: + raise ScratchOrgError("Failed to build your job after ") def deploy_org_settings( @@ -301,7 +319,9 @@ def deploy_org_settings( keychain=cci.keychain, originating_user_id=originating_user_id, ) - path = os.path.join(cci.project_config.repo_root, scratch_org_config.config_file) + path = os.path.join( + cci.project_config.repo_root, scratch_org_config.config_file + ) task_config = TaskConfig({"options": {"definition_file": path}}) task = DeployOrgSettings(cci.project_config, task_config, org_config) task() @@ -352,9 +372,13 @@ def create_org( ) org_result = poll_for_scratch_org_completion(devhub_api, org_result) mutate_scratch_org( - scratch_org_config=scratch_org_config, org_result=org_result, email=email + scratch_org_config=scratch_org_config, + org_result=org_result, + email=email, + ) + get_access_token( + org_result=org_result, scratch_org_config=scratch_org_config ) - get_access_token(org_result=org_result, scratch_org_config=scratch_org_config) org_config = deploy_org_settings( cci=cci, org_name=org_name, @@ -418,7 +442,9 @@ def run_flow(*, cci, org_config, flow_name, project_path, user): orig_stdout, _ = p.communicate() if p.returncode: p = subprocess.run( - [command, "error", "info"], capture_output=True, env={"HOME": project_path} + [command, "error", "info"], + capture_output=True, + env={"HOME": project_path}, ) traceback = p.stdout.decode("utf-8") logger.warning(traceback) From 69a98312148818c2699a0fd8a96aa888d79f81b3 Mon Sep 17 00:00:00 2001 From: Josh Kofsky Date: Wed, 9 Nov 2022 15:57:49 -0500 Subject: [PATCH 02/16] Adding thoughts --- metecho/api/sf_run_flow.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/metecho/api/sf_run_flow.py b/metecho/api/sf_run_flow.py index 269f69e5f..3a392c119 100644 --- a/metecho/api/sf_run_flow.py +++ b/metecho/api/sf_run_flow.py @@ -1,3 +1,4 @@ +from asyncio import sleep import contextlib import json import logging @@ -6,7 +7,7 @@ import subprocess import time from datetime import datetime -import urllib3 +from urllib3.exceptions import NewConnectionError, ProxyError from cumulusci.core.config import OrgConfig, TaskConfig from cumulusci.core.runtime import BaseCumulusCI from cumulusci.oauth.client import OAuth2Client, OAuth2ClientConfig @@ -300,10 +301,11 @@ def get_access_token(*, org_result, scratch_org_config): "access_token" ] return - except urllib3.exceptions.NewConnectionError: + except (NewConnectionError, ProxyError): attempts += 10 - if attempts >= settings.MAXIMUM_JOB_LENGTH: - raise ScratchOrgError("Failed to build your job after ") + sleep(attempts) + if attempts >= settings.MAXIMUM_JOB_LENGTH: + raise ScratchOrgError(message="Failed to build your job after") def deploy_org_settings( From 0db9f96e9b401d83d657f02ac30e5bc82a893f8e Mon Sep 17 00:00:00 2001 From: Josh Kofsky Date: Wed, 9 Nov 2022 17:55:04 -0500 Subject: [PATCH 03/16] Updating files --- src/js/components/user/connect.tsx | 6 +++- src/js/utils/helpers.tsx | 20 +++++++++++ test/js/components/user/connect.test.jsx | 42 ++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 1 deletion(-) diff --git a/src/js/components/user/connect.tsx b/src/js/components/user/connect.tsx index 7dc9c4c35..ee7099e3c 100644 --- a/src/js/components/user/connect.tsx +++ b/src/js/components/user/connect.tsx @@ -7,6 +7,7 @@ import { Trans, useTranslation } from 'react-i18next'; import { ExternalLink } from '@/js/components/utils'; import { User } from '@/js/store/user/reducer'; +import { extractShard, extractCustomDomain } from '@/js/utils/helpers'; const ConnectModal = ({ user, @@ -134,7 +135,10 @@ const ConnectModal = ({ className="slds-form-element__help slds-truncate slds-p-top_small" data-testid="custom-domain" > - https://{customDomain || domain}.my.salesforce.com + https:// + {extractCustomDomain(customDomain.trim()) || domain} + {extractShard(customDomain.trim())} + .my.salesforce.com