Skip to content

Commit

Permalink
Default new projects to the Dev Hub's latest API version (#2132)
Browse files Browse the repository at this point in the history
  • Loading branch information
davidmreed committed Jul 2, 2023
1 parent 52e8f45 commit 8ef8dc4
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 11 deletions.
19 changes: 16 additions & 3 deletions metecho/api/jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from cumulusci.tasks.github.util import CommitDir
from cumulusci.tasks.vlocity.vlocity import VlocityRetrieveTask
from cumulusci.utils import download_extract_github, temporary_dir
from cumulusci.utils.http.requests_utils import safe_json_from_response
from django.conf import settings
from django.db import transaction
from django.db.models.query_utils import Q
Expand Down Expand Up @@ -71,7 +72,7 @@
get_latest_revision_numbers,
get_valid_target_directories,
)
from .sf_run_flow import create_org, delete_org, run_flow
from .sf_run_flow import create_org, delete_org, get_devhub_api, run_flow

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -252,14 +253,26 @@ def create_repository(
zipfile = download_extract_github(org_gh, tpl_repo.owner, tpl_repo.name)
zipfile.extractall()

# Bootstrap repository with CumulusCI
runtime = CliRuntime()

try:
# Ask the user's Dev Hub what its latest API version is
sf = get_devhub_api(devhub_username=user.sf_username)
response = requests.get(
f"https://{sf.sf_instance}/services/data", headers=sf.headers
)

version = safe_json_from_response(response)[-1]["version"]
except Exception:
version = runtime.universal_config.project__package__api_version

# Bootstrap repository with CumulusCI
context = {
"cci_version": cumulusci.__version__,
"project_name": project.repo_name,
"package_name": project.repo_name,
"package_namespace": None,
"api_version": runtime.universal_config.project__package__api_version,
"api_version": version,
"source_format": "sfdx",
"dependencies": [
{"type": "github", "url": url} for url in dependencies
Expand Down
2 changes: 1 addition & 1 deletion metecho/api/sf_run_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ def refresh_access_token(
return org_config


def get_devhub_api(*, devhub_username, scratch_org=None):
def get_devhub_api(*, devhub_username, scratch_org=None) -> SimpleSalesforce:
"""
Get an access token (session) for the specified dev hub username.
This only works if the user has already authorized the connected app
Expand Down
59 changes: 52 additions & 7 deletions metecho/api/tests/jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -1384,12 +1384,56 @@ def github_mocks(self, mocker, project, git_hub_collaboration_factory):
gh_org.create_team.return_value = team
gh_org.create_repository.return_value = repo

return project, gh_org, team, repo
get_devhub_api = mocker.patch(f"{PATCH_ROOT}.get_devhub_api", autospec=True)
get_devhub_api.sf_instance = "foo"

requests = mocker.patch(f"{PATCH_ROOT}.requests", autospec=True)
# Wild API version so we can easily detect it came from here
requests.get.return_value.json.return_value = [{"version": "600.0"}]

return project, gh_org, team, repo, get_devhub_api, requests

def test_ok(self, mocker, github_mocks, user_factory):
user = user_factory()
project, org, team, repo = github_mocks
mocker.patch(f"{PATCH_ROOT}.init_from_context")
project, org, team, repo, get_devhub_api, requests = github_mocks
init_from_context = mocker.patch(f"{PATCH_ROOT}.init_from_context")
sarge = mocker.patch(f"{PATCH_ROOT}.sarge", autospec=True)
sarge.capture_both.return_value.returncode = 0
async_to_sync = mocker.patch("metecho.api.model_mixins.async_to_sync")
zipfile = mocker.patch(f"{PATCH_ROOT}.download_extract_github").return_value

create_repository(
project,
user=user,
dependencies=["http://foo.com"],
template_repo_owner="owner",
template_repo_name="repo",
)
project.refresh_from_db()

assert project.repo_id == 123456
assert (
team.add_or_update_membership.call_count == 2
), "Expected one call each collaborator"
async_to_sync.return_value.assert_called_with(
project,
{
"type": "PROJECT_CREATE",
"payload": {"originating_user_id": user.pk},
},
for_list=False,
group_name=None,
include_user=False,
)
assert sarge.capture_both.called
assert zipfile.extractall.called
assert init_from_context.call_args_list[0][0][0]["api_version"] == "600.0"

def test_ok__no_version_from_devhub(self, mocker, github_mocks, user_factory):
user = user_factory()
project, org, team, repo, get_devhub_api, requests = github_mocks
get_devhub_api.side_effect = Exception
init_from_context = mocker.patch(f"{PATCH_ROOT}.init_from_context")
sarge = mocker.patch(f"{PATCH_ROOT}.sarge", autospec=True)
sarge.capture_both.return_value.returncode = 0
async_to_sync = mocker.patch("metecho.api.model_mixins.async_to_sync")
Expand Down Expand Up @@ -1420,8 +1464,9 @@ def test_ok(self, mocker, github_mocks, user_factory):
)
assert sarge.capture_both.called
assert zipfile.extractall.called
assert init_from_context.call_args_list[0][0][0]["api_version"] != "600.0"

def test__gh_error(self, mocker, caplog, project, user_factory):
def test__gh_error(self, mocker, caplog, project, user_factory, github_mocks):
user = user_factory()
async_to_sync = mocker.patch("metecho.api.model_mixins.async_to_sync")
mocker.patch(f"{PATCH_ROOT}.gh_as_user", side_effect=Exception("Oh no!"))
Expand Down Expand Up @@ -1449,7 +1494,7 @@ def test__gh_error(self, mocker, caplog, project, user_factory):

def test__team_name_taken(self, mocker, github_mocks, project, user_factory):
user = user_factory()
project, org, team, repo = github_mocks
project, org, _, _, _, _ = github_mocks
resp = mocker.MagicMock(status_code=422)
resp.json.return_value = {"message": "Validation Failed"}
# Simulate the first two team names being taken
Expand All @@ -1476,7 +1521,7 @@ def test__team_name_taken(self, mocker, github_mocks, project, user_factory):

def test__team_error(self, mocker, github_mocks, project, user_factory):
user = user_factory()
project, org, team, repo = github_mocks
project, org, _, _, _, _ = github_mocks
resp = mocker.MagicMock(status_code=422)
resp.json.return_value = {"message": "Not a validation error"}
org.create_team.side_effect = UnprocessableEntity(resp)
Expand All @@ -1489,7 +1534,7 @@ def test__push_error(
self, mocker, caplog, github_mocks, user_factory, fail_repo_delete
):
user = user_factory()
project, org, team, repo = github_mocks
project, _, team, repo, _, _ = github_mocks
repo.teams.return_value = [team]
if fail_repo_delete:
repo.delete.side_effect = Exception("REPO DELETE FAIL")
Expand Down

0 comments on commit 8ef8dc4

Please sign in to comment.