Remove Link type

This type is only used for output response formatting, not for input
validation, so it can be replaced with a basic dict equivalent without
disruption.

This results in fields in WSME types which shouldn't be handled by
WSME because they are already in a dict format. This is handled by
relaxing the validation in the (ex-WSME) types so that a None type
means that WSME shouldn't serialize that attribute. This will allow
old style type serialization to be mixed with plain dicts during the
transition period.

Story: 1651346
Task: 10551

Change-Id: Ifae9bd005fb7cf951b069ade0c92b8d61e095e0f
This commit is contained in:
Steve Baker 2020-07-16 14:41:20 +12:00
parent 5e12d502fe
commit b1328fa996
18 changed files with 247 additions and 264 deletions

View File

@ -14,8 +14,6 @@
# under the License.
from ironic import api
from ironic.api.controllers import base
from ironic.api import types as atypes
def build_url(resource, resource_args, bookmark=False, base_url=None):
@ -30,28 +28,15 @@ def build_url(resource, resource_args, bookmark=False, base_url=None):
return template % {'url': base_url, 'res': resource, 'args': resource_args}
class Link(base.Base):
"""A link representation."""
href = str
"""The url of a link."""
rel = str
"""The name of a link."""
type = str
"""Indicates the type of document/link."""
@staticmethod
def make_link(rel_name, url, resource, resource_args,
bookmark=False, type=atypes.Unset):
href = build_url(resource, resource_args,
bookmark=bookmark, base_url=url)
return Link(href=href, rel=rel_name, type=type)
@classmethod
def sample(cls):
sample = cls(href="http://localhost:6385/chassis/"
"eaaca217-e7d8-47b4-bb41-3f99f20eed89",
rel="bookmark")
return sample
def make_link(rel_name, url, resource, resource_args,
bookmark=False, type=None):
"""Build a dict representing a link"""
href = build_url(resource, resource_args,
bookmark=bookmark, base_url=url)
l = {
'href': href,
'rel': rel_name
}
if type:
l['type'] = type
return l

View File

@ -77,158 +77,158 @@ class V1(base.Base):
media_types = [MediaType]
"""An array of supported media types for this version"""
links = [link.Link]
links = None
"""Links that point to a specific URL for this version and documentation"""
chassis = [link.Link]
chassis = None
"""Links to the chassis resource"""
nodes = [link.Link]
nodes = None
"""Links to the nodes resource"""
ports = [link.Link]
ports = None
"""Links to the ports resource"""
portgroups = [link.Link]
portgroups = None
"""Links to the portgroups resource"""
drivers = [link.Link]
drivers = None
"""Links to the drivers resource"""
volume = [link.Link]
volume = None
"""Links to the volume resource"""
lookup = [link.Link]
lookup = None
"""Links to the lookup resource"""
heartbeat = [link.Link]
heartbeat = None
"""Links to the heartbeat resource"""
conductors = [link.Link]
conductors = None
"""Links to the conductors resource"""
allocations = [link.Link]
allocations = None
"""Links to the allocations resource"""
deploy_templates = [link.Link]
deploy_templates = None
"""Links to the deploy_templates resource"""
version = version.Version
"""Version discovery information."""
events = [link.Link]
events = None
"""Links to the events resource"""
@staticmethod
def convert():
v1 = V1()
v1.id = "v1"
v1.links = [link.Link.make_link('self', api.request.public_url,
'v1', '', bookmark=True),
link.Link.make_link('describedby',
'https://docs.openstack.org',
'/ironic/latest/contributor/',
'webapi.html',
bookmark=True, type='text/html')
v1.links = [link.make_link('self', api.request.public_url,
'v1', '', bookmark=True),
link.make_link('describedby',
'https://docs.openstack.org',
'/ironic/latest/contributor/',
'webapi.html',
bookmark=True, type='text/html')
]
v1.media_types = [MediaType('application/json',
'application/vnd.openstack.ironic.v1+json')]
v1.chassis = [link.Link.make_link('self', api.request.public_url,
'chassis', ''),
link.Link.make_link('bookmark',
api.request.public_url,
'chassis', '',
bookmark=True)
v1.chassis = [link.make_link('self', api.request.public_url,
'chassis', ''),
link.make_link('bookmark',
api.request.public_url,
'chassis', '',
bookmark=True)
]
v1.nodes = [link.Link.make_link('self', api.request.public_url,
'nodes', ''),
link.Link.make_link('bookmark',
api.request.public_url,
'nodes', '',
bookmark=True)
v1.nodes = [link.make_link('self', api.request.public_url,
'nodes', ''),
link.make_link('bookmark',
api.request.public_url,
'nodes', '',
bookmark=True)
]
v1.ports = [link.Link.make_link('self', api.request.public_url,
'ports', ''),
link.Link.make_link('bookmark',
api.request.public_url,
'ports', '',
bookmark=True)
v1.ports = [link.make_link('self', api.request.public_url,
'ports', ''),
link.make_link('bookmark',
api.request.public_url,
'ports', '',
bookmark=True)
]
if utils.allow_portgroups():
v1.portgroups = [
link.Link.make_link('self', api.request.public_url,
'portgroups', ''),
link.Link.make_link('bookmark', api.request.public_url,
'portgroups', '', bookmark=True)
link.make_link('self', api.request.public_url,
'portgroups', ''),
link.make_link('bookmark', api.request.public_url,
'portgroups', '', bookmark=True)
]
v1.drivers = [link.Link.make_link('self', api.request.public_url,
'drivers', ''),
link.Link.make_link('bookmark',
api.request.public_url,
'drivers', '',
bookmark=True)
v1.drivers = [link.make_link('self', api.request.public_url,
'drivers', ''),
link.make_link('bookmark',
api.request.public_url,
'drivers', '',
bookmark=True)
]
if utils.allow_volume():
v1.volume = [
link.Link.make_link('self',
api.request.public_url,
'volume', ''),
link.Link.make_link('bookmark',
api.request.public_url,
'volume', '',
bookmark=True)
link.make_link('self',
api.request.public_url,
'volume', ''),
link.make_link('bookmark',
api.request.public_url,
'volume', '',
bookmark=True)
]
if utils.allow_ramdisk_endpoints():
v1.lookup = [link.Link.make_link('self', api.request.public_url,
'lookup', ''),
link.Link.make_link('bookmark',
api.request.public_url,
'lookup', '',
bookmark=True)
v1.lookup = [link.make_link('self', api.request.public_url,
'lookup', ''),
link.make_link('bookmark',
api.request.public_url,
'lookup', '',
bookmark=True)
]
v1.heartbeat = [link.Link.make_link('self',
api.request.public_url,
'heartbeat', ''),
link.Link.make_link('bookmark',
api.request.public_url,
'heartbeat', '',
bookmark=True)
v1.heartbeat = [link.make_link('self',
api.request.public_url,
'heartbeat', ''),
link.make_link('bookmark',
api.request.public_url,
'heartbeat', '',
bookmark=True)
]
if utils.allow_expose_conductors():
v1.conductors = [link.Link.make_link('self',
api.request.public_url,
'conductors', ''),
link.Link.make_link('bookmark',
api.request.public_url,
'conductors', '',
bookmark=True)
v1.conductors = [link.make_link('self',
api.request.public_url,
'conductors', ''),
link.make_link('bookmark',
api.request.public_url,
'conductors', '',
bookmark=True)
]
if utils.allow_allocations():
v1.allocations = [link.Link.make_link('self',
api.request.public_url,
'allocations', ''),
link.Link.make_link('bookmark',
api.request.public_url,
'allocations', '',
bookmark=True)
v1.allocations = [link.make_link('self',
api.request.public_url,
'allocations', ''),
link.make_link('bookmark',
api.request.public_url,
'allocations', '',
bookmark=True)
]
if utils.allow_expose_events():
v1.events = [link.Link.make_link('self', api.request.public_url,
'events', ''),
link.Link.make_link('bookmark',
api.request.public_url,
'events', '',
bookmark=True)
v1.events = [link.make_link('self', api.request.public_url,
'events', ''),
link.make_link('bookmark',
api.request.public_url,
'events', '',
bookmark=True)
]
if utils.allow_deploy_templates():
v1.deploy_templates = [
link.Link.make_link('self',
api.request.public_url,
'deploy_templates', ''),
link.Link.make_link('bookmark',
api.request.public_url,
'deploy_templates', '',
bookmark=True)
link.make_link('self',
api.request.public_url,
'deploy_templates', ''),
link.make_link('bookmark',
api.request.public_url,
'deploy_templates', '',
bookmark=True)
]
v1.version = version.default_version()
return v1

View File

@ -65,7 +65,7 @@ class Allocation(base.APIBase):
name = atypes.wsattr(str)
"""The logical name for this allocation"""
links = atypes.wsattr([link.Link], readonly=True)
links = None
"""A list containing a self link and associated allocation links"""
state = atypes.wsattr(str, readonly=True)
@ -107,9 +107,9 @@ class Allocation(base.APIBase):
# This field is only used in POST, never return it.
allocation.node = atypes.Unset
allocation.links = [
link.Link.make_link('self', url, 'allocations', allocation.uuid),
link.Link.make_link('bookmark', url, 'allocations',
allocation.uuid, bookmark=True)
link.make_link('self', url, 'allocations', allocation.uuid),
link.make_link('bookmark', url, 'allocations',
allocation.uuid, bookmark=True)
]
return allocation

View File

@ -37,7 +37,7 @@ class BIOSSetting(base.APIBase):
value = atypes.wsattr(str)
links = atypes.wsattr([link.Link], readonly=True)
links = None
def __init__(self, **kwargs):
self.fields = []
@ -52,11 +52,11 @@ class BIOSSetting(base.APIBase):
def _convert_with_links(bios, node_uuid, url):
"""Add links to the bios setting."""
name = bios.name
bios.links = [link.Link.make_link('self', url, 'nodes',
"%s/bios/%s" % (node_uuid, name)),
link.Link.make_link('bookmark', url, 'nodes',
"%s/bios/%s" % (node_uuid, name),
bookmark=True)]
bios.links = [link.make_link('self', url, 'nodes',
"%s/bios/%s" % (node_uuid, name)),
link.make_link('bookmark', url, 'nodes',
"%s/bios/%s" % (node_uuid, name),
bookmark=True)]
return bios
@classmethod

View File

@ -58,10 +58,10 @@ class Chassis(base.APIBase):
extra = {str: types.jsontype}
"""The metadata of the chassis"""
links = atypes.wsattr([link.Link], readonly=True)
links = None
"""A list containing a self link and associated chassis links"""
nodes = atypes.wsattr([link.Link], readonly=True)
nodes = None
"""Links to the collection of nodes contained in this chassis"""
def __init__(self, **kwargs):
@ -76,23 +76,23 @@ class Chassis(base.APIBase):
@staticmethod
def _convert_with_links(chassis, url, fields=None):
if fields is None:
chassis.nodes = [link.Link.make_link('self',
url,
'chassis',
chassis.uuid + "/nodes"),
link.Link.make_link('bookmark',
url,
'chassis',
chassis.uuid + "/nodes",
bookmark=True)
chassis.nodes = [link.make_link('self',
url,
'chassis',
chassis.uuid + "/nodes"),
link.make_link('bookmark',
url,
'chassis',
chassis.uuid + "/nodes",
bookmark=True)
]
chassis.links = [link.Link.make_link('self',
url,
'chassis', chassis.uuid),
link.Link.make_link('bookmark',
url,
'chassis', chassis.uuid,
bookmark=True)
chassis.links = [link.make_link('self',
url,
'chassis', chassis.uuid),
link.make_link('bookmark',
url,
'chassis', chassis.uuid,
bookmark=True)
]
return chassis

View File

@ -52,5 +52,5 @@ class Collection(base.Base):
'args': q_args, 'limit': limit,
'marker': getattr(self.collection[-1], self.get_key_field())}
return link.Link.make_link('next', api.request.public_url,
resource_url, next_args).href
return link.make_link('next', api.request.public_url,
resource_url, next_args)['href']

View File

@ -53,7 +53,7 @@ class Conductor(base.APIBase):
drivers = atypes.wsattr([str])
"""The drivers enabled on this conductor"""
links = atypes.wsattr([link.Link])
links = None
"""A list containing a self link and associated conductor links"""
def __init__(self, **kwargs):
@ -72,11 +72,11 @@ class Conductor(base.APIBase):
@staticmethod
def _convert_with_links(conductor, url, fields=None):
conductor.links = [link.Link.make_link('self', url, 'conductors',
conductor.hostname),
link.Link.make_link('bookmark', url, 'conductors',
conductor.hostname,
bookmark=True)]
conductor.links = [link.make_link('self', url, 'conductors',
conductor.hostname),
link.make_link('bookmark', url, 'conductors',
conductor.hostname,
bookmark=True)]
return conductor
@classmethod

View File

@ -82,7 +82,7 @@ class DeployTemplate(base.APIBase):
steps = atypes.wsattr([DeployStepType], mandatory=True)
"""The deploy steps of this deploy template."""
links = atypes.wsattr([link.Link])
links = None
"""A list containing a self link and associated deploy template links."""
extra = {str: types.jsontype}
@ -148,11 +148,11 @@ class DeployTemplate(base.APIBase):
@staticmethod
def _convert_with_links(template, url, fields=None):
template.links = [
link.Link.make_link('self', url, 'deploy_templates',
template.uuid),
link.Link.make_link('bookmark', url, 'deploy_templates',
template.uuid,
bookmark=True)
link.make_link('self', url, 'deploy_templates',
template.uuid),
link.make_link('bookmark', url, 'deploy_templates',
template.uuid,
bookmark=True)
]
return template

View File

@ -96,10 +96,10 @@ class Driver(base.Base):
type = str
"""Whether the driver is classic or dynamic (hardware type)"""
links = atypes.wsattr([link.Link], readonly=True)
links = None
"""A list containing self and bookmark links"""
properties = atypes.wsattr([link.Link], readonly=True)
properties = None
"""A list containing links to driver properties"""
"""Default interface for a hardware type"""
@ -146,23 +146,23 @@ class Driver(base.Base):
driver.name = name
driver.hosts = hosts
driver.links = [
link.Link.make_link('self',
api.request.public_url,
'drivers', name),
link.Link.make_link('bookmark',
api.request.public_url,
'drivers', name,
bookmark=True)
link.make_link('self',
api.request.public_url,
'drivers', name),
link.make_link('bookmark',
api.request.public_url,
'drivers', name,
bookmark=True)
]
if api_utils.allow_links_node_states_and_driver_properties():
driver.properties = [
link.Link.make_link('self',
api.request.public_url,
'drivers', name + "/properties"),
link.Link.make_link('bookmark',
api.request.public_url,
'drivers', name + "/properties",
bookmark=True)
link.make_link('self',
api.request.public_url,
'drivers', name + "/properties"),
link.make_link('bookmark',
api.request.public_url,
'drivers', name + "/properties",
bookmark=True)
]
if api_utils.allow_dynamic_drivers():

View File

@ -343,7 +343,7 @@ class Indicator(base.APIBase):
states = atypes.ArrayType(str)
links = atypes.wsattr([link.Link], readonly=True)
links = None
def __init__(self, **kwargs):
self.name = kwargs.get('name')
@ -355,11 +355,11 @@ class Indicator(base.APIBase):
def _convert_with_links(node_uuid, indicator, url):
"""Add links to the indicator."""
indicator.links = [
link.Link.make_link(
link.make_link(
'self', url, 'nodes',
'%s/management/indicators/%s' % (
node_uuid, indicator.name)),
link.Link.make_link(
link.make_link(
'bookmark', url, 'nodes',
'%s/management/indicators/%s' % (
node_uuid, indicator.name),
@ -1206,19 +1206,19 @@ class Node(base.APIBase):
_set_chassis_uuid)
"""The UUID of the chassis this node belongs"""
links = atypes.wsattr([link.Link], readonly=True)
links = None
"""A list containing a self link and associated node links"""
ports = atypes.wsattr([link.Link], readonly=True)
ports = None
"""Links to the collection of ports on this node"""
portgroups = atypes.wsattr([link.Link], readonly=True)
portgroups = None
"""Links to the collection of portgroups on this node"""
volume = atypes.wsattr([link.Link], readonly=True)
volume = None
"""Links to endpoint for retrieving volume resources on this node"""
states = atypes.wsattr([link.Link], readonly=True)
states = None
"""Links to endpoint for retrieving and setting node states"""
boot_interface = atypes.wsattr(str)
@ -1336,38 +1336,38 @@ class Node(base.APIBase):
def _convert_with_links(node, url, fields=None, show_states_links=True,
show_portgroups=True, show_volume=True):
if fields is None:
node.ports = [link.Link.make_link('self', url, 'nodes',
node.uuid + "/ports"),
link.Link.make_link('bookmark', url, 'nodes',
node.uuid + "/ports",
bookmark=True)
node.ports = [link.make_link('self', url, 'nodes',
node.uuid + "/ports"),
link.make_link('bookmark', url, 'nodes',
node.uuid + "/ports",
bookmark=True)
]
if show_states_links:
node.states = [link.Link.make_link('self', url, 'nodes',
node.uuid + "/states"),
link.Link.make_link('bookmark', url, 'nodes',
node.uuid + "/states",
bookmark=True)]
node.states = [link.make_link('self', url, 'nodes',
node.uuid + "/states"),
link.make_link('bookmark', url, 'nodes',
node.uuid + "/states",
bookmark=True)]
if show_portgroups:
node.portgroups = [
link.Link.make_link('self', url, 'nodes',
node.uuid + "/portgroups"),
link.Link.make_link('bookmark', url, 'nodes',
node.uuid + "/portgroups",
bookmark=True)]
link.make_link('self', url, 'nodes',
node.uuid + "/portgroups"),
link.make_link('bookmark', url, 'nodes',
node.uuid + "/portgroups",
bookmark=True)]
if show_volume:
node.volume = [
link.Link.make_link('self', url, 'nodes',
node.uuid + "/volume"),
link.Link.make_link('bookmark', url, 'nodes',
node.uuid + "/volume",
bookmark=True)]
link.make_link('self', url, 'nodes',
node.uuid + "/volume"),
link.make_link('bookmark', url, 'nodes',
node.uuid + "/volume",
bookmark=True)]
node.links = [link.Link.make_link('self', url, 'nodes',
node.uuid),
link.Link.make_link('bookmark', url, 'nodes',
node.uuid, bookmark=True)
node.links = [link.make_link('self', url, 'nodes',
node.uuid),
link.make_link('bookmark', url, 'nodes',
node.uuid, bookmark=True)
]
return node

View File

@ -155,7 +155,7 @@ class Port(base.APIBase):
physical_network = atypes.StringType(max_length=64)
"""The name of the physical network to which this port is connected."""
links = atypes.wsattr([link.Link], readonly=True)
links = None
"""A list containing a self link and associated port links"""
is_smartnic = types.boolean
@ -199,11 +199,11 @@ class Port(base.APIBase):
url = api.request.public_url
port.links = [link.Link.make_link('self', url,
'ports', port.uuid),
link.Link.make_link('bookmark', url,
'ports', port.uuid,
bookmark=True)
port.links = [link.make_link('self', url,
'ports', port.uuid),
link.make_link('bookmark', url,
'ports', port.uuid,
bookmark=True)
]
if not sanitize:

View File

@ -90,7 +90,7 @@ class Portgroup(base.APIBase):
name = atypes.wsattr(str)
"""The logical name for this portgroup"""
links = atypes.wsattr([link.Link], readonly=True)
links = None
"""A list containing a self link and associated portgroup links"""
standalone_ports_supported = types.boolean
@ -105,7 +105,7 @@ class Portgroup(base.APIBase):
properties = {str: types.jsontype}
"""This portgroup's properties"""
ports = atypes.wsattr([link.Link], readonly=True)
ports = None
"""Links to the collection of ports of this portgroup"""
def __init__(self, **kwargs):
@ -133,20 +133,20 @@ class Portgroup(base.APIBase):
"""Add links to the portgroup."""
if fields is None:
portgroup.ports = [
link.Link.make_link('self', url, 'portgroups',
portgroup.uuid + "/ports"),
link.Link.make_link('bookmark', url, 'portgroups',
portgroup.uuid + "/ports", bookmark=True)
link.make_link('self', url, 'portgroups',
portgroup.uuid + "/ports"),
link.make_link('bookmark', url, 'portgroups',
portgroup.uuid + "/ports", bookmark=True)
]
# never expose the node_id attribute
portgroup.node_id = atypes.Unset
portgroup.links = [link.Link.make_link('self', url,
'portgroups', portgroup.uuid),
link.Link.make_link('bookmark', url,
'portgroups', portgroup.uuid,
bookmark=True)
portgroup.links = [link.make_link('self', url,
'portgroups', portgroup.uuid),
link.make_link('bookmark', url,
'portgroups', portgroup.uuid,
bookmark=True)
]
return portgroup

View File

@ -14,7 +14,6 @@
# under the License.
from ironic.api.controllers import base
from ironic.api.controllers import link
class State(base.APIBase):
@ -28,5 +27,5 @@ class State(base.APIBase):
available = [str]
"""A list of available states it is able to transition to"""
links = [link.Link]
links = None
"""A list containing a self link and associated state links"""

View File

@ -24,7 +24,6 @@ from ironic.api.controllers.v1 import utils as api_utils
from ironic.api.controllers.v1 import volume_connector
from ironic.api.controllers.v1 import volume_target
from ironic.api import expose
from ironic.api import types as atypes
from ironic.common import exception
from ironic.common import policy
@ -36,13 +35,13 @@ class Volume(base.APIBase):
targets controllers.
"""
links = atypes.wsattr([link.Link], readonly=True)
links = None
"""A list containing a self link and associated volume links"""
connectors = atypes.wsattr([link.Link], readonly=True)
connectors = None
"""Links to the volume connectors resource"""
targets = atypes.wsattr([link.Link], readonly=True)
targets = None
"""Links to the volume targets resource"""
@staticmethod
@ -57,19 +56,19 @@ class Volume(base.APIBase):
args = ''
volume.links = [
link.Link.make_link('self', url, resource, args),
link.Link.make_link('bookmark', url, resource, args,
bookmark=True)]
link.make_link('self', url, resource, args),
link.make_link('bookmark', url, resource, args,
bookmark=True)]
volume.connectors = [
link.Link.make_link('self', url, resource, args + 'connectors'),
link.Link.make_link('bookmark', url, resource, args + 'connectors',
bookmark=True)]
link.make_link('self', url, resource, args + 'connectors'),
link.make_link('bookmark', url, resource, args + 'connectors',
bookmark=True)]
volume.targets = [
link.Link.make_link('self', url, resource, args + 'targets'),
link.Link.make_link('bookmark', url, resource, args + 'targets',
bookmark=True)]
link.make_link('self', url, resource, args + 'targets'),
link.make_link('bookmark', url, resource, args + 'targets',
bookmark=True)]
return volume

View File

@ -87,7 +87,7 @@ class VolumeConnector(base.APIBase):
_set_node_identifiers, mandatory=True)
"""The UUID of the node this volume connector belongs to"""
links = atypes.wsattr([link.Link], readonly=True)
links = None
"""A list containing a self link and associated volume connector links"""
def __init__(self, **kwargs):
@ -117,13 +117,13 @@ class VolumeConnector(base.APIBase):
@staticmethod
def _convert_with_links(connector, url):
connector.links = [link.Link.make_link('self', url,
'volume/connectors',
connector.uuid),
link.Link.make_link('bookmark', url,
'volume/connectors',
connector.uuid,
bookmark=True)
connector.links = [link.make_link('self', url,
'volume/connectors',
connector.uuid),
link.make_link('bookmark', url,
'volume/connectors',
connector.uuid,
bookmark=True)
]
return connector

View File

@ -94,7 +94,7 @@ class VolumeTarget(base.APIBase):
_set_node_identifiers, mandatory=True)
"""The UUID of the node this volume target belongs to"""
links = atypes.wsattr([link.Link], readonly=True)
links = None
"""A list containing a self link and associated volume target links"""
def __init__(self, **kwargs):
@ -124,13 +124,13 @@ class VolumeTarget(base.APIBase):
@staticmethod
def _convert_with_links(target, url):
target.links = [link.Link.make_link('self', url,
'volume/targets',
target.uuid),
link.Link.make_link('bookmark', url,
'volume/targets',
target.uuid,
bookmark=True)
target.links = [link.make_link('self', url,
'volume/targets',
target.uuid),
link.make_link('bookmark', url,
'volume/targets',
target.uuid,
bookmark=True)
]
return target

View File

@ -27,7 +27,7 @@ class Version(base.Base):
id = str
"""The ID of the (major) version, also acts as the release number"""
links = [link.Link]
links = None
"""A Link that point to a specific version of the API"""
status = str
@ -47,8 +47,8 @@ class Version(base.Base):
def __init__(self, id, min_version, version, status='CURRENT'):
self.id = id
self.links = [link.Link.make_link('self', api.request.public_url,
self.id, '', bookmark=True)]
self.links = [link.make_link('self', api.request.public_url,
self.id, '', bookmark=True)]
self.status = status
self.version = version
self.min_version = min_version

View File

@ -280,7 +280,7 @@ Unset = UnsetType()
def validate_value(datatype, value):
if value in (Unset, None):
if value in (Unset, None) or datatype is None:
return value
# Try to promote the data type to one of our complex types.