Fix bit(1) import coercion and subpath login redirect

The WJ import loader coerced MySQL bit(1) columns with bool(), which is
always true for the bytes pymysql returns; applications.isinstallable
and notifications.isshopfloor imported as 1 for every row. Route both
through the existing _truthy_bit helper. The loader also no longer
requires the employee source database: without it, notifications keep
the bare SSO and the verify stage skips the employee count.

Under a subpath mount, the session-expiry interceptor stored the
browser path (which includes the mount base) as the login redirect,
and the router applied its base again after login. Add stripBase()
and store base-free router paths; the login page also strips a stale
base from old bookmarked redirects.

Also point the external-plugin CI helper at the repo's GitHub URL.
This commit is contained in:
2026-07-16 14:08:19 -04:00
parent a9d0dfff4a
commit 95cfa9e939
6 changed files with 43 additions and 17 deletions

View File

@@ -16,6 +16,8 @@ the machineid->assetid crosswalk this harness persists.
import argparse
import pymysql
from .harness import Harness
@@ -451,7 +453,7 @@ def stage_applications(h):
continue
payload = {'appname': name, 'appdescription': a['appdescription'],
'supportteamid': h.ids.get('supportteam', a['supportteamid']),
'isinstallable': bool(a['isinstallable']),
'isinstallable': _truthy_bit(a['isinstallable']),
'applicationnotes': a['applicationnotes'], 'installpath': a['installpath'],
'applicationlink': a['applicationlink'],
'documentationpath': a['documentationpath']}
@@ -534,13 +536,17 @@ def stage_notifications(h):
# SSO -> "First Last" from the employee source, so recognition/training
# notifications display a name, not a bare SSO (the model shows employeename
# or falls back to employeesso).
# or falls back to employeesso). Employee source is optional: without it
# (shopdb-only import) notifications keep the bare SSO.
ssoname = {}
for e in h.source.rows('wjf_employees_src',
'SELECT SSO, First_Name, Last_Name FROM employees'):
full = f"{(e['First_Name'] or '').strip()} {(e['Last_Name'] or '').strip()}".strip()
if full:
ssoname[str(e['SSO']).strip()] = full
try:
for e in h.source.rows('wjf_employees_src',
'SELECT SSO, First_Name, Last_Name FROM employees'):
full = f"{(e['First_Name'] or '').strip()} {(e['Last_Name'] or '').strip()}".strip()
if full:
ssoname[str(e['SSO']).strip()] = full
except pymysql.err.OperationalError:
print(' (no wjf_employees_src - notifications keep bare SSOs)')
def _names(employeesso):
if not employeesso:
@@ -566,7 +572,7 @@ def stage_notifications(h):
'appid': h.ids.get('app', n['appid']),
'starttime': str(n['starttime']) if n['starttime'] else None,
'endtime': endtime, 'ticketnumber': n['ticketnumber'], 'link': n['link'],
'isshopfloor': bool(n['isshopfloor']), 'employeesso': n['employeesso'],
'isshopfloor': _truthy_bit(n['isshopfloor']), 'employeesso': n['employeesso'],
'employeename': _names(n['employeesso'])}
status, _ = h.post('/api/notifications', payload)
if status in (200, 201):
@@ -759,7 +765,11 @@ def stage_verify(h):
from sqlalchemy import text
report = {}
for label, sdb, sql, target_table in checks:
src = h.source.rows(sdb, sql)[0]['c']
try:
src = h.source.rows(sdb, sql)[0]['c']
except pymysql.err.OperationalError:
report[label] = 'source db absent - skipped'
continue
tgt = db.session.execute(text(f'SELECT COUNT(*) FROM {target_table}')).scalar()
report[label] = f'source~{src} target={tgt}'
return report

View File

@@ -22,7 +22,7 @@
# PLUGIN_DIR ($1) path to the plugin directory (holds manifest.json). Required.
# FRAMEWORK_REF ($2) git ref to test against in CI mode. Default: main.
# FRAMEWORK_URL framework git URL for CI mode.
# Default: https://<git-host>/ge-aerospace/shopdb-flask.git
# Default: https://github.com/ge-aero/shopdb-flask.git
# LOCAL_FRAMEWORK path to an existing framework checkout. Set it to run offline.
#
# Examples:
@@ -43,7 +43,7 @@ set -eu
PLUGIN_DIR="${PLUGIN_DIR:-${1:-}}"
FRAMEWORK_REF="${FRAMEWORK_REF:-${2:-main}}"
FRAMEWORK_URL="${FRAMEWORK_URL:-https://<git-host>/ge-aerospace/shopdb-flask.git}"
FRAMEWORK_URL="${FRAMEWORK_URL:-https://github.com/ge-aero/shopdb-flask.git}"
LOCAL_FRAMEWORK="${LOCAL_FRAMEWORK:-}"
if [ -z "$PLUGIN_DIR" ]; then