poll the backend for active users instead of keeping the connection open so it does not hang on a process w/ burnettk
This commit is contained in:
parent
c5d7a87e61
commit
d6724087f6
|
@ -1,11 +1,10 @@
|
||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
from typing import Generator
|
|
||||||
|
|
||||||
import flask.wrappers
|
import flask.wrappers
|
||||||
from flask import current_app
|
|
||||||
from flask import g
|
from flask import g
|
||||||
from flask import stream_with_context
|
from flask import jsonify
|
||||||
|
from flask import make_response
|
||||||
from flask.wrappers import Response
|
from flask.wrappers import Response
|
||||||
|
|
||||||
from spiffworkflow_backend.models.active_user import ActiveUserModel
|
from spiffworkflow_backend.models.active_user import ActiveUserModel
|
||||||
|
@ -24,30 +23,19 @@ def active_user_updates(last_visited_identifier: str) -> Response:
|
||||||
db.session.add(active_user)
|
db.session.add(active_user)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
return Response(
|
active_user.last_seen_in_seconds = round(time.time())
|
||||||
stream_with_context(_active_user_updates(last_visited_identifier, active_user=active_user)),
|
db.session.add(active_user)
|
||||||
mimetype="text/event-stream",
|
db.session.commit()
|
||||||
headers={"X-Accel-Buffering": "no"},
|
|
||||||
|
cutoff_time_in_seconds = time.time() - 30
|
||||||
|
active_users = (
|
||||||
|
UserModel.query.join(ActiveUserModel)
|
||||||
|
.filter(ActiveUserModel.last_visited_identifier == last_visited_identifier)
|
||||||
|
.filter(ActiveUserModel.last_seen_in_seconds > cutoff_time_in_seconds)
|
||||||
|
.filter(UserModel.id != g.user.id)
|
||||||
|
.all()
|
||||||
)
|
)
|
||||||
|
return make_response(jsonify(active_users), 200)
|
||||||
|
|
||||||
def _active_user_updates(last_visited_identifier: str, active_user: ActiveUserModel) -> Generator[str, None, None]:
|
|
||||||
while True:
|
|
||||||
active_user.last_seen_in_seconds = round(time.time())
|
|
||||||
db.session.add(active_user)
|
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
cutoff_time_in_seconds = time.time() - 15
|
|
||||||
active_users = (
|
|
||||||
UserModel.query.join(ActiveUserModel)
|
|
||||||
.filter(ActiveUserModel.last_visited_identifier == last_visited_identifier)
|
|
||||||
.filter(ActiveUserModel.last_seen_in_seconds > cutoff_time_in_seconds)
|
|
||||||
.filter(UserModel.id != g.user.id)
|
|
||||||
.all()
|
|
||||||
)
|
|
||||||
yield f"data: {current_app.json.dumps(active_users)} \n\n"
|
|
||||||
|
|
||||||
time.sleep(5)
|
|
||||||
|
|
||||||
|
|
||||||
def active_user_unregister(last_visited_identifier: str) -> flask.wrappers.Response:
|
def active_user_unregister(last_visited_identifier: str) -> flask.wrappers.Response:
|
||||||
|
|
|
@ -247,15 +247,21 @@ export const splitProcessModelId = (processModelId: string) => {
|
||||||
export const refreshAtInterval = (
|
export const refreshAtInterval = (
|
||||||
interval: number,
|
interval: number,
|
||||||
timeout: number,
|
timeout: number,
|
||||||
func: Function
|
periodicFunction: Function,
|
||||||
|
cleanupFunction?: Function
|
||||||
) => {
|
) => {
|
||||||
const intervalRef = setInterval(() => func(), interval * 1000);
|
const intervalRef = setInterval(() => periodicFunction(), interval * 1000);
|
||||||
const timeoutRef = setTimeout(
|
const timeoutRef = setTimeout(() => {
|
||||||
() => clearInterval(intervalRef),
|
clearInterval(intervalRef);
|
||||||
timeout * 1000
|
if (cleanupFunction) {
|
||||||
);
|
cleanupFunction();
|
||||||
|
}
|
||||||
|
}, timeout * 1000);
|
||||||
return () => {
|
return () => {
|
||||||
clearInterval(intervalRef);
|
clearInterval(intervalRef);
|
||||||
|
if (cleanupFunction) {
|
||||||
|
cleanupFunction();
|
||||||
|
}
|
||||||
clearTimeout(timeoutRef);
|
clearTimeout(timeoutRef);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -25,9 +25,7 @@ import Col from 'react-bootstrap/Col';
|
||||||
import Editor, { DiffEditor } from '@monaco-editor/react';
|
import Editor, { DiffEditor } from '@monaco-editor/react';
|
||||||
|
|
||||||
import MDEditor from '@uiw/react-md-editor';
|
import MDEditor from '@uiw/react-md-editor';
|
||||||
import { fetchEventSource } from '@microsoft/fetch-event-source';
|
import HttpService from '../services/HttpService';
|
||||||
import { BACKEND_BASE_URL } from '../config';
|
|
||||||
import HttpService, { getBasicHeaders } from '../services/HttpService';
|
|
||||||
import ReactDiagramEditor from '../components/ReactDiagramEditor';
|
import ReactDiagramEditor from '../components/ReactDiagramEditor';
|
||||||
import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
|
import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
|
||||||
import useAPIError from '../hooks/UseApiError';
|
import useAPIError from '../hooks/UseApiError';
|
||||||
|
@ -35,6 +33,8 @@ import {
|
||||||
makeid,
|
makeid,
|
||||||
modifyProcessIdentifierForPathParam,
|
modifyProcessIdentifierForPathParam,
|
||||||
encodeBase64,
|
encodeBase64,
|
||||||
|
refreshAtInterval,
|
||||||
|
REFRESH_TIMEOUT_SECONDS,
|
||||||
} from '../helpers';
|
} from '../helpers';
|
||||||
import {
|
import {
|
||||||
CarbonComboBoxProcessSelection,
|
CarbonComboBoxProcessSelection,
|
||||||
|
@ -134,6 +134,12 @@ export default function ProcessModelEditDiagram() {
|
||||||
|
|
||||||
const lastVisitedIdentifier = encodeBase64(window.location.pathname);
|
const lastVisitedIdentifier = encodeBase64(window.location.pathname);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const updateActiveUsers = () => {
|
||||||
|
HttpService.makeCallToBackend({
|
||||||
|
path: `/active-users/updates/${lastVisitedIdentifier}`,
|
||||||
|
successCallback: setActiveUsers,
|
||||||
|
});
|
||||||
|
};
|
||||||
// Grab all available process models in case we need to search for them.
|
// Grab all available process models in case we need to search for them.
|
||||||
// Taken from the Process Group List
|
// Taken from the Process Group List
|
||||||
const processResults = (result: any) => {
|
const processResults = (result: any) => {
|
||||||
|
@ -155,30 +161,14 @@ export default function ProcessModelEditDiagram() {
|
||||||
successCallback: setActiveUsers,
|
successCallback: setActiveUsers,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
fetchEventSource(
|
updateActiveUsers();
|
||||||
`${BACKEND_BASE_URL}/active-users/updates/${lastVisitedIdentifier}`,
|
|
||||||
{
|
|
||||||
headers: getBasicHeaders(),
|
|
||||||
onmessage(ev) {
|
|
||||||
const retValue = JSON.parse(ev.data);
|
|
||||||
if ('error_code' in retValue) {
|
|
||||||
addError(retValue);
|
|
||||||
} else {
|
|
||||||
setActiveUsers(retValue);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onclose() {
|
|
||||||
unregisterUser();
|
|
||||||
},
|
|
||||||
onerror(err: any) {
|
|
||||||
throw err;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// FIXME: this is not getting called when navigating away from this page.
|
return refreshAtInterval(
|
||||||
// we do not know why yet.
|
15,
|
||||||
return unregisterUser;
|
REFRESH_TIMEOUT_SECONDS,
|
||||||
|
updateActiveUsers,
|
||||||
|
unregisterUser
|
||||||
|
);
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []); // it is critical to only run this once.
|
}, []); // it is critical to only run this once.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue