示例#1
0
    def test_set_field_order(self):
        schema = JSONSchema()
        schema.add_property('foo', {'type': 'string'})
        schema.add_property('bar', {'type': 'string'})
        schema.set_field_order(['foo', 'bar'])

        expected = {
            '$schema': 'http://json-schema.org/draft-04/schema#',
            'type': 'object',
            'properties': {
                'foo': {'type': 'string'},
                'bar': {'type': 'string'},
            },
            'field_order': ['foo', 'bar'],
        }
        serialized = schema.serialize()

        self.assertEqual(expected, serialized)
示例#2
0
class JSONSchemaBuilder(object):
    """Builds a JSON Schema representation of a single GEVER type.
    """
    def __init__(self, portal_type):
        self.portal_type = portal_type
        self.schema = None

        if portal_type in GEVER_TYPES:
            self.type_dumper = TypeDumper()
        elif portal_type in GEVER_SQL_TYPES:
            self.type_dumper = SQLTypeDumper()
        else:
            raise Exception("Unmapped type: %r" % portal_type)

    def build_schema(self):
        type_dump = self.type_dumper.dump(self.portal_type)

        self.schema = JSONSchema(
            title=type_dump['title'],
            additional_properties=False,
        )
        field_order = []
        # Collect field info from all schemas (base schema + behaviors)
        for _schema in type_dump['schemas']:
            # Note: This is not the final / "correct" field order as displayed
            # in the user interface (which should eventually honor fieldsets
            # and plone.autoform directives).
            # The intent here is rather to keep a *consistent* order for now.
            field_order.extend([field['name'] for field in _schema['fields']])

            translated_title_fields = []
            for field in _schema['fields']:
                prop_def = self._property_definition_from_field(field)
                self.schema.add_property(field['name'], prop_def)

                if field['name'] in TRANSLATED_TITLE_NAMES:
                    translated_title_fields.append(field['name'])

            if translated_title_fields:
                self.schema.require_any_of(translated_title_fields)

        self.schema.set_field_order(field_order)
        return self.schema

    def _property_definition_from_field(self, field):
        """Create a JSON Schema property definition from a field info dump.
        """
        prop_def = self._js_type_from_zope_type(field['type'])

        prop_def['title'] = field['title']
        prop_def['description'] = field['description']
        prop_def['_zope_schema_type'] = field['type']

        self._process_choice(prop_def, field)
        self._process_max_length(prop_def, field)
        self._process_default(prop_def, field)
        self._process_vocabulary(prop_def, field)
        self._process_required(prop_def, field)

        return prop_def

    def _js_type_from_zope_type(self, zope_type):
        """Map a zope.schema type to a JSON schema (JavaScript) type.

        Returns a minimal property definition dict including at least a
        'type', and possibly a 'format' as well.
        """
        if zope_type not in JSON_SCHEMA_FIELD_TYPES:
            raise Exception(
                "Don't know what JS type to map %r to. Please map it in "
                "JSON_SCHEMA_FIELD_TYPES first." % zope_type)

        type_spec = JSON_SCHEMA_FIELD_TYPES[zope_type].copy()
        return type_spec

    def _process_choice(self, prop_def, field):
        if field['type'] == 'Choice':
            prop_def['type'] = field['value_type']

    def _process_max_length(self, prop_def, field):
        if field.get('max_length') is not None:
            prop_def['maxLength'] = field['max_length']

    def _process_default(self, prop_def, field):
        if 'default' in field:
            prop_def['default'] = field['default']

    def _process_vocabulary(self, prop_def, field):
        if field.get('vocabulary'):
            vocab = field['vocabulary']
            if isinstance(vocab, basestring) and vocab.startswith('<'):
                # Placeholder vocabulary (like <Any valid user> )
                prop_def['_vocabulary'] = vocab
            else:
                prop_def['enum'] = vocab

    def _process_required(self, prop_def, field):
        if field.get('required', False):
            if field['name'] not in TRANSLATED_TITLE_NAMES:
                self.schema.set_required(field['name'])
示例#3
0
class JSONSchemaBuilder(object):
    """Builds a JSON Schema representation of a single GEVER type.
    """

    def __init__(self, portal_type):
        self.portal_type = portal_type
        self.schema = None

        if portal_type in GEVER_TYPES:
            self.type_dumper = TypeDumper()
        elif portal_type in GEVER_SQL_TYPES:
            self.type_dumper = SQLTypeDumper()
        else:
            raise Exception("Unmapped type: %r" % portal_type)

    def build_schema(self):
        type_dump = self.type_dumper.dump(self.portal_type)

        self.schema = JSONSchema(
            title=type_dump['title'],
            additional_properties=False,
        )
        field_order = []
        # Collect field info from all schemas (base schema + behaviors)
        for _schema in type_dump['schemas']:
            # Note: This is not the final / "correct" field order as displayed
            # in the user interface (which should eventually honor fieldsets
            # and plone.autoform directives).
            # The intent here is rather to keep a *consistent* order for now.
            field_order.extend([field['name'] for field in _schema['fields']])

            translated_title_fields = []
            for field in _schema['fields']:
                prop_def = self._property_definition_from_field(field)
                self.schema.add_property(field['name'], prop_def)

                if field['name'] in TRANSLATED_TITLE_NAMES:
                    translated_title_fields.append(field['name'])

            if translated_title_fields:
                self.schema.require_any_of(translated_title_fields)

        self.schema.set_field_order(field_order)
        return self.schema

    def _property_definition_from_field(self, field):
        """Create a JSON Schema property definition from a field info dump.
        """
        prop_def = self._js_type_from_zope_type(field['type'])

        prop_def['title'] = field['title']
        prop_def['description'] = field['description']
        prop_def['_zope_schema_type'] = field['type']

        self._process_choice(prop_def, field)
        self._process_max_length(prop_def, field)
        self._process_default(prop_def, field)
        self._process_vocabulary(prop_def, field)
        self._process_required(prop_def, field)

        return prop_def

    def _js_type_from_zope_type(self, zope_type):
        """Map a zope.schema type to a JSON schema (JavaScript) type.

        Returns a minimal property definition dict including at least a
        'type', and possibly a 'format' as well.
        """
        if zope_type not in JSON_SCHEMA_FIELD_TYPES:
            raise Exception(
                "Don't know what JS type to map %r to. Please map it in "
                "JSON_SCHEMA_FIELD_TYPES first." % zope_type)

        type_spec = JSON_SCHEMA_FIELD_TYPES[zope_type].copy()
        return type_spec

    def _process_choice(self, prop_def, field):
        if field['type'] == 'Choice':
            prop_def['type'] = field['value_type']

    def _process_max_length(self, prop_def, field):
        if field.get('max_length') is not None:
            prop_def['maxLength'] = field['max_length']

    def _process_default(self, prop_def, field):
        if 'default' in field:
            prop_def['default'] = field['default']

    def _process_vocabulary(self, prop_def, field):
        if field.get('vocabulary'):
            vocab = field['vocabulary']
            if isinstance(vocab, basestring) and vocab.startswith('<'):
                # Placeholder vocabulary (like <Any valid user> )
                prop_def['_vocabulary'] = vocab
            else:
                prop_def['enum'] = vocab

    def _process_required(self, prop_def, field):
        if field.get('required', False):
            if field['name'] not in TRANSLATED_TITLE_NAMES:
                self.schema.set_required(field['name'])