Add common constraint to validate JSON string

Add the new 'json_string' constraint to ensure the given string can be
parsed as json.

Change-Id: I42e459aebab665c0e6dd0c68a4e793e8de225b44
This commit is contained in:
Takashi Kajinami 2024-10-15 16:06:06 +09:00
parent fe3fe44a14
commit 9350c47150
5 changed files with 50 additions and 4 deletions

View File

@ -13,6 +13,7 @@
import croniter
import eventlet
import json
import netaddr
import zoneinfo
@ -188,6 +189,19 @@ class ExpirationConstraint(constraints.BaseCustomConstraint):
raise ValueError(_('Expiration time is out of date.'))
except Exception as ex:
self._error_message = (_(
'Expiration {0} is invalid: {1}').format(value,
str(ex)))
'Expiration {0} is invalid: {1}').format(value, str(ex)))
return False
class JsonStringConstraint(constraints.BaseCustomConstraint):
def validate(self, value, context):
if not value:
return True
try:
json.loads(value)
return True
except json.decoder.JSONDecodeError as ex:
self._error_message = (_(
'JSON string {0} is invalid: {1}').format(value, str(ex)))
return False

View File

@ -12,6 +12,7 @@
# under the License.
from heat.common.i18n import _
from heat.engine import constraints
from heat.engine import properties
from heat.engine import resource
from heat.engine import support
@ -50,7 +51,8 @@ class AvailabilityZoneProfile(resource.Resource):
properties.Schema.STRING,
_('JSON string containing the availability zone metadata.'),
update_allowed=True,
required=True
required=True,
constraints=[constraints.CustomConstraint('json_string')]
),
PROVIDER_NAME: properties.Schema(
properties.Schema.STRING,

View File

@ -12,6 +12,7 @@
# under the License.
from heat.common.i18n import _
from heat.engine import constraints
from heat.engine import properties
from heat.engine import resource
from heat.engine import support
@ -44,7 +45,8 @@ class FlavorProfile(resource.Resource):
properties.Schema.STRING,
_('JSON string containing the flavor metadata.'),
update_allowed=True,
required=True
required=True,
constraints=[constraints.CustomConstraint('json_string')]
),
PROVIDER_NAME: properties.Schema(
properties.Schema.STRING,

View File

@ -445,3 +445,30 @@ class ExpirationConstraintTest(common.HeatTestCase):
def test_validation_none(self):
self.assertTrue(self.constraint.validate(None, self.ctx))
class JsonStringConstraintTest(common.HeatTestCase):
def setUp(self):
super(JsonStringConstraintTest, self).setUp()
self.ctx = utils.dummy_context()
self.constraint = cc.JsonStringConstraint()
def test_validate_json(self):
data = '{"key": "value"}'
self.assertTrue(self.constraint.validate(data, None))
def test_validation_error(self):
data = '{\'key\': \'value\'}'
expected = (
"JSON string {0} is invalid: Expecting property name "
"enclosed in double quotes: line 1 column 2 (char 1)".format(data))
self.assertFalse(self.constraint.validate(data, self.ctx))
self.assertEqual(
expected,
str(self.constraint._error_message)
)
def test_validation_none(self):
self.assertTrue(self.constraint.validate(None, self.ctx))

View File

@ -107,6 +107,7 @@ heat.constraints =
ip_or_cidr = heat.engine.constraint.common_constraints:IPCIDRConstraint
test_constr = heat.engine.constraint.common_constraints:TestConstraintDelay
timezone = heat.engine.constraint.common_constraints:TimezoneConstraint
json_string = heat.engine.constraint.common_constraints:JsonStringConstraint
# service constraints
barbican.container = heat.engine.clients.os.barbican:ContainerConstraint
barbican.secret = heat.engine.clients.os.barbican:SecretConstraint