예제 #1
0
  def __init__(self, api, name, def_dict, method):
    super(Parameter, self).__init__(def_dict, api, parent=method,
                                    wire_name=name)
    self.ValidateName(name)
    self.schema = api

    # TODO(user): Deal with dots in names better. What we should do is:
    # For x.y, x.z create a little class X, with members y and z. Then
    # have the constructor method take an X.

    self._repeated = self.values.get('repeated', False)
    self._required = self.values.get('required', False)
    self._location = (self.values.get('location')
                      or self.values.get('restParameterType')
                      or 'query')
    # TODO(user): Why not just use Schema.Create here?
    referenced_schema = self.values.get('$ref')
    if referenced_schema:
      self._data_type = (api.SchemaByName(referenced_schema) or
                         data_types.SchemaReference(referenced_schema, api))
    elif def_dict.get('type') == 'array':
      self._data_type = Schema.Create(api, name, def_dict, name, method)
    elif self.values.get('enum'):
      self._data_type = data_types.Enum(def_dict,
                                        api,
                                        name,
                                        self.values.get('enum'),
                                        self.values.get('enumDescriptions'),
                                        parent=method)
      self.SetTemplateValue('enumType', self._data_type)
    else:
      self._data_type = data_types.PrimitiveDataType(def_dict, api, parent=self)
    if self._repeated:
      self._data_type = data_types.ArrayDataType(name, self._data_type,
                                                 parent=self)
    def Create(cls, api, default_name, def_dict, wire_name, parent=None):
        """Construct a Schema or DataType from a discovery dictionary.

    Schemas contain either object declarations, simple type declarations, or
    references to other Schemas.  Object declarations conceptually map to real
    classes.  Simple types will map to a target language built-in type.
    References should effectively be replaced by the referenced Schema.

    Args:
      api: (Api) the Api instance owning the Schema
      default_name: (str) the default name of the Schema. If there is an 'id'
        member in the definition, that is used for the name instead.
      def_dict: (dict) a discovery dictionary
      wire_name: The name which will identify objects of this type in data on
        the wire. The path of wire_names can trace an item back through
        discovery.
      parent: (Schema) The containing schema. To be used to establish nesting
        for anonymous sub-schemas.

    Returns:
      A Schema or DataType.

    Raises:
      ApiException: If the definition dict is not correct.
    """

        schema_id = def_dict.get('id')
        if schema_id:
            name = schema_id
        else:
            name = default_name
        class_name = api.ToClassName(name, None, element_type='schema')

        _LOGGER.debug(
            'Create: %s, parent=%s', name,
            parent.values.get('wireName', '<anon>') if parent else 'None')

        # Schema objects come in several patterns.
        #
        # 1. Simple objects
        #    { type: object, properties: { "foo": {schema} ... }}
        #
        # 2. Maps of objects
        #    { type: object, additionalProperties: { "foo": {inner_schema} ... }}
        #
        #    What we want is a data type which is Map<string, {inner_schema}>
        #    The schema we create here is essentially a built in type which we
        #    don't want to generate a class for.
        #
        # 3. Arrays of objects
        #    { type: array, items: { inner_schema }}
        #
        #    Same kind of issue as the map, but with List<{inner_schema}>
        #
        # 4. Primitive data types, described by type and format.
        #    { type: string, format: int32 }
        #    { type: string, enum: ["value", ...], enumDescriptions: ["desc", ...]}
        #
        # 5. Refs to another schema.
        #    { $ref: name }
        #
        # 6. Variant schemas
        #    { type: object, variant: { discriminant: "prop", map:
        #             [ { 'type_value': value, '$ref': wireName }, ... ] } }
        #
        #    What we do is map the variant schema to a schema with a single
        #    property for the discriminant. To that property, we attach
        #    the variant map which specifies which discriminator values map
        #    to which schema references. We also collect variant information
        #    in the api so we can later associate discriminator value and
        #    base type with the generated variant subtypes.

        if 'type' in def_dict:
            # The 'type' field of the schema can either be 'array', 'object', or a
            # base json type.
            json_type = def_dict['type']
            if json_type == 'object':

                # Look for variants
                variant = def_dict.get('variant')
                if variant:
                    return cls._CreateVariantType(variant, api, name, def_dict,
                                                  wire_name, parent)

                # Look for full object definition.  You can have properties or
                # additionalProperties, but it does not  do anything useful to have
                # both.

                # Replace properties dict with Property's
                props = def_dict.get('properties')
                if props:
                    # This case 1 from above
                    return cls._CreateObjectWithProperties(
                        props, api, name, def_dict, wire_name, parent)

                # Look for case 2
                additional_props = def_dict.get(_ADDITIONAL_PROPERTIES)
                if additional_props:
                    return cls._CreateMapType(additional_props, api, name,
                                              wire_name, class_name, parent)

                # no properties
                return cls._CreateSchemaWithoutProperties(
                    api, name, def_dict, wire_name, parent)

            elif json_type == 'array':
                # Case 3: Look for array definition
                return cls._CreateArrayType(api, def_dict, wire_name,
                                            class_name, schema_id, parent)
            else:
                # Case 4: This must be a basic type.  Create a DataType for it.
                return data_types.CreatePrimitiveDataType(def_dict,
                                                          api,
                                                          wire_name,
                                                          parent=parent)

        referenced_schema = def_dict.get('$ref')
        if referenced_schema:
            # Case 5: Reference to another Schema.
            #
            # There are 4 ways you can see '$ref' in discovery.
            # 1. In a property of a schema or a method request/response, pointing
            #    back to a previously defined schema
            # 2. As above, pointing to something not defined yet.
            # 3. In a method request or response or property of a schema pointing to
            #    something undefined.
            #
            # For case 1, the schema will be in the API name to schema map.
            #
            # For case 2, just creating this placeholder here is fine.  When the
            # actual schema is hit in the loop in _BuildSchemaDefinitions, we will
            # replace the entry and DataTypeFromJson will resolve the to the new def.
            #
            # For case 3, we will end up with a dangling reference and fail later.
            schema = api.SchemaByName(referenced_schema)
            # The stored "schema" may not be an instance of Schema, but rather a
            # data_types.PrimitiveDataType, which has no 'wireName' value.
            if schema:
                _LOGGER.debug('Schema.Create: %s => %s', default_name,
                              schema.values.get('wireName', '<unknown>'))
                return schema
            return data_types.SchemaReference(referenced_schema, api)

        raise ApiException('Cannot decode JSON Schema for: %s' % def_dict)
