From 93344c95731afc65e25ddd3d4414531a0380f6b4 Mon Sep 17 00:00:00 2001
From: Patrick Jentsch
Date: Fri, 6 Dec 2024 11:41:36 +0100
Subject: [PATCH] Add job namespace and remove old json_routes logic
---
app/__init__.py | 3 +
app/blueprints/jobs/__init__.py | 2 +-
app/blueprints/jobs/json_routes.py | 72 ------------------------
app/namespaces/jobs.py | 8 +--
app/static/js/app.js | 1 +
app/static/js/app/endpoints/jobs.js | 37 ++++++++++++
app/static/js/app/endpoints/users.js | 18 +++---
app/static/js/resource-lists/job-list.js | 10 ++--
app/templates/_base/scripts.html.j2 | 1 +
app/templates/jobs/job.html.j2 | 40 +++++++------
10 files changed, 82 insertions(+), 110 deletions(-)
delete mode 100644 app/blueprints/jobs/json_routes.py
create mode 100644 app/static/js/app/endpoints/jobs.js
diff --git a/app/__init__.py b/app/__init__.py
index 9953a1cf..4a198125 100644
--- a/app/__init__.py
+++ b/app/__init__.py
@@ -133,6 +133,9 @@ def create_app(config: Config = Config) -> Flask:
from .namespaces.cqi_over_sio import CQiOverSocketIONamespace
socketio.on_namespace(CQiOverSocketIONamespace('/cqi_over_sio'))
+ from .namespaces.jobs import JobsNamespace
+ socketio.on_namespace(JobsNamespace('/jobs'))
+
from .namespaces.users import UsersNamespace
socketio.on_namespace(UsersNamespace('/users'))
# endregion SocketIO Namespaces
diff --git a/app/blueprints/jobs/__init__.py b/app/blueprints/jobs/__init__.py
index 1350e7e1..1f311bd2 100644
--- a/app/blueprints/jobs/__init__.py
+++ b/app/blueprints/jobs/__init__.py
@@ -15,4 +15,4 @@ def before_request():
pass
-from . import routes, json_routes
+from . import routes
diff --git a/app/blueprints/jobs/json_routes.py b/app/blueprints/jobs/json_routes.py
deleted file mode 100644
index 54a682cb..00000000
--- a/app/blueprints/jobs/json_routes.py
+++ /dev/null
@@ -1,72 +0,0 @@
-from flask import abort, current_app
-from flask_login import current_user
-from threading import Thread
-from app import db
-from app.decorators import admin_required, content_negotiation
-from app.models import Job, JobStatus
-from . import bp
-
-
-@bp.route('/', methods=['DELETE'])
-@content_negotiation(produces='application/json')
-def delete_job(job_id):
- def _delete_job(app, job_id):
- with app.app_context():
- job = Job.query.get(job_id)
- job.delete()
- db.session.commit()
-
- job = Job.query.get_or_404(job_id)
- if not (job.user == current_user or current_user.is_administrator):
- abort(403)
- thread = Thread(
- target=_delete_job,
- args=(current_app._get_current_object(), job_id)
- )
- thread.start()
- response_data = {
- 'message': f'Job "{job.title}" marked for deletion'
- }
- return response_data, 202
-
-
-@bp.route('//log')
-@admin_required
-@content_negotiation(produces='application/json')
-def job_log(job_id):
- job = Job.query.get_or_404(job_id)
- if job.status not in [JobStatus.COMPLETED, JobStatus.FAILED]:
- response = {'errors': {'message': 'Job status is not completed or failed'}}
- return response, 409
- with open(job.path / 'pipeline_data' / 'logs' / 'pyflow_log.txt') as log_file:
- log = log_file.read()
- response_data = {
- 'jobLog': log
- }
- return response_data, 200
-
-
-@bp.route('//restart', methods=['POST'])
-@content_negotiation(produces='application/json')
-def restart_job(job_id):
- def _restart_job(app, job_id):
- with app.app_context():
- job = Job.query.get(job_id)
- job.restart()
- db.session.commit()
-
- job = Job.query.get_or_404(job_id)
- if not (job.user == current_user or current_user.is_administrator):
- abort(403)
- if job.status == JobStatus.FAILED:
- response = {'errors': {'message': 'Job status is not "failed"'}}
- return response, 409
- thread = Thread(
- target=_restart_job,
- args=(current_app._get_current_object(), job_id)
- )
- thread.start()
- response_data = {
- 'message': f'Job "{job.title}" marked for restarting'
- }
- return response_data, 202
diff --git a/app/namespaces/jobs.py b/app/namespaces/jobs.py
index 03da5543..53c6a2b4 100644
--- a/app/namespaces/jobs.py
+++ b/app/namespaces/jobs.py
@@ -13,14 +13,14 @@ def _delete_job(app: Flask, job_id: int):
db.session.commit()
-def _restart_job(app, job_id):
+def _restart_job(app: Flask, job_id: int):
with app.app_context():
job = Job.query.get(job_id)
job.restart()
db.session.commit()
-class UsersNamespace(Namespace):
+class JobsNamespace(Namespace):
@socketio_login_required
def on_delete(self, job_hashid: str) -> dict:
job_id = hashids.decode(job_hashid)
@@ -52,7 +52,7 @@ class UsersNamespace(Namespace):
}
@socketio_admin_required
- def on_log(self, job_hashid: str):
+ def on_log(self, job_hashid: str) -> dict:
job_id = hashids.decode(job_hashid)
if not isinstance(job_id, int):
@@ -76,7 +76,7 @@ class UsersNamespace(Namespace):
}
socketio_login_required
- def on_restart(self, job_hashid: str):
+ def on_restart(self, job_hashid: str) -> dict:
job_id = hashids.decode(job_hashid)
if not isinstance(job_id, int):
diff --git a/app/static/js/app.js b/app/static/js/app.js
index fc52a7f2..e9469df4 100644
--- a/app/static/js/app.js
+++ b/app/static/js/app.js
@@ -3,6 +3,7 @@ nopaque.App = class App {
this.socket = io({transports: ['websocket'], upgrade: false});
// Endpoints
+ this.jobs = new nopaque.app.endpoints.Jobs(this);
this.users = new nopaque.app.endpoints.Users(this);
// Extensions
diff --git a/app/static/js/app/endpoints/jobs.js b/app/static/js/app/endpoints/jobs.js
new file mode 100644
index 00000000..aa42fa81
--- /dev/null
+++ b/app/static/js/app/endpoints/jobs.js
@@ -0,0 +1,37 @@
+nopaque.app.endpoints.Jobs = class Jobs {
+ constructor(app) {
+ this.app = app;
+
+ this.socket = io('/jobs', {transports: ['websocket'], upgrade: false});
+ }
+
+ async delete(id) {
+ const response = await this.socket.emitWithAck('delete', id);
+
+ if (response.status != 202) {
+ throw new Error(`[${response.status}] ${response.statusText}`);
+ }
+
+ return response.body;
+ }
+
+ async log(id) {
+ const response = await this.socket.emitWithAck('log', id);
+
+ if (response.status != 200) {
+ throw new Error(`[${response.status}] ${response.statusText}`);
+ }
+
+ return response.body;
+ }
+
+ async restart(id) {
+ const response = await this.socket.emitWithAck('restart', id);
+
+ if (response.status != 202) {
+ throw new Error(`[${response.status}] ${response.statusText}`);
+ }
+
+ return response.body;
+ }
+}
diff --git a/app/static/js/app/endpoints/users.js b/app/static/js/app/endpoints/users.js
index b1bfa7fd..b7dec629 100644
--- a/app/static/js/app/endpoints/users.js
+++ b/app/static/js/app/endpoints/users.js
@@ -5,8 +5,8 @@ nopaque.app.endpoints.Users = class Users {
this.socket = io('/users', {transports: ['websocket'], upgrade: false});
}
- async get(userId) {
- const response = await this.socket.emitWithAck('get', userId);
+ async get(id) {
+ const response = await this.socket.emitWithAck('get', id);
if (response.status !== 200) {
throw new Error(`[${response.status}] ${response.statusText}`);
@@ -15,27 +15,29 @@ nopaque.app.endpoints.Users = class Users {
return response.body;
}
- async subscribe(userId) {
- const response = await this.socket.emitWithAck('subscribe', userId);
+ async subscribe(id) {
+ const response = await this.socket.emitWithAck('subscribe', id);
if (response.status != 200) {
throw new Error(`[${response.status}] ${response.statusText}`);
}
}
- async unsubscribe(userId) {
- const response = await this.socket.emitWithAck('unsubscribe', userId);
+ async unsubscribe(id) {
+ const response = await this.socket.emitWithAck('unsubscribe', id);
if (response.status != 200) {
throw new Error(`[${response.status}] ${response.statusText}`);
}
}
- async delete(userId) {
- const response = await this.socket.emitWithAck('delete', userId);
+ async delete(id) {
+ const response = await this.socket.emitWithAck('delete', id);
if (response.status != 202) {
throw new Error(`[${response.status}] ${response.statusText}`);
}
+
+ return response.body;
}
}
diff --git a/app/static/js/resource-lists/job-list.js b/app/static/js/resource-lists/job-list.js
index 43a3b58f..cef0ea83 100644
--- a/app/static/js/resource-lists/job-list.js
+++ b/app/static/js/resource-lists/job-list.js
@@ -136,8 +136,9 @@ nopaque.resource_lists.JobList = class JobList extends nopaque.resource_lists.Re
}
);
let confirmElement = modalElement.querySelector('.action-button[data-action="confirm"]');
- confirmElement.addEventListener('click', (event) => {
- nopaque.requests.jobs.entity.delete(itemId);
+ confirmElement.addEventListener('click', async (event) => {
+ const message = await app.jobs.delete(itemId);
+ app.ui.flash(message, 'job');
});
modal.open();
break;
@@ -221,8 +222,9 @@ nopaque.resource_lists.JobList = class JobList extends nopaque.resource_lists.Re
);
let confirmElement = modalElement.querySelector('.action-button[data-action="confirm"]');
confirmElement.addEventListener('click', (event) => {
- this.selectedItemIds.forEach(selectedItemId => {
- nopaque.requests.jobs.entity.delete(selectedItemId);
+ this.selectedItemIds.forEach(async (selectedItemId) => {
+ const message = await app.jobs.delete(selectedItemId);
+ app.ui.flash(message, 'job');
});
this.selectedItemIds.clear();
this.renderingItemSelection();
diff --git a/app/templates/_base/scripts.html.j2 b/app/templates/_base/scripts.html.j2
index 5bb3d1de..d4eb6c7f 100644
--- a/app/templates/_base/scripts.html.j2
+++ b/app/templates/_base/scripts.html.j2
@@ -11,6 +11,7 @@
'js/app.js',
'js/app/index.js',
'js/app/endpoints/index.js',
+ 'js/app/endpoints/jobs.js',
'js/app/endpoints/users.js',
'js/app/extensions/index.js',
'js/app/extensions/toaster.js',
diff --git a/app/templates/jobs/job.html.j2 b/app/templates/jobs/job.html.j2
index b852b5ed..1430666f 100644
--- a/app/templates/jobs/job.html.j2
+++ b/app/templates/jobs/job.html.j2
@@ -137,28 +137,26 @@
{% block scripts %}
{{ super() }}
{% endblock scripts %}