Skip to content

Commit

Permalink
Get rex release version from github repository
Browse files Browse the repository at this point in the history
  • Loading branch information
TylerZeroMaster committed Oct 29, 2024
1 parent 4094a22 commit 29915e3
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 38 deletions.
5 changes: 5 additions & 0 deletions backend/app/app/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@
REX_WEB_RELEASE_URL = os.getenv(
"REX_WEB_RELEASE_URL", "https://openstax.org/rex/release.json"
)
REX_WEB_ARCHIVE_CONFIG = os.getenv(
"REX_WEB_ARCHIVE_CONFIG",
# owner:repo:path
"openstax:rex-web:src/config.archive-url.json",
)

# GITHUB OAUTH
CLIENT_ID = os.getenv("GITHUB_OAUTH_ID")
Expand Down
37 changes: 37 additions & 0 deletions backend/app/app/github/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import json
from datetime import datetime
from typing import Any, Dict, List, Tuple
from urllib.parse import urlencode

from lxml import etree

Expand Down Expand Up @@ -120,6 +121,42 @@ async def get_collections(
}


def normpath(*parts: str):
return tuple(p.strip("/") for p in parts)


def build_url(*parts: str, **kwargs: str | None):
path = "/".join(("https://api.github.com", *normpath(*parts)))
kwargs = {k: v for k, v in kwargs.items() if v}
if kwargs:
path = "?".join((path, urlencode(kwargs)))
return path


async def get_file_response(
client: AuthenticatedClient,
owner: str,
repo: str,
path: str,
ref: str | None = None,
):
url = build_url("repos", owner, repo, "contents", path, ref=ref)
response = await client.get(url)
response.raise_for_status()
return response


async def get_file_content(
client: AuthenticatedClient,
owner: str,
repo: str,
path: str,
ref: str | None = None,
):
payload = (await get_file_response(client, owner, repo, path, ref)).json()
return base64.b64decode(payload["content"])


async def push_to_github(
client: AuthenticatedClient,
path: str,
Expand Down
24 changes: 14 additions & 10 deletions backend/app/app/service/abl.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import re
import json
from typing import Any, Dict, List, Optional

from httpx import AsyncClient, HTTPStatusError
Expand All @@ -16,6 +16,7 @@
Consumer,
Repository,
)
from app.github.api import get_file_content


async def get_rex_release_json(client: AsyncClient):
Expand All @@ -38,15 +39,18 @@ async def get_rex_books(client: AsyncClient):


async def get_rex_release_version(client: AsyncClient):
rex_release = await get_rex_release_json(client)
archive_url = rex_release.get("archiveUrl", "").strip()
if archive_url == "":
raise CustomBaseError("Could not find valid REX archive URL")
# Search for: %Y%m%d.%H%M%S
version_matches = re.findall(r"\d{8}\.\d{6}", archive_url)
if len(version_matches) != 1:
raise CustomBaseError("Could not determine REX release version")
return version_matches[0]
owner, repo, path = config.REX_WEB_ARCHIVE_CONFIG.split(":", 2)
try:
raw_contents = await get_file_content(client, owner, repo, path)
except HTTPStatusError as he:
raise CustomBaseError(
f"Failed to fetch rex release version: {he.response.status_code}"
) from he
contents = json.loads(raw_contents)
version = contents.get("REACT_APP_ARCHIVE", "").strip()
if not version:
raise CustomBaseError("Could not find valid REX version")
return version


def get_rex_book_versions(rex_books: Dict[str, Any], book_uuids: List[str]):
Expand Down
42 changes: 14 additions & 28 deletions backend/app/tests/unit/test_abl.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import base64
import json

import pytest

from app.core import config
Expand Down Expand Up @@ -216,11 +219,16 @@ def test_get_rex_book_versions(rex_books, book_uuids, expected):

@pytest.mark.asyncio
async def test_get_rex_release_version(mock_http_client):
url = "https://api.github.com/repos/openstax/rex-web/contents/src/config.archive-url.json"
fake_api_response = {
"content": base64.b64encode(
json.dumps({"REACT_APP_ARCHIVE": "20240101.000001"}).encode()
).decode()
}

# GIVEN: A valid response
mock_client: MockAsyncClient = mock_http_client(
get={
config.REX_WEB_RELEASE_URL: {"archiveUrl": "a/b/20240101.000001/c"}
}
get={url: fake_api_response}
)
# WHEN: A request is made
version = await get_rex_release_version(mock_client)
Expand All @@ -231,39 +239,17 @@ async def test_get_rex_release_version(mock_http_client):
# THEN: The expected version is matched
assert version == "20240101.000001"

# GIVEN: An invalid response with zero matches
mock_client: MockAsyncClient = mock_http_client(
get={config.REX_WEB_RELEASE_URL: {"archiveUrl": "a/b/c"}}
)
# WHEN: A request is made
# THEN: An error is raised
with pytest.raises(CustomBaseError) as cbe:
await get_rex_release_version(mock_client)
assert len(mock_client.responses) == 1
assert cbe.match("Could not determine REX release version")
# GIVEN: An invalid response with more than one match
# GIVEN: An invalid response
mock_client: MockAsyncClient = mock_http_client(
get={
config.REX_WEB_RELEASE_URL: {
"archiveUrl": "a/b/c/20240101.000001/d/12345678.123456"
}
url: {"content": base64.b64encode(json.dumps({}).encode()).decode()}
}
)
# WHEN: A request is made
# THEN: An error is raised
with pytest.raises(CustomBaseError) as cbe:
await get_rex_release_version(mock_client)
assert len(mock_client.responses) == 1
assert cbe.match("Could not determine REX release version")
# GIVEN: An invalid response
mock_client: MockAsyncClient = mock_http_client(
get={config.REX_WEB_RELEASE_URL: {}}
)
# WHEN: A request is made
# THEN: An error is raised
with pytest.raises(CustomBaseError) as cbe:
await get_rex_release_version(mock_client)
assert cbe.match("Could not find valid REX archive URL")
assert cbe.match("Could not find valid REX version")
# GIVEN: A no response
mock_client: MockAsyncClient = mock_http_client()
# WHEN: A request is made
Expand Down

0 comments on commit 29915e3

Please sign in to comment.