Skip to content
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ When adding a new entry, please use the following format:

- [2026-03-30] fix: added test decorators to resolve lack of "TTN_API_KEY" on fork PR's. removed k6 from github actions. changed github action to utilize env-import.py as opposeed to directly accessing s3 bucket for env variables. [#736] (https://github.com/jlab-sensing/ENTS-backend/pull/736)
- [2026-03-23] fix: increase fallback SECRET_KEY length to resolve PyJWT InsecureKeyLengthWarning [#682](https://github.com/jlab-sensing/ENTS-backend/pull/682)
- [2026-03-21] fix: Return 400 on duplicate logger name in PUT /api/logger/:id [#710](https://github.com/jlab-sensing/ENTS-backend/pull/710)
- [2026-03-12] fix: safely catch PyJWT DecodeErrors and remove log spam for invalid tokens [#683](https://github.com/jlab-sensing/ENTS-backend/pull/683)
- [2026-03-12] hotfix: linted AddCellModal, ensured functionality of D10 charts, and added flags to github action pytest for logging [#703](https://github.com/jlab-sensing/ENTS-backend/pull/703)
- [2026-03-12] hotfix: Resolve global frontend linting failure in AddCellModal [#709](https://github.com/jlab-sensing/ENTS-backend/pull/709)
Expand Down
3 changes: 3 additions & 0 deletions backend/api/resources/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,9 @@ def put(self, _user, logger_id: int):
# Update database fields
updated = False
if "name" in json_data:
existing = LoggerModel.find_by_name(json_data["name"])
if existing and existing.id != logger_id:
return {"message": "Duplicate logger name"}, 400
logger.name = json_data.get("name")
updated = True
if "description" in json_data:
Expand Down
3 changes: 3 additions & 0 deletions backend/api/schemas/cell_schema.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from ..schemas import ma
from ..models.cell import Cell
from ..models.user import (
User, # noqa: F401 - ensures User is registered before SQLAlchemy mapper configures
)


class CellSchema(ma.SQLAlchemyAutoSchema):
Expand Down
46 changes: 46 additions & 0 deletions backend/tests/test_logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,3 +288,49 @@ def _raise_integrity(*_args, **_kwargs):

assert status == 400
assert body["message"] == "Duplicate logger name"


def test_put_duplicate_name_returns_400(monkeypatch):
"""PUT /api/logger/:id with a name already used by another logger returns 400."""
existing_logger = StubLogger(logger_id=1)
target_logger = StubLogger(logger_id=2)

monkeypatch.setattr(
"api.resources.logger.LoggerModel.get",
lambda logger_id: target_logger,
)
monkeypatch.setattr(
"api.resources.logger.LoggerModel.find_by_name",
lambda name: existing_logger, # name already taken by a different logger
)

app = _make_test_app()
with app.test_request_context(json={"name": "existing-name"}):
body, status = LoggerResource().put(None, logger_id=2)

assert status == 400
assert body["message"] == "Duplicate logger name"


def test_put_same_name_returns_200(monkeypatch):
"""PUT /api/logger/:id renaming to its own current name should succeed."""
stub_logger = StubLogger(logger_id=1)
stub_logger.name = "my-logger"
stub_logger.description = "desc"
stub_logger.type = ""
stub_logger.save = lambda: None

monkeypatch.setattr(
"api.resources.logger.LoggerModel.get",
lambda logger_id: stub_logger,
)
monkeypatch.setattr(
"api.resources.logger.LoggerModel.find_by_name",
lambda name: stub_logger, # same logger — not a conflict
)

app = _make_test_app()
with app.test_request_context(json={"name": "my-logger"}):
body, status = LoggerResource().put(None, logger_id=1)

assert status == 200
23 changes: 22 additions & 1 deletion frontend/src/pages/profile/components/EditLoggerModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ function EditLoggerModal({ logger }) {
const [formData, setFormData] = useState({ ...logger });
const [response, setResponse] = useState(null);
const [isSubmitting, setSubmitting] = useState(false);
const [error, setError] = useState(null);

const handleOpen = () => {
setOpen(true);
setResponse(null);
setError(null);
setFormData({ ...logger });
};

Expand All @@ -40,9 +42,13 @@ function EditLoggerModal({ logger }) {
updateLogger(logger.id, updateData, auth?.accessToken)
.then((res) => {
setResponse(res);
setError(null);
refetch();
})
.catch((err) => console.error('Edit failed:', err))
.catch((err) => {
const msg = err?.response?.data?.message || 'Failed to update logger.';
setError(msg);
})
.finally(() => setSubmitting(false));
};

Expand Down Expand Up @@ -176,6 +182,21 @@ function EditLoggerModal({ logger }) {
/>
</Box>

{/* Error Message */}
{error && (
<Box sx={{
mt: '1rem',
p: '0.75rem 1rem',
backgroundColor: '#fdecea',
borderRadius: '8px',
border: '1px solid #f5c6cb'
}}>
<Typography color='error' variant='body2'>
{error}
</Typography>
</Box>
)}

{/* Action Buttons */}
<Box sx={{
display: 'flex',
Expand Down
Loading