예제 #3
0
    def testDataValue(self):
        foo_def_dict = {
            'className': 'Foo',
            'type': 'string',
        }
        prototype = data_types.DataType(foo_def_dict,
                                        None,
                                        language_model=self.language_model)
        dv = data_value.DataValue(3, prototype)

        # Basic Checks
        self.assertEqual(3, dv.value)
        self.assertEqual(prototype, dv.data_type)
        self.assertEqual({}, dv.metadata)
        self.assertEqual('Foo', dv.code_type)

        dv.metadata['foo'] = 'bar'
        self.assertEqual({'foo': 'bar'}, dv.metadata)

        dv.SetValue('four')
        self.assertEqual(dv.value, 'four')

        self.assertEqual(self.language_model, dv.GetLanguageModel())
        other_language_model = language_model.LanguageModel(
            class_name_delimiter='+')
        dv.SetLanguageModel(other_language_model)
        self.assertEqual(other_language_model, dv.GetLanguageModel())

        # Now that we've set a local language model... make sure the codepath
        # for setting the data_type's language model gets exercised.
        self.assertEqual('Foo', dv.code_type)

        # Check that the constructor doesn't freak if an odd object is passed in
        dv = data_value.DataValue(object, prototype)
        self.assertEqual(dv.value, object)

        # A standard case is the prototype being a Property object.  It is not
        # uncommon that the Property's data_type is a SchemaReference. To verify
        # this case is handled correctly we must fake an API.
        bar_def_dict = {
            'className': 'Foo',
            'type': 'string',
        }

        class MockApi(object):
            def __init__(self):
                self.model_module = None

            def SetSchema(self, s):
                self.schema = s

            def SetSchemaRef(self, schema_ref):
                self.schema_ref = schema_ref

            # pylint: disable=unused-argument
            def ToClassName(self, name, element, element_type):
                return name

            # pylint: disable=unused-argument
            def SchemaByName(self, schema_name):
                return self.schema

            # pylint: disable=unused-argument
            def DataTypeFromJson(self,
                                 unused_def_dict,
                                 tentative_class_name,
                                 parent=None,
                                 wire_name=None):
                return self.schema_ref

            def NestedClassNameForProperty(self, name, owning_schema):
                return '%s%s' % (owning_schema.class_name, name)

        mock_api = MockApi()
        bar_schema = schema.Schema(mock_api, 'Bar', bar_def_dict)
        mock_api.SetSchema(bar_schema)
        schema_ref = data_types.SchemaReference('Bar', mock_api)
        mock_api.SetSchemaRef(schema_ref)

        prototype = schema.Property(mock_api, schema_ref, 'baz', foo_def_dict)
        dv = data_value.DataValue('3', prototype)
        # Assure all the unwrapping gymnastics in the DataValue constructor did
        # their job correctly.
        self.assertEqual(mock_api.schema, dv.data_type)
