Skip to content

Commit 6c15f2c

Browse files
committed
ENH: Add '/fmu' GET route
1 parent 7c30898 commit 6c15f2c

File tree

3 files changed

+126
-3
lines changed

3 files changed

+126
-3
lines changed

src/fmu_settings_api/v1/routes/fmu.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
"""Routes to initialize a .fmu session."""
22

3+
from pathlib import Path
4+
35
from fastapi import APIRouter, HTTPException, Response
4-
from fmu.settings import get_fmu_directory
6+
from fmu.settings import find_nearest_fmu_directory, get_fmu_directory
57
from fmu.settings._init import init_fmu_directory
68
from fmu.settings.resources.config import Config
79

@@ -12,6 +14,37 @@
1214
router = APIRouter(prefix="/fmu", tags=["fmu"])
1315

1416

17+
@router.get("/", response_model=Config)
18+
async def get_cwd_fmu_directory_session(response: Response) -> Config:
19+
"""Returns the configuration for the nearest .fmu directory.
20+
21+
This directory is searched for above the current working directory.
22+
"""
23+
try:
24+
path = Path.cwd()
25+
fmu_dir = find_nearest_fmu_directory(path)
26+
session_id = await create_fmu_session(fmu_dir)
27+
response.set_cookie(
28+
key=settings.SESSION_COOKIE_KEY,
29+
value=session_id,
30+
httponly=True,
31+
secure=True,
32+
samesite="lax",
33+
)
34+
return fmu_dir.config.load()
35+
except PermissionError as e:
36+
raise HTTPException(
37+
status_code=403,
38+
detail="Permission denied locating .fmu",
39+
) from e
40+
except FileNotFoundError as e:
41+
raise HTTPException(
42+
status_code=404, detail=f"No .fmu directory found from {path}"
43+
) from e
44+
except Exception as e:
45+
raise HTTPException(status_code=500, detail=str(e)) from e
46+
47+
1548
@router.post("/", response_model=Config)
1649
async def get_fmu_directory_session(
1750
response: Response, fmu_dir_path: FMUDirPath

tests/conftest.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,11 @@ def fmu_dir_path(fmu_dir: FMUDirectory) -> Path:
3838

3939

4040
@pytest.fixture
41-
def fmu_dir_no_permissions(fmu_dir_path: Path) -> Path:
41+
def fmu_dir_no_permissions(fmu_dir_path: Path) -> Generator[Path, None, None]:
4242
"""Mocks a .fmu in a tmp_path without permissions."""
4343
(fmu_dir_path / ".fmu").chmod(stat.S_IRUSR)
44-
return fmu_dir_path
44+
yield fmu_dir_path
45+
(fmu_dir_path / ".fmu").chmod(stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
4546

4647

4748
@pytest.fixture

tests/test_v1/test_fmu.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from fastapi.testclient import TestClient
99
from fmu.settings._init import init_fmu_directory
1010
from fmu.settings.resources.config import Config
11+
from pytest import MonkeyPatch
1112

1213
from fmu_settings_api.__main__ import app
1314
from fmu_settings_api.config import settings
@@ -32,6 +33,94 @@ def test_get_fmu_invalid_token() -> None:
3233
assert response.json() == {"detail": "Not authorized"}
3334

3435

36+
def test_get_cwd_fmu_directory_no_permissions(
37+
mock_token: str, fmu_dir_no_permissions: Path, monkeypatch: MonkeyPatch
38+
) -> None:
39+
"""Test 403 returns when lacking permissions somewhere in the path tree."""
40+
ert_model_path = fmu_dir_no_permissions / "project/24.0.3/ert/model"
41+
ert_model_path.mkdir(parents=True)
42+
monkeypatch.chdir(ert_model_path)
43+
response = client.get(
44+
ROUTE,
45+
headers={settings.TOKEN_HEADER_NAME: mock_token},
46+
)
47+
assert response.status_code == status.HTTP_403_FORBIDDEN
48+
assert response.json() == {"detail": "Permission denied locating .fmu"}
49+
50+
51+
def test_get_cwd_fmu_directory_does_not_exist(
52+
mock_token: str, tmp_path: Path, monkeypatch: MonkeyPatch
53+
) -> None:
54+
"""Test 404 returns when .fmu or directory does not exist from the cwd."""
55+
ert_model_path = tmp_path / "project/24.0.3/ert/model"
56+
ert_model_path.mkdir(parents=True)
57+
monkeypatch.chdir(ert_model_path)
58+
response = client.get(
59+
ROUTE,
60+
headers={settings.TOKEN_HEADER_NAME: mock_token},
61+
)
62+
assert response.status_code == status.HTTP_404_NOT_FOUND
63+
assert response.json() == {
64+
"detail": f"No .fmu directory found from {ert_model_path}"
65+
}
66+
67+
68+
def test_get_cwd_fmu_directory_is_not_directory(
69+
mock_token: str, tmp_path: Path, monkeypatch: MonkeyPatch
70+
) -> None:
71+
"""Test 404 returns when .fmu exists but is not a directory.
72+
73+
Although a .fmu file exists, because a .fmu _directory_ is not, it is
74+
treated as a 404.
75+
"""
76+
path = tmp_path / ".fmu"
77+
path.touch()
78+
ert_model_path = tmp_path / "project/24.0.3/ert/model"
79+
ert_model_path.mkdir(parents=True)
80+
monkeypatch.chdir(ert_model_path)
81+
82+
response = client.get(
83+
ROUTE,
84+
headers={settings.TOKEN_HEADER_NAME: mock_token},
85+
)
86+
assert response.status_code == status.HTTP_404_NOT_FOUND
87+
assert response.json() == {
88+
"detail": f"No .fmu directory found from {ert_model_path}"
89+
}
90+
91+
92+
def test_get_cwd_fmu_directory_raises_other_exceptions(mock_token: str) -> None:
93+
"""Test 500 returns if other exceptions are raised."""
94+
with patch(
95+
"fmu_settings_api.v1.routes.fmu.find_nearest_fmu_directory",
96+
side_effect=Exception("foo"),
97+
):
98+
response = client.get(
99+
ROUTE,
100+
headers={settings.TOKEN_HEADER_NAME: mock_token},
101+
)
102+
assert response.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR
103+
assert response.json() == {"detail": "foo"}
104+
105+
106+
def test_get_cwd_fmu_directory_exists(
107+
mock_token: str, tmp_path: Path, monkeypatch: MonkeyPatch
108+
) -> None:
109+
"""Test 200 and config returns when .fmu exists."""
110+
fmu_dir = init_fmu_directory(tmp_path)
111+
ert_model_path = tmp_path / "project/24.0.3/ert/model"
112+
ert_model_path.mkdir(parents=True)
113+
monkeypatch.chdir(ert_model_path)
114+
115+
response = client.get(
116+
ROUTE,
117+
headers={settings.TOKEN_HEADER_NAME: mock_token},
118+
)
119+
assert response.status_code == status.HTTP_200_OK
120+
config = Config.model_validate(response.json())
121+
assert fmu_dir.config.load() == config
122+
123+
35124
def test_get_fmu_directory_no_permissions(
36125
mock_token: str, fmu_dir_no_permissions: Path
37126
) -> None:

0 commit comments

Comments
 (0)