refactor: remove command field from GraphQL Alarm type

command is generated internally and has no informational value to API
consumers, so stop exposing it on getAlarms/getAlarm/setAlarm.
This commit is contained in:
2026-06-22 10:04:48 +02:00
parent b2bb5bdaf9
commit a67f57c37e
3 changed files with 28 additions and 16 deletions
+3 -4
View File
@@ -82,7 +82,6 @@ query {
getAlarms { getAlarms {
id id
cronExpression cronExpression
command
isEnabled isEnabled
} }
} }
@@ -92,16 +91,16 @@ query {
```graphql ```graphql
mutation { mutation {
setAlarm( setAlarm(
cronExpression: "45 6 * * 1-5", cronExpression: "45 6 * * 1-5",
isEnabled: true isEnabled: true
) { ) {
id id
cronExpression cronExpression
command isEnabled
} }
} }
``` ```
*(Note: The `command` is managed automatically by the API and cannot be overridden, to prevent command injection into the system crontab.)* *(Note: `command` is generated automatically by the API and is **not** accepted as an input parameter on `setAlarm`, to prevent command injection into the system crontab.)*
**Delete an alarm:** **Delete an alarm:**
```graphql ```graphql
+13 -4
View File
@@ -65,7 +65,6 @@ def _start_wecker_process() -> subprocess.Popen:
class Alarm: class Alarm:
id: str id: str
cron_expression: str cron_expression: str
command: str
is_enabled: bool is_enabled: bool
@@ -78,7 +77,14 @@ class Query:
@strawberry.field @strawberry.field
def get_alarms(self) -> List[Alarm]: def get_alarms(self) -> List[Alarm]:
manager = get_manager() manager = get_manager()
return [Alarm(**a) for a in manager.get_alarms()] return [
Alarm(
id=a["id"],
cron_expression=a["cron_expression"],
is_enabled=a["is_enabled"],
)
for a in manager.get_alarms()
]
@strawberry.field @strawberry.field
def get_alarm(self, id: str) -> Optional[Alarm]: def get_alarm(self, id: str) -> Optional[Alarm]:
@@ -86,7 +92,11 @@ class Query:
alarms = manager.get_alarms() alarms = manager.get_alarms()
for a in alarms: for a in alarms:
if a["id"] == id: if a["id"] == id:
return Alarm(**a) return Alarm(
id=a["id"],
cron_expression=a["cron_expression"],
is_enabled=a["is_enabled"],
)
return None return None
@@ -111,7 +121,6 @@ class Mutation:
return Alarm( return Alarm(
id=new_id, id=new_id,
cron_expression=cron_expression, cron_expression=cron_expression,
command=command,
is_enabled=is_enabled, is_enabled=is_enabled,
) )
+12 -8
View File
@@ -102,13 +102,16 @@ def test_bugfix_default_command_uses_append_for_logs():
instead of overwriting (>) it. instead of overwriting (>) it.
""" """
from api.schema import Mutation from api.schema import Mutation
from api.crontab_manager import CrontabManager
mutation = Mutation() mutation = Mutation()
# Call the resolver directly without command to trigger default command generation # Call the resolver directly without command to trigger default command generation
alarm = mutation.set_alarm(cron_expression="0 9 * * *") alarm = mutation.set_alarm(cron_expression="0 9 * * *")
# Verify the generated command string manager = CrontabManager(tabfile=os.environ["TABFILE"])
command = alarm.command alarms = manager.get_alarms()
assert len(alarms) == 1
command = alarms[0]["command"]
assert ">> wecker.log 2>&1" in command, ( assert ">> wecker.log 2>&1" in command, (
f"Command must use append '>>' syntax. Got: {command}" f"Command must use append '>>' syntax. Got: {command}"
) )
@@ -126,21 +129,24 @@ def test_set_alarm_default_command_append():
mutation { mutation {
setAlarm(cronExpression: "0 9 * * *") { setAlarm(cronExpression: "0 9 * * *") {
id id
command
} }
} }
""" """
res = client.post("/graphql", json={"query": mutation}, headers=headers) res = client.post("/graphql", json={"query": mutation}, headers=headers)
assert res.status_code == 200 assert res.status_code == 200
data = res.json()["data"]["setAlarm"] alarm_id = res.json()["data"]["setAlarm"]["id"]
from api.crontab_manager import CrontabManager
manager = CrontabManager(tabfile=os.environ["TABFILE"])
alarms = manager.get_alarms()
assert len(alarms) == 1
command = alarms[0]["command"]
# Assert the command contains the correct append syntax (>>) and not just overwrite (>) # Assert the command contains the correct append syntax (>>) and not just overwrite (>)
command = data["command"]
assert ">> wecker.log 2>&1" in command assert ">> wecker.log 2>&1" in command
assert "> wecker.log 2>&1" not in command.replace(">> wecker.log", "REPLACED") assert "> wecker.log 2>&1" not in command.replace(">> wecker.log", "REPLACED")
# Cleanup so we don't break subsequent tests # Cleanup so we don't break subsequent tests
alarm_id = data["id"]
mutation_delete = f""" mutation_delete = f"""
mutation {{ mutation {{
deleteAlarm(id: "{alarm_id}") deleteAlarm(id: "{alarm_id}")
@@ -202,7 +208,6 @@ def test_graphql_workflow():
setAlarm(cronExpression: "30 7 * * *", isEnabled: true) { setAlarm(cronExpression: "30 7 * * *", isEnabled: true) {
id id
cronExpression cronExpression
command
isEnabled isEnabled
} }
} }
@@ -211,7 +216,6 @@ def test_graphql_workflow():
assert res.status_code == 200 assert res.status_code == 200
alarm = res.json()["data"]["setAlarm"] alarm = res.json()["data"]["setAlarm"]
assert alarm["cronExpression"] == "30 7 * * *" assert alarm["cronExpression"] == "30 7 * * *"
assert "wecker.py" in alarm["command"]
assert alarm["isEnabled"] is True assert alarm["isEnabled"] is True
alarm_id = alarm["id"] alarm_id = alarm["id"]