Example #1
0
        /// <summary>
        /// Normalize a swagger schema by dereferencing schema references and evaluating 
        /// schema composition
        /// </summary>
        /// <param name="schema">The schema to normalize</param>
        /// <returns>A normalized swagger schema</returns>
        public Schema Unwrap(Schema schema)
        {
            if (schema == null)
            {
                return null;
            }
            Schema unwrappedSchema = schema;
            // If referencing global definitions serializationProperty
            if (schema.Reference != null)
            {
                unwrappedSchema = Dereference(schema.Reference);
            }

            ExpandAllOf(unwrappedSchema);
            return unwrappedSchema;
        }
        public override IType BuildServiceType(string serviceTypeName)
        {
            // Check if already generated
            if (serviceTypeName != null && Modeler.GeneratedTypes.ContainsKey(serviceTypeName))
            {
                return Modeler.GeneratedTypes[serviceTypeName];
            }

            _schema = Modeler.Resolver.Unwrap(_schema);

            // If primitive type
            if ((_schema.Type != null && _schema.Type != DataType.Object) ||
                _schema.AdditionalProperties != null)
            {
                return _schema.GetBuilder(Modeler).ParentBuildServiceType(serviceTypeName);
            }

            // If the object does not have any properties, treat it as raw json (i.e. object)
            if (_schema.Properties.IsNullOrEmpty() && string.IsNullOrEmpty(_schema.Extends))
            {
                return PrimaryType.Object;
            }

            // Otherwise create new object type
            var objectType = new CompositeType 
                            { 
                                Name = serviceTypeName, 
                                SerializedName = serviceTypeName, 
                                Documentation = _schema.Description 
                            };
            // Put this in already generated types serializationProperty
            Modeler.GeneratedTypes[serviceTypeName] = objectType;

            if (_schema.Properties != null)
            {
                // Visit each property and recursively build service types
                foreach (var property in _schema.Properties)
                {
                    string name = property.Key;
                    if (name != _schema.Discriminator)
                    {
                        string propertyServiceTypeName;
                        if (property.Value.Reference != null)
                        {
                            propertyServiceTypeName = property.Value.Reference.StripDefinitionPath();
                        }
                        else
                        {
                            propertyServiceTypeName = serviceTypeName + "_" + property.Key;
                        }
                        var propertyType =
                            property.Value.GetBuilder(Modeler).BuildServiceType(propertyServiceTypeName);

                        var propertyObj = new Property
                        {
                            Name = name,
                            SerializedName = name,
                            Type = propertyType,
                            IsRequired = property.Value.IsRequired
                        };

                        //propertyObj.Type = objectType;
                        propertyObj.Documentation = property.Value.Description;
                        var enumType = propertyType as EnumType;
                        if (enumType != null)
                        {
                            if (propertyObj.Documentation == null)
                            {
                                propertyObj.Documentation = string.Empty;
                            }
                            else
                            {
                                propertyObj.Documentation = propertyObj.Documentation.TrimEnd('.') + ". ";
                            }
                            propertyObj.Documentation += "Possible values for this property include: " +
                                                       string.Join(", ", enumType.Values.Select(v =>
                                                           string.Format(CultureInfo.InvariantCulture, 
                                                           "'{0}'", v.Name))) + ".";
                        }
                        propertyObj.IsReadOnly = property.Value.ReadOnly;
                        objectType.Properties.Add(propertyObj);
                    }
                    else
                    {
                        objectType.PolymorphicDiscriminator = name;
                    }
                }
            }

            // Copy over extensions
            _schema.Extensions.ForEach(e => objectType.Extensions[e.Key] = e.Value);

            // Put this in the extended type serializationProperty for building method return type in the end
            if (_schema.Extends != null)
            {
                Modeler.ExtendedTypes[serviceTypeName] = _schema.Extends.StripDefinitionPath();
            }

            return objectType;
        }
 public SchemaBuilder(Schema schema, SwaggerModeler modeler)
     : base(schema, modeler)
 {
     _schema = schema;
 }
