Exemple #1
0
    def test_add_definition(self):
        schema = JSONSchema()
        subschema = JSONSchema()
        subschema.add_property('subfoo', {'type': 'string'})
        schema.add_definition('sub', subschema)

        expected = {
            '$schema': 'http://json-schema.org/draft-04/schema#',
            'type': 'object',
            'definitions': {
                'sub': {
                    'type': 'object',
                    'properties': {
                        'subfoo': {'type': 'string'},
                    },
                }
            }
        }
        serialized = schema.serialize()

        self.assertDictEqual(expected, serialized)
class OGGBundleJSONSchemaBuilder(object):
    """Builds a JSON Schema representation of a single OGGBundle type.
    """
    def __init__(self, portal_type):
        self.portal_type = portal_type
        self.short_name = GEVER_TYPES_TO_OGGBUNDLE_TYPES[portal_type]

        # Bundle schema (array of items)
        self.schema = None
        # Content type schema (item, i.e. GEVER type)
        self.ct_schema = None

    def build_schema(self):
        # The OGGBundle JSON schemas all are flat arrays of items, where the
        # actual items' schemas are (more or less) the GEVER content types'
        # schemas, stored in #/definitions/<short_name>
        self.schema = JSONSchema(type_='array')
        self.schema._schema['items'] = {
            "$ref": "#/definitions/%s" % self.short_name
        }

        # Build the standard content type schema
        self.ct_schema = JSONSchemaBuilder(self.portal_type).build_schema()

        # Tweak the content type schema for use in OGGBundles
        self._add_review_state_property()
        self._add_guid_properties()
        self._add_permissions_property()
        self._add_file_properties()
        self._add_sequence_number_property()
        self._add_mail_properties()

        self._filter_fields()
        self.ct_schema.make_optional_properties_nullable()

        # Finally add the CT schema under #/definitions/<short_name>
        self.schema.add_definition(self.short_name, self.ct_schema)
        return self.schema

    def _add_review_state_property(self):
        # XXX: Eventually, these states should be dynamically generated by
        # interrogating the FTI / workflow tool. Hardcoded for now.
        self.ct_schema.add_property(
            'review_state',
            {
                'type': 'string',
                'enum': ALLOWED_REVIEW_STATES[self.portal_type]
            },
            required=True,
        )

    def _add_guid_properties(self):
        self.ct_schema.add_property('guid', {'type': 'string'}, required=True)

        if self.portal_type != 'opengever.repository.repositoryroot':
            # Everything except repository roots needs a parent GUID
            self.ct_schema.add_property('parent_guid', {'type': 'string'})

            array_of_ints = {
                "type": "array",
                "items": {
                    "type": "integer"
                },
            }
            self.ct_schema.add_property('parent_reference', {
                'type': 'array',
                'items': array_of_ints
            })

            self.ct_schema.require_any_of(['parent_guid', 'parent_reference'])

    def _add_permissions_property(self):
        if not self.portal_type == 'opengever.document.document':
            permissions_schema = self._build_permission_subschema()
            self.ct_schema._schema['properties']['_permissions'] = {
                "$ref": "#/definitions/permission"
            }

            # XXX: This is just to preserve order in definitions for now
            self.schema._schema['definitions'] = OrderedDict()
            self.schema._schema['definitions'][self.short_name] = None

            self.schema.add_definition('permission', permissions_schema)

    def _build_permission_subschema(self):
        subschema = JSONSchema(additional_properties=False)
        string_array = {
            "type": "array",
            "items": {
                "type": "string"
            },
        }

        subschema.add_property('block_inheritance', {"type": "boolean"})
        subschema.add_property('read', string_array)
        subschema.add_property('add', string_array)
        subschema.add_property('edit', string_array)
        subschema.add_property('close', string_array)
        subschema.add_property('reactivate', string_array)

        if self.portal_type in [
                'opengever.repository.repositoryroot',
                'opengever.repository.repositoryfolder'
        ]:
            subschema.add_property('manage_dossiers', string_array)
        return subschema

    def _add_file_properties(self):
        if self.portal_type == 'opengever.document.document':
            self.ct_schema.add_property('filepath', {'type': 'string'})

            # In OGGBundles we always require a title for documents
            self.ct_schema.set_required('title')

            # XXX: Documents without files? For now we always require filepath
            self.ct_schema.set_required('filepath')

    def _add_sequence_number_property(self):
        if self.portal_type not in SEQUENCE_NUMBER_LABELS:
            return

        desc = SEQUENCE_NUMBER_LABELS[self.portal_type]
        self.ct_schema.add_property('sequence_number', {
            'type': 'integer',
            'title': u'Laufnummer',
            'description': desc,
        })

    def _add_mail_properties(self):
        # Mails in OGGBundles are expecter in documents.json, and for large
        # parts treated like documents. Only later (bundle loader) will their
        # *actual* portal_type (ftw.mail.mail) be determined and set.
        if self.portal_type == 'opengever.document.document':
            self.ct_schema.add_property('original_message_path',
                                        {'type': 'string'})

    def _filter_fields(self):
        filtered_fields = IGNORED_OGGBUNDLE_FIELDS.get(self.short_name, [])
        for field_name in filtered_fields:
            self.ct_schema.drop_property(field_name)
