Skip to content

Commit 76d6e52

Browse files
committed
Coderabbitai review bug and other fixes
- Fix the conditional commas in the INSERT values list of chain.tasks - _init_ is not __init__ — this initializer never runs. - Prevent updating non-existent database_connection column - Another unguarded check in pg_timetable init - Change terminology from pgAgent Jobs/steps to pgTimetable Chain/tasks - Make task ordering less fragile - Fix some issues with parameter update when reordering
1 parent cbc371e commit 76d6e52

6 files changed

Lines changed: 128 additions & 80 deletions

File tree

web/pgadmin/browser/server_groups/servers/pg_timetable/__init__.py

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -170,14 +170,16 @@ def wrap(self, *args, **kwargs):
170170
self.template_path = 'pgt_chain/sql/default'
171171

172172
if 'timetable' not in self.manager.db_info:
173-
_, res = self.conn.execute_dict("""
173+
status, res = self.conn.execute_dict("""
174174
SELECT EXISTS(
175175
SELECT 1 FROM information_schema.columns
176176
WHERE
177177
table_schema='timetable' AND table_name='task' AND
178178
column_name='database_connection'
179179
) has_connstr""")
180180

181+
if not status:
182+
return internal_server_error(errormsg=res)
181183
self.manager.db_info['timetable'] = res['rows'][0]
182184

183185
return f(self, *args, **kwargs)
@@ -271,7 +273,7 @@ def properties(self, gid, sid, chain_id=None):
271273

272274
@check_precondition
273275
def create(self, gid, sid):
274-
"""Create the pgAgent job."""
276+
"""Create the pgTimeTable chain."""
275277
required_args = [
276278
'chain_name'
277279
]
@@ -492,21 +494,15 @@ def _process_ctasks(self, chain_id, ctasks):
492494
def _upsert_task_params(self, task_id, parameters):
493495
if not parameters:
494496
return True, None
495-
if isinstance(parameters, dict):
496-
parameters = parameters.get('added', []) + parameters.get('changed', [])
497-
if not parameters:
498-
return True, None
499-
status, res = self.conn.execute_void(
500-
"DELETE FROM timetable.parameter WHERE task_id = %s", (task_id,)
501-
)
502-
if not status:
503-
return status, res
504-
for idx, param in enumerate(parameters):
497+
498+
def _insert_param(idx, param):
505499
if not isinstance(param, dict):
506500
param = {'order_id': idx + 1, 'value': str(param)}
501+
param.pop('_t', None)
507502
order_id = param.get('order_id')
508503
if order_id is None:
509504
order_id = idx + 1
505+
order_id = int(order_id)
510506
val = param.get('value', '')
511507
if val is None:
512508
val = ''
@@ -517,14 +513,28 @@ def _upsert_task_params(self, task_id, parameters):
517513
except (ValueError, TypeError):
518514
sql = "INSERT INTO timetable.parameter(task_id, order_id, value) VALUES (%s, %s, to_jsonb(%s::text))"
519515
params = (task_id, order_id, val)
520-
status, res = self.conn.execute_void(sql, params)
516+
return self.conn.execute_void(sql, params)
517+
518+
status, res = self.conn.execute_void(
519+
"DELETE FROM timetable.parameter WHERE task_id = %s", (task_id,)
520+
)
521+
if not status:
522+
return status, res
523+
524+
if isinstance(parameters, dict):
525+
all_params = parameters.get('changed', []) + parameters.get('added', [])
526+
else:
527+
all_params = parameters
528+
529+
for idx, param in enumerate(all_params):
530+
status, res = _insert_param(idx, param)
521531
if not status:
522532
return status, res
523533
return True, None
524534

525535
@check_precondition
526536
def delete(self, gid, sid, chain_id=None):
527-
"""Delete the pgAgent Job."""
537+
"""Delete the pgTimeTable chain."""
528538

529539
if chain_id is None:
530540
data = request.form if request.form else json.loads(
@@ -646,8 +656,8 @@ def sql(self, gid, sid, chain_id):
646656
@check_precondition
647657
def run_now(self, gid, sid, chain_id):
648658
"""
649-
This function will set the next run to now, to inform the pgAgent to
650-
run the job now.
659+
This function will set the next run to now, to inform pgTimeTable to
660+
run the chain now.
651661
"""
652662
status, res = self.conn.execute_void(
653663
render_template(

web/pgadmin/browser/server_groups/servers/pg_timetable/static/js/pgt_chain.ui.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,14 @@ export default class PgtChainSchema extends BaseUISchema {
109109
depChange: (state, source, topState, actionObj) => {
110110
if (actionObj.type === SCHEMA_STATE_ACTIONS.ADD_ROW && state?.ctasks) {
111111
const tasks = state.ctasks;
112-
const lastOrder = tasks.reduce((max, t) => Math.max(max, t.task_order || 0), 0);
113-
tasks[tasks.length - 1].task_order = lastOrder + 10;
112+
const addedTask = actionObj.value?.cid
113+
? tasks.find(t => t.cid === actionObj.value.cid)
114+
: tasks[tasks.length - 1];
115+
116+
if (addedTask && (addedTask.task_order === undefined || addedTask.task_order === null)) {
117+
const lastOrder = tasks.reduce((max, t) => Math.max(max, parseInt(t.task_order, 10) || 0), 0);
118+
addedTask.task_order = lastOrder + 10;
119+
}
114120
}
115121
return state;
116122
},

web/pgadmin/browser/server_groups/servers/pg_timetable/tasks/__init__.py

Lines changed: 70 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
#
88
##########################################################################
99

10-
"""Implements pgAgent Job Step Node"""
10+
"""Implements pgTimeTable Chain Task Node"""
1111

1212
import json
1313
from functools import wraps
@@ -107,7 +107,7 @@ class ChainTaskView(PGChildNodeView)
107107
108108
A view class for ChainTask node derived from PGChildNodeView.
109109
This class is responsible for all the stuff related to view like
110-
updating job step node, showing properties, showing sql in sql pane.
110+
updating chain task, showing properties, showing sql in sql pane.
111111
112112
Methods:
113113
-------
@@ -120,29 +120,29 @@ class ChainTaskView(PGChildNodeView)
120120
manager,conn & template_path properties to self
121121
122122
* list()
123-
- This function is used to list all the job step nodes within that
123+
- This function is used to list all the chain tasks within that
124124
collection.
125125
126126
* nodes()
127127
- This function will used to create all the child node within that
128128
collection.
129-
Here it will create all the job step node.
129+
Here it will create all the chain task nodes.
130130
131131
* properties(gid, sid, chain_id, task_id)
132-
- This function will show the properties of the selected job step node
132+
- This function will show the properties of the selected chain task
133133
134134
* update(gid, sid, chain_id, task_id)
135-
- This function will update the data for the selected job step node
135+
- This function will update the data for the selected chain task
136136
137137
* msql(gid, sid, chain_id, task_id)
138138
- This function is used to return modified SQL for the selected
139-
job step node
139+
chain task
140140
141141
* sql(gid, sid, chain_id, jscid)
142142
- Dummy response for sql panel
143143
144144
* delete(gid, sid, chain_id, jscid)
145-
- Drops job step
145+
- Drops chain task
146146
"""
147147

148148
node_type = blueprint.node_type
@@ -168,7 +168,7 @@ class ChainTaskView(PGChildNodeView)
168168
'stats': [{'get': 'statistics'}]
169169
})
170170