예제 #4
0
    def Create(api, default_name, def_dict, wire_name, parent=None):
        """Construct a Schema or DataType from a discovery dictionary.

    Schemas contain either object declarations, simple type declarations, or
    references to other Schemas.  Object declarations conceptually map to real
    classes.  Simple types will map to a target language built-in type.
    References should effectively be replaced by the referenced Schema.

    Args:
      api: (Api) the Api instance owning the Schema
      default_name: (str) the default name of the Schema. If there is an 'id'
        member in the definition, that is used for the name instead.
      def_dict: (dict) a discovery dictionary
      wire_name: The name which will identify objects of this type in data on
        the wire.
      parent: (Schema) The containing schema. To be used to establish nesting
        for anonymous sub-schemas.

    Returns:
      A Schema or DataType.

    Raises:
      ApiException: If the definition dict is not correct.
    """

        schema_id = def_dict.get('id')
        if schema_id:
            name = schema_id
        else:
            name = default_name
        class_name = api.ToClassName(name, element_type='schema')

        # Schema objects come in several patterns.
        #
        # 1. Simple objects
        #    { type: object, properties: { "foo": {schema} ... }}
        #
        # 2. Maps of objects
        #    { type: object, additionalProperties: { "foo": {inner_schema} ... }}
        #
        #    What we want is a data type which is Map<string, {inner_schema}>
        #    The schema we create here is essentially a built in type which we
        #    don't want to generate a class for.
        #
        # 3. Arrays of objects
        #    { type: array, items: { inner_schema }}
        #
        #    Same kind of issue as the map, but with List<{inner_schema}>
        #
        # 4. Primative data types, described by type and format.
        #    { type: string, format: int32 }
        #
        # 5. Refs to another schema.
        #    { $ref: name }

        if 'type' in def_dict:
            # The 'type' field of the schema can either be 'array', 'object', or a
            # base json type.
            json_type = def_dict['type']
            if json_type == 'object':
                # Look for full object definition.  You can have properties or
                # additionalProperties, but it does not  do anything useful to have
                # both.

                # Replace properties dict with Property's
                props = def_dict.get('properties')
                if props:
                    # This case 1 from above
                    properties = []
                    schema = Schema(api, name, def_dict, parent=parent)
                    if wire_name:
                        schema.SetTemplateValue('wireName', wire_name)
                    for prop_name, prop_dict in props.iteritems():
                        Trace('  adding prop: %s to %s' % (prop_name, name))
                        properties.append(
                            Property(api, schema, prop_name, prop_dict))
                    Trace('Marking %s fully defined' %
                          schema.values['className'])
                    schema.SetTemplateValue('properties', properties)
                    return schema

                # Look for case 2
                additional_props = def_dict.get(_ADDITIONAL_PROPERTIES)
                if additional_props:
                    Trace('Have only additionalProps for %s, dict=%s' %
                          (name, str(additional_props)))
                    # TODO(user): Remove this hack at the next large breaking change
                    # The "Items" added to the end is unneeded and ugly. This is for
                    # temporary backwards compatability.  And in case 3 too.
                    if additional_props.get('type') == 'array':
                        name = '%sItems' % name
                    # Note, since this is an interim, non class just to hold the map
                    # make the parent schema the parent passed in, not myself.
                    base_type = api.DataTypeFromJson(additional_props,
                                                     name,
                                                     parent=parent,
                                                     wire_name=wire_name)
                    map_type = data_types.MapDataType(base_type, parent=parent)
                    Trace('  %s is MapOf<string, %s>' %
                          (class_name, base_type.class_name))
                    return map_type

                raise ApiException('object without properties in: %s' %
                                   def_dict)

            elif json_type == 'array':
                # Case 3: Look for array definition
                items = def_dict.get('items')
                if not items:
                    raise ApiException('array without items in: %s' % def_dict)
                tentative_class_name = class_name
                if schema_id:
                    Trace('Top level schema %s is an array' % class_name)
                    tentative_class_name += 'Items'
                base_type = api.DataTypeFromJson(items,
                                                 tentative_class_name,
                                                 parent=parent,
                                                 wire_name=wire_name)
                Trace('  %s is ArrayOf<%s>' %
                      (class_name, base_type.class_name))
                array_type = data_types.ArrayDataType(base_type, parent=parent)

                # If I am not a top level schema, mark me as not generatable
                if not schema_id:
                    array_type.SetTemplateValue('builtIn', True)
                else:
                    Trace('Top level schema %s is an array' % class_name)
                    array_type.SetTemplateValue('className', schema_id)
                return array_type

            else:
                # Case 4: This must be a basic type.  Create a DataType for it.
                format_type = def_dict.get('format')
                if format_type:
                    Trace(' Found Type: %s with Format: %s' %
                          (json_type, format_type))

                base_type = data_types.BuiltInDataType(def_dict,
                                                       api,
                                                       parent=parent)
                return base_type

        referenced_schema = def_dict.get('$ref')
        if referenced_schema:
            # Case 5: Reference to another Schema.
            #
            # There are 4 ways you can see '$ref' in discovery.
            # 1. In a property of a schema, pointing back to one previously defined
            # 2. In a property of a schema, pointing forward
            # 3. In a method request or response pointing to a defined schema
            # 4. In a method request or response or property of a schema pointing to
            #    something undefined.
            #
            # This code is not reached in case 1.  The way the Generators loads
            # schemas (see _BuildSchemaDefinitions), is to loop over them and add
            # them to a dict of schemas. A backwards reference would be in the table
            # so the DataTypeFromJson call in the Property constructor will resolve
            # to the defined schema.
            #
            # For case 2.  Just creating this placeholder here is fine.  When the
            # actual schema is hit in the loop in _BuildSchemaDefinitions, we will
            # replace the entry and DataTypeFromJson will resolve the to the new def.
            #
            # For case 3, we should not reach this code, because the
            # DataTypeFromJson would
            # have returned the defined schema.
            #
            # For case 4, we punt on the whole API.
            return data_types.SchemaReference(referenced_schema, api)

        raise ApiException('Cannot decode JSON Schema for: %s' % def_dict)