Exemple #3
0
class OGGBundleJSONSchemaBuilder(object):
    """Builds a JSON Schema representation of a single OGGBundle type.
    """

    def __init__(self, portal_type):
        self.portal_type = portal_type
        self.short_name = GEVER_TYPES_TO_OGGBUNDLE_TYPES[portal_type]

        # Bundle schema (array of items)
        self.schema = None
        # Content type schema (item, i.e. GEVER type)
        self.ct_schema = None

    def build_schema(self):
        # The OGGBundle JSON schemas all are flat arrays of items, where the
        # actual items' schemas are (more or less) the GEVER content types'
        # schemas, stored in #/definitions/<short_name>
        self.schema = JSONSchema(type_='array')
        self.schema._schema['items'] = {
            "$ref": "#/definitions/%s" % self.short_name}

        # Build the standard content type schema
        self.ct_schema = JSONSchemaBuilder(self.portal_type).build_schema()

        # Tweak the content type schema for use in OGGBundles
        self._add_review_state_property()
        self._add_guid_properties()
        self._add_permissions_property()
        self._add_file_properties()
        self._add_sequence_number_property()
        self._add_mail_properties()

        self._filter_fields()
        self.ct_schema.make_optional_properties_nullable()

        # Finally add the CT schema under #/definitions/<short_name>
        self.schema.add_definition(self.short_name, self.ct_schema)
        return self.schema

    def _add_review_state_property(self):
        # XXX: Eventually, these states should be dynamically generated by
        # interrogating the FTI / workflow tool. Hardcoded for now.
        self.ct_schema.add_property(
            'review_state',
            {'type': 'string',
             'enum': ALLOWED_REVIEW_STATES[self.portal_type]},
            required=True,
        )

    def _add_guid_properties(self):
        self.ct_schema.add_property('guid', {'type': 'string'}, required=True)

        if self.portal_type != 'opengever.repository.repositoryroot':
            # Everything except repository roots needs a parent GUID
            self.ct_schema.add_property('parent_guid', {'type': 'string'})

            array_of_ints = {
                "type": "array",
                "items": {"type": "integer"},
            }
            self.ct_schema.add_property(
                'parent_reference', {'type': 'array', 'items': array_of_ints})

            self.ct_schema.require_any_of(['parent_guid', 'parent_reference'])

    def _add_permissions_property(self):
        if not self.portal_type == 'opengever.document.document':
            permissions_schema = self._build_permission_subschema()
            self.ct_schema._schema['properties']['_permissions'] = {
                "$ref": "#/definitions/permission"}

            # XXX: This is just to preserve order in definitions for now
            self.schema._schema['definitions'] = OrderedDict()
            self.schema._schema['definitions'][self.short_name] = None

            self.schema.add_definition('permission', permissions_schema)

    def _build_permission_subschema(self):
        subschema = JSONSchema(additional_properties=False)
        string_array = {
            "type": "array",
            "items": {"type": "string"},
        }

        subschema.add_property('block_inheritance', {"type": "boolean"})
        subschema.add_property('read', string_array)
        subschema.add_property('add', string_array)
        subschema.add_property('edit', string_array)
        subschema.add_property('close', string_array)
        subschema.add_property('reactivate', string_array)

        if self.portal_type in ['opengever.repository.repositoryroot',
                                'opengever.repository.repositoryfolder']:
            subschema.add_property('manage_dossiers', string_array)
        return subschema

    def _add_file_properties(self):
        if self.portal_type == 'opengever.document.document':
            self.ct_schema.add_property('filepath', {'type': 'string'})

            # In OGGBundles we always require a title for documents
            self.ct_schema.set_required('title')

            # XXX: Documents without files? For now we always require filepath
            self.ct_schema.set_required('filepath')

    def _add_sequence_number_property(self):
        if self.portal_type not in SEQUENCE_NUMBER_LABELS:
            return

        desc = SEQUENCE_NUMBER_LABELS[self.portal_type]
        self.ct_schema.add_property('sequence_number', {
            'type': 'integer',
            'title': u'Laufnummer',
            'description': desc,
        })

    def _add_mail_properties(self):
        # Mails in OGGBundles are expecter in documents.json, and for large
        # parts treated like documents. Only later (bundle loader) will their
        # *actual* portal_type (ftw.mail.mail) be determined and set.
        if self.portal_type == 'opengever.document.document':
            self.ct_schema.add_property('original_message_path',
                                        {'type': 'string'})

    def _filter_fields(self):
        filtered_fields = IGNORED_OGGBUNDLE_FIELDS.get(self.short_name, [])
        for field_name in filtered_fields:
            self.ct_schema.drop_property(field_name)