171-
def _init_(self, **kwargs):
171+
def __init__(self, **kwargs):
172172
"""
173173
Method is used to initialize the ChainTaskView and its base view.
174174
Initialize all the variables create/used dynamically like conn,
@@ -220,13 +220,13 @@ def wrap(*args, **kwargs):
220220
@check_precondition
221221
def list(self, gid, sid, chain_id):
222222
"""
223-
This function is used to list all the job step nodes within
223+
This function is used to list all the chain tasks within
224224
that collection.
225225
226226
Args:
227227
gid: Server Group ID
228228
sid: Server ID
229-
chain_id: Job ID
229+
chain_id: Chain ID
230230
"""
231231
sql = render_template(
232232
"/".join([self.template_path, self._PROPERTIES_SQL]),
@@ -253,12 +253,12 @@ def nodes(self, gid, sid, chain_id, task_id=None):
253253
"""
254254
This function is used to create all the child nodes
255255
within the collection.
256-
Here it will create all the job step nodes.
256+
Here it will create all the chain tasks.
257257
258258
Args:
259259
gid: Server Group ID
260260
sid: Server ID
261-
chain_id: Job ID
261+
chain_id: Chain ID
262262
"""
263263
res = []
264264
sql = render_template(
@@ -311,12 +311,12 @@ def nodes(self, gid, sid, chain_id, task_id=None):
311311
@check_precondition
312312
def properties(self, gid, sid, chain_id, task_id):
313313
"""
314-
This function will show the properties of the selected job step node.
314+
This function will show the properties of the selected chain task.
315315
316316
Args:
317317
gid: Server Group ID
318318
sid: Server ID
319-
chain_id: Job ID
319+
chain_id: Chain ID
320320
task_id: ChainTask ID
321321
"""
322322
sql = render_template(
@@ -346,27 +346,16 @@ def properties(self, gid, sid, chain_id, task_id):
346346
@check_precondition
347347
def create(self, gid, sid, chain_id):
348348
"""
349-
This function will update the data for the selected job step node.
350-
351-
Args:
352-
gid: Server Group ID
353-
sid: Server ID
354-
chain_id: Job ID
349+
This function will create the chain task.
355350
"""
356-
data = {}
357-
if request.args:
358-
for k, v in request.args.items():
359-
try:
360-
data[k] = json.loads(
361-
v.decode('utf-8') if hasattr(v, 'decode') else v
362-
)
363-
except ValueError:
364-
data[k] = v
365-
else:
366-
data = json.loads(request.data.decode())
351+
data = request.form if request.form else json.loads(
352+
request.data.decode('utf-8')
353+
)
367354

368355
if 'parameters' in data:
369356
params_raw = data.get('parameters', [])
357+
if isinstance(params_raw, str):
358+
params_raw = json.loads(params_raw)
370359
if isinstance(params_raw, dict):
371360
params_raw = params_raw.get('added', []) + params_raw.get('changed', [])
372361
cleaned = []
@@ -409,7 +398,7 @@ def create(self, gid, sid, chain_id):
409398
if len(res['rows']) == 0:
410399
return gone(
411400
errormsg=gettext(
412-
"Job step creation failed."
401+
"Chain task creation failed."
413402
)
414403
)
415404
row = res['rows'][0]
@@ -425,12 +414,12 @@ def create(self, gid, sid, chain_id):
425414
@check_precondition
426415
def update(self, gid, sid, chain_id, task_id):
427416
"""
428-
This function will update the data for the selected job step node.
417+
This function will update the data for the selected chain task.
429418
430419
Args:
431420
gid: Server Group ID
432421
sid: Server ID
433-
chain_id: Job ID
422+
chain_id: Chain ID
434423
task_id: ChainTask ID
435424
"""
436425
data = request.form if request.form else json.loads(
@@ -439,19 +428,26 @@ def update(self, gid, sid, chain_id, task_id):
439428

440429
if 'parameters' in data:
441430
params_raw = data.get('parameters', [])
431+
if isinstance(params_raw, str):
432+
params_raw = json.loads(params_raw)
433+
442434
if isinstance(params_raw, dict):
443-
params_raw = params_raw.get('added', []) + params_raw.get('changed', [])
435+
params_list = params_raw.get('changed', []) + params_raw.get('added', [])
436+
else:
437+
params_list = params_raw
438+
444439
cleaned = []
445-
for idx, param in enumerate(params_raw):
440+
for idx, param in enumerate(params_list):
446441
if not isinstance(param, dict):
447-
cleaned.append({'order_id': idx + 1, 'value': str(param), '_is_json': False})
448-
else:
449-
try:
450-
json.loads(param.get('value', ''))
451-
param['_is_json'] = True
452-
except (ValueError, TypeError):
453-
param['_is_json'] = False
454-
cleaned.append(param)
442+
param = {'order_id': idx + 1, 'value': str(param)}
443+
param.pop('_t', None)
444+
param.setdefault('value', '')
445+
try:
446+
json.loads(param['value'])
447+
param['_is_json'] = True
448+
except (ValueError, TypeError):
449+
param['_is_json'] = False
450+
cleaned.append(param)
455451
data['parameters'] = cleaned
456452

457453
sql = render_template(
@@ -482,7 +478,7 @@ def update(self, gid, sid, chain_id, task_id):
482478
if len(res['rows']) == 0:
483479
return gone(
484480
errormsg=gettext(
485-
"Job step update failed."
481+
"Chain task update failed."
486482
)
487483
)
488484
row = res['rows'][0]
@@ -498,7 +494,7 @@ def update(self, gid, sid, chain_id, task_id):
498494

499495
@check_precondition
500496
def delete(self, gid, sid, chain_id, task_id=None):
501-
"""Delete the Job step."""
497+
"""Delete the chain task."""
502498

503499
if task_id is None:
504500
data = request.form if request.form else json.loads(
@@ -523,13 +519,13 @@ def delete(self, gid, sid, chain_id, task_id=None):
523519
def msql(self, gid, sid, chain_id, task_id=None):
524520
"""
525521
This function is used to return modified SQL for the selected
526-
job step node.
522+
chain task.
527523
528524
Args:
529525
gid: Server Group ID
530526
sid: Server ID
531-
chain_id: Job ID
532-
task_id: Job Step ID
527+
chain_id: Chain ID
528+
task_id: Chain Task ID
533529
"""
534530
data = {}
535531
sql = ''
@@ -539,6 +535,30 @@ def msql(self, gid, sid, chain_id, task_id=None):
539535
except ValueError:
540536
data[k] = v
541537

538+
if 'parameters' in data:
539+
params_raw = data.get('parameters', [])
540+
if isinstance(params_raw, str):
541+
params_raw = json.loads(params_raw)
542+
543+
if isinstance(params_raw, dict):
544+
params_list = params_raw.get('changed', []) + params_raw.get('added', [])
545+
else:
546+
params_list = params_raw
547+
548+
cleaned = []
549+
for idx, param in enumerate(params_list):
550+
if not isinstance(param, dict):
551+
param = {'order_id': idx + 1, 'value': str(param)}
552+
param.pop('_t', None)
553+
param.setdefault('value', '')
554+
try:
555+
json.loads(param['value'])
556+
param['_is_json'] = True
557+
except (ValueError, TypeError):
558+
param['_is_json'] = False
559+
cleaned.append(param)
560+
data['parameters'] = cleaned
561+
542562
if task_id is None:
543563
sql = render_template(
544564
"/".join([self.template_path, self._CREATE_SQL]),

0 commit comments

Comments
 (0)