Store the policy JSON columns as TEXT for MySQL 5.6
The branch stored scopejson, desiredstate and params as sa.JSON, the only JSON columns in the product. MySQL 5.6 has no JSON type - it arrived in 5.7 - and the migration failed there with a bare syntax error near the word JSON. They are TEXT now, through a JSONText type decorator, so the seventeen call sites still assign and read dicts. The convention elsewhere is already TEXT: guides.blocksjson, geenforce.manifestjson. Spec regenerated: 331 paths, 477 operations across 22 surfaces.
This commit is contained in:
44
plugins/computers/models/jsontext.py
Normal file
44
plugins/computers/models/jsontext.py
Normal file
@@ -0,0 +1,44 @@
|
||||
"""JSON stored in a TEXT column.
|
||||
|
||||
MySQL 5.6 has no JSON type - it arrived in 5.7 - and a site is still running
|
||||
5.6, where `sa.JSON()` fails at migration time with a bare syntax error near the
|
||||
word JSON. Every other JSON-bearing column in this product is already TEXT for
|
||||
that reason: guides' `blocksjson`, GE-Enforce's `manifestjson`.
|
||||
|
||||
This keeps the Python side identical. A model attribute still holds a dict or a
|
||||
list, still assigns and reads like one; only the storage changes. The
|
||||
alternative - json.dumps and json.loads at seventeen call sites - is the same
|
||||
thing written out seventeen times, with seventeen chances to forget one.
|
||||
|
||||
Note the SQLite behaviour is deliberately the same as MySQL's. Tests run on
|
||||
SQLite and production does not, so a type that behaved differently across the
|
||||
two would hide exactly the failure this exists to prevent.
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
from sqlalchemy import Text
|
||||
from sqlalchemy.types import TypeDecorator
|
||||
|
||||
|
||||
class JSONText(TypeDecorator):
|
||||
"""A dict or list on the Python side, TEXT in the database."""
|
||||
|
||||
impl = Text
|
||||
cache_ok = True
|
||||
|
||||
def process_bind_param(self, value, dialect):
|
||||
if value is None:
|
||||
return None
|
||||
return json.dumps(value, separators=(',', ':'))
|
||||
|
||||
def process_result_value(self, value, dialect):
|
||||
if value is None or value == '':
|
||||
return None
|
||||
try:
|
||||
return json.loads(value)
|
||||
except (TypeError, ValueError):
|
||||
# A row written before this column was JSON, or hand-edited. Hand
|
||||
# back the raw text rather than raising: a policy that cannot be
|
||||
# parsed should be visible and fixable, not an exception on read.
|
||||
return value
|
||||
@@ -18,6 +18,7 @@ every table for a clean rollback.
|
||||
"""
|
||||
|
||||
from shopdb.api import db, BaseModel
|
||||
from .jsontext import JSONText
|
||||
|
||||
|
||||
# Provenance of a policy row.
|
||||
@@ -43,7 +44,7 @@ class ConfigurationPolicy(BaseModel):
|
||||
# Boolean target AST: {"type":"and","nodes":[{"type":"pctype","id":3},
|
||||
# {"type":"not","node":{"type":"app","id":12}}]}. Validated server-side
|
||||
# (known node types, depth cap) before it is stored - see policyscope.
|
||||
scopejson = db.Column(db.JSON, nullable=True)
|
||||
scopejson = db.Column(JSONText, nullable=True)
|
||||
|
||||
# GE-Enforce interop seam (see POLICY_SOURCE_LOCAL).
|
||||
source = db.Column(db.String(50), default=POLICY_SOURCE_LOCAL, nullable=False)
|
||||
@@ -101,11 +102,11 @@ class ConfigurationPolicyAction(BaseModel):
|
||||
|
||||
# Desired-state target for mode=desired, as JSON so different actions can
|
||||
# express different targets (e.g. {"running": true}). Null for gate mode.
|
||||
desiredstate = db.Column(db.JSON, nullable=True)
|
||||
desiredstate = db.Column(JSONText, nullable=True)
|
||||
|
||||
# Optional action parameters passed through to the command (JSON), and an
|
||||
# optional reconcile cadence in minutes (null = reconcile on every report).
|
||||
params = db.Column(db.JSON, nullable=True)
|
||||
params = db.Column(JSONText, nullable=True)
|
||||
intervalminutes = db.Column(db.Integer, nullable=True)
|
||||
|
||||
sortorder = db.Column(db.Integer, default=0, nullable=False)
|
||||
|
||||
Reference in New Issue
Block a user