Example #4
0
        public override IType BuildServiceType(string serviceTypeName)
        {
            // Check if already generated
            if (serviceTypeName != null && Modeler.GeneratedTypes.ContainsKey(serviceTypeName))
            {
                return Modeler.GeneratedTypes[serviceTypeName];
            }

            _schema = Modeler.Resolver.Unwrap(_schema);

            // If primitive type
            if ((_schema.Type != null && _schema.Type != DataType.Object) ||
                _schema.AdditionalProperties != null)
            {
                return _schema.GetBuilder(Modeler).ParentBuildServiceType(serviceTypeName);
            }

            // If object with file format treat as stream
            if (_schema.Type != null 
                && _schema.Type == DataType.Object 
                && "file".Equals(SwaggerObject.Format, StringComparison.OrdinalIgnoreCase))
            {
                return PrimaryType.Stream;
            }

            // If the object does not have any properties, treat it as raw json (i.e. object)
            if (_schema.Properties.IsNullOrEmpty() && string.IsNullOrEmpty(_schema.Extends))
            {
                return PrimaryType.Object;
            }

            // Otherwise create new object type
            var objectType = new CompositeType 
                            { 
                                Name = serviceTypeName, 
                                SerializedName = serviceTypeName, 
                                Documentation = _schema.Description 
                            };
            // Put this in already generated types serializationProperty
            Modeler.GeneratedTypes[serviceTypeName] = objectType;

            if (_schema.Properties != null)
            {
                // Visit each property and recursively build service types
                foreach (var property in _schema.Properties)
                {
                    string name = property.Key;
                    if (name != _schema.Discriminator)
                    {
                        string propertyServiceTypeName;
                        if (property.Value.Reference != null)
                        {
                            propertyServiceTypeName = property.Value.Reference.StripDefinitionPath();
                        }
                        else
                        {
                            propertyServiceTypeName = serviceTypeName + "_" + property.Key;
                        }
                        var propertyType =
                            property.Value.GetBuilder(Modeler).BuildServiceType(propertyServiceTypeName);

                        var propertyObj = new Property
                        {
                            Name = name,
                            SerializedName = name,
                            Type = propertyType,
                            IsRequired = property.Value.IsRequired,
                            IsReadOnly = property.Value.ReadOnly,
                            DefaultValue = property.Value.Default
                        };
                        SetConstraints(propertyObj.Constraints, property.Value);

                        //propertyObj.Type = objectType;
                        propertyObj.Documentation = property.Value.Description;
                        var enumType = propertyType as EnumType;
                        if (enumType != null)
                        {
                            if (propertyObj.Documentation == null)
                            {
                                propertyObj.Documentation = string.Empty;
                            }
                            else
                            {
                                propertyObj.Documentation = propertyObj.Documentation.TrimEnd('.') + ". ";
                            }
                            propertyObj.Documentation += "Possible values for this property include: " +
                                                       string.Join(", ", enumType.Values.Select(v =>
                                                           string.Format(CultureInfo.InvariantCulture, 
                                                           "'{0}'", v.Name))) + ".";
                        }
                        objectType.Properties.Add(propertyObj);
                    }
                    else
                    {
                        objectType.PolymorphicDiscriminator = name;
                    }
                }
            }

            // Copy over extensions
            _schema.Extensions.ForEach(e => objectType.Extensions[e.Key] = e.Value);

            if (_schema.Extends != null)
            {
                // Optionally override the discriminator value for polymorphic types. We expect this concept to be
                // added to Swagger at some point, but until it is, we use an extension.
                object discriminatorValueExtension;
                if (objectType.Extensions.TryGetValue(DiscriminatorValueExtension, out discriminatorValueExtension))
                {
                    string discriminatorValue = discriminatorValueExtension as string;
                    if (discriminatorValue != null)
                    {
                        objectType.SerializedName = discriminatorValue;
                    }
                }

                // Put this in the extended type serializationProperty for building method return type in the end
                Modeler.ExtendedTypes[serviceTypeName] = _schema.Extends.StripDefinitionPath();
            }

            return objectType;
        }
Example #5
0
        /// <summary>
        /// Evaluate the composition of properties for a swagger spec and save the 
        /// evaluated form in the specification.  This transformation is idempotent
        /// </summary>
        /// <param name="schema">The swagger schema to evaluate.</param>
        public void ExpandAllOf(Schema schema)
        {
            if (schema == null)
            {
                throw new ArgumentNullException("schema");
            }

            if (schema.AllOf != null)
            {
                CheckCircularAllOf(schema, null, null);
                var references = schema.AllOf.Where(s => s.Reference != null).ToList();

                if (references.Count == 1)
                {
                    if (schema.Extends != null)
                    {
                        throw new ArgumentException(
                            string.Format(CultureInfo.InvariantCulture, 
                            Properties.Resources.InvalidTypeExtendsWithAllOf, schema.Title));
                    }

                    schema.Extends = references[0].Reference;
                    schema.AllOf.Remove(references[0]);
                }
                var parentSchema = schema.Extends;
                var propertiesOnlySchema = new Schema
                {
                    Properties =
                        schema.Properties
                };

                var schemaList =
                    new List<Schema>().Concat(schema.AllOf)
                        .Concat(new List<Schema> {propertiesOnlySchema});
                schema.Properties = new Dictionary<string, Schema>();
                foreach (var componentSchema in schemaList)
                {
                    // keep the same resolver state for each of the children
                    var unwrappedComponent = ((SchemaResolver) Clone()).Unwrap(
                        componentSchema);
                    if (unwrappedComponent != null && unwrappedComponent.Properties != null)
                    {
                        foreach (var propertyName in unwrappedComponent.Properties.Keys)
                        {
                            var unwrappedProperty = unwrappedComponent.Properties[propertyName];
                            if (schema.Properties.ContainsKey(propertyName))
                            {
                                if (!SchemaTypesAreEquivalent(
                                    schema.Properties[propertyName], unwrappedProperty))
                                {
                                    throw new InvalidOperationException(
                                        string.Format(CultureInfo.InvariantCulture, 
                                        Properties.Resources.IncompatibleTypesInSchemaComposition,
                                            propertyName,
                                            unwrappedComponent.Properties[propertyName].Type,
                                            schema.Properties[propertyName].Type,
                                            schema.Title));
                                }
                            }
                            else
                            {
                                var parentProperty = ((SchemaResolver) Clone())
                                    .FindParentProperty(parentSchema, propertyName);
                                if (parentProperty != null)
                                {
                                    if (!SchemaTypesAreEquivalent(parentProperty, unwrappedProperty))
                                    {
                                        throw new InvalidOperationException(
                                            string.Format(CultureInfo.InvariantCulture, 
                                                Properties.Resources.IncompatibleTypesInBaseSchema, propertyName,
                                                parentProperty.Type,
                                                unwrappedProperty.Type, schema.Title));
                                    }
                                }
                                else
                                {
                                    schema.Properties[propertyName] = unwrappedProperty;
                                }
                            }
                        }
                    }
                    if (unwrappedComponent != null && unwrappedComponent.Required != null)
                    {
                        var requiredProperties = schema.Required ?? new List<string>();
                        foreach (var requiredProperty in unwrappedComponent.Required)
                        {
                            if (!requiredProperties.Contains(requiredProperty))
                            {
                                requiredProperties.Add(requiredProperty);
                            }
                        }

                        schema.Required = requiredProperties;
                    }
                }

                schema.AllOf = null;
            }
        }
Example #6
0
        /// <summary>
        /// Determine equivalence between the types described by two schemas. 
        /// Limit the comparison to exclude comparison of complexe inline schemas.
        /// </summary>
        /// <param name="parentProperty"></param>
        /// <param name="unwrappedProperty"></param>
        /// <returns></returns>
        private bool SchemaTypesAreEquivalent(Schema parentProperty,
            Schema unwrappedProperty)
        {
            Debug.Assert(parentProperty != null && unwrappedProperty != null);
            if (parentProperty == null)
            {
                throw new ArgumentNullException("parentProperty");
            }

            if (unwrappedProperty == null)
            {
                throw new ArgumentNullException("unwrappedProperty");
            }

            if ((parentProperty.Type == null || parentProperty.Type == DataType.Object) &&
                (unwrappedProperty.Type == null || unwrappedProperty.Type == DataType.Object))
            {
                var parentPropertyToCompare = parentProperty;
                var unwrappedPropertyToCompare = unwrappedProperty;
                if (!string.IsNullOrEmpty(parentProperty.Reference))
                {
                    parentPropertyToCompare = Dereference(parentProperty.Reference);
                }
                if (!string.IsNullOrEmpty(unwrappedProperty.Reference))
                {
                    unwrappedPropertyToCompare = Dereference(unwrappedProperty.Reference);
                }

                if (parentPropertyToCompare == unwrappedPropertyToCompare)
                {
                    return true; // when fully dereferenced, they can refer to the same thing
                }

                // or they can refer to different things... but there can be an inheritance relation...
                while (unwrappedPropertyToCompare != null && unwrappedPropertyToCompare.Extends != null)
                {
                    unwrappedPropertyToCompare = Dereference(unwrappedPropertyToCompare.Extends);
                    if (unwrappedPropertyToCompare == parentPropertyToCompare)
                    {
                        return true;
                    }
                }

                return false;
            }
            if (parentProperty.Type == DataType.Array &&
                unwrappedProperty.Type == DataType.Array)
            {
                return SchemaTypesAreEquivalent(parentProperty.Items, unwrappedProperty.Items);
            }
            return parentProperty.Type == unwrappedProperty.Type
                   && parentProperty.Format == unwrappedProperty.Format;
        }
Example #7
0
        void CheckCircularAllOf(Schema schema, HashSet<Schema> visited, Stack<string> referenceChain)
        {
            visited = visited ?? new HashSet<Schema>();
            referenceChain = referenceChain ?? new Stack<string>();
            if (!visited.Add(schema)) // was already present in the set
            {
                var setDescription = "(" + String.Join(", ", referenceChain) + ")";
                throw new InvalidOperationException(
                    string.Format(CultureInfo.InvariantCulture,
                    Properties.Resources.CircularBaseSchemaSet, setDescription));
            }

            if (schema.AllOf != null)
            {
                foreach (var reference in schema.AllOf.Select(s => s.Reference).Where(r => r != null))
                {
                    referenceChain.Push(reference);

                    var deref = Dereference(reference);
                    CheckCircularAllOf(deref, visited, referenceChain);

                    Debug.Assert(reference == referenceChain.Peek());
                    referenceChain.Pop();
                }
            }
            visited.Remove(schema);
        }
Example #8
0
        public override IType BuildServiceType(string serviceTypeName)
        {
            // Check if already generated
            if (serviceTypeName != null && Modeler.GeneratedTypes.ContainsKey(serviceTypeName))
            {
                return Modeler.GeneratedTypes[serviceTypeName];
            }

            _schema = Modeler.Resolver.Unwrap(_schema);

            // If primitive type
            if (_schema.Type != null && _schema.Type != DataType.Object || ( _schema.AdditionalProperties != null && _schema.Properties.IsNullOrEmpty() ))
            {
                // Notes: 
                //      'additionalProperties' on a type AND no defined 'properties', indicates that
                //      this type is a Dictionary. (and is handled by ObjectBuilder)

                return _schema.GetBuilder(Modeler).ParentBuildServiceType(serviceTypeName);
            }

            // If object with file format treat as stream
            if (_schema.Type != null 
                && _schema.Type == DataType.Object 
                && "file".Equals(SwaggerObject.Format, StringComparison.OrdinalIgnoreCase))
            {
                return new PrimaryType(KnownPrimaryType.Stream);
            }

            // If the object does not have any properties, treat it as raw json (i.e. object)
            if (_schema.Properties.IsNullOrEmpty() && string.IsNullOrEmpty(_schema.Extends) && _schema.AdditionalProperties == null)
            {
                return new PrimaryType(KnownPrimaryType.Object);
            }

            // Otherwise create new object type
            var objectType = new CompositeType 
                            { 
                                Name = serviceTypeName, 
                                SerializedName = serviceTypeName, 
                                Documentation = _schema.Description,
                                ExternalDocsUrl = _schema.ExternalDocs?.Url,
                                Summary = _schema.Title
                            };

            // Put this in already generated types serializationProperty
            Modeler.GeneratedTypes[serviceTypeName] = objectType;

            if (_schema.Type == DataType.Object && _schema.AdditionalProperties != null)
            {
                // this schema is defining 'additionalProperties' which expects to create an extra
                // property that will catch all the unbound properties during deserialization.
                var name = "additionalProperties";
                var propertyType = new DictionaryType
                {
                    ValueType = _schema.AdditionalProperties.GetBuilder(Modeler).BuildServiceType(
                               _schema.AdditionalProperties.Reference != null
                               ? _schema.AdditionalProperties.Reference.StripDefinitionPath()
                               : serviceTypeName + "Value"),
                    SupportsAdditionalProperties = true
                };

                // now add the extra property to the type.
                objectType.Properties.Add(new Property
                {
                    Name = name,
                    Type = propertyType,
                    Documentation = "Unmatched properties from the message are deserialized this collection"
                });
            }

            if (_schema.Properties != null)
            {
                // Visit each property and recursively build service types
                foreach (var property in _schema.Properties)
                {
                    string name = property.Key;
                    if (name != _schema.Discriminator)
                    {
                        string propertyServiceTypeName;
                        if (property.Value.Reference != null)
                        {
                            propertyServiceTypeName = property.Value.Reference.StripDefinitionPath();
                        }
                        else
                        {
                            propertyServiceTypeName = serviceTypeName + "_" + property.Key;
                        }
                        var propertyType =
                            property.Value.GetBuilder(Modeler).BuildServiceType(propertyServiceTypeName);
                        if (property.Value.ReadOnly && property.Value.IsRequired)
                        {
                            throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture,
                           Resources.ReadOnlyNotRequired, name, serviceTypeName));
                        }

                        var propertyObj = new Property
                        {
                            Name = name,
                            SerializedName = name,
                            Type = propertyType,
                            IsReadOnly = property.Value.ReadOnly,
                            Summary = property.Value.Title
                        };
                        PopulateParameter(propertyObj, property.Value);
                        var propertyCompositeType = propertyType as CompositeType;
                        if (propertyObj.IsConstant || 
                            (propertyCompositeType != null 
                                && propertyCompositeType.ContainsConstantProperties))
                        {
                            objectType.ContainsConstantProperties = true;
                        }
                        
                        objectType.Properties.Add(propertyObj);
                    }
                    else
                    {
                        objectType.PolymorphicDiscriminator = name;
                    }
                }
            }

            // Copy over extensions
            _schema.Extensions.ForEach(e => objectType.Extensions[e.Key] = e.Value);

            if (_schema.Extends != null)
            {
                // Optionally override the discriminator value for polymorphic types. We expect this concept to be
                // added to Swagger at some point, but until it is, we use an extension.
                object discriminatorValueExtension;
                if (objectType.Extensions.TryGetValue(DiscriminatorValueExtension, out discriminatorValueExtension))
                {
                    string discriminatorValue = discriminatorValueExtension as string;
                    if (discriminatorValue != null)
                    {
                        objectType.SerializedName = discriminatorValue;
                    }
                }

                // Put this in the extended type serializationProperty for building method return type in the end
                Modeler.ExtendedTypes[serviceTypeName] = _schema.Extends.StripDefinitionPath();
            }

            return objectType;
        }
Example #9
0
        /// <summary>
        /// Determine equivalence between the types described by two schemas. 
        /// Limit the comparison to exclude comparison of complexe inline schemas.
        /// </summary>
        /// <param name="parentProperty"></param>
        /// <param name="unwrappedProperty"></param>
        /// <returns></returns>
        private bool SchemaTypesAreEquivalent(Schema parentProperty,
            Schema unwrappedProperty)
        {
            Debug.Assert(parentProperty != null && unwrappedProperty != null);
            if (parentProperty == null)
            {
                throw new ArgumentNullException("parentProperty");
            }

            if (unwrappedProperty == null)
            {
                throw new ArgumentNullException("unwrappedProperty");
            }

            if ((parentProperty.Type == null || parentProperty.Type == DataType.Object) &&
                (unwrappedProperty.Type == null || unwrappedProperty.Type == DataType.Object))
            {
                if (!string.IsNullOrEmpty(parentProperty.Reference) ||
                    !string.IsNullOrEmpty(unwrappedProperty.Reference))
                {
                    return parentProperty.Reference == unwrappedProperty.Reference;
                }
                // do not compare inline schemas
                return false;
            }
            if (parentProperty.Type == DataType.Array &&
                unwrappedProperty.Type == DataType.Array)
            {
                return SchemaTypesAreEquivalent(parentProperty.Items, unwrappedProperty.Items);
            }
            return parentProperty.Type == unwrappedProperty.Type
                   && parentProperty.Format == unwrappedProperty.Format